<commit_before>#!/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.
'''
import ambari_simplejson as json
import logging
import os
import subprocess
import pprint
import threading
import platform
from threading import Thread
import time
from BackgroundCommandExecutionHandle import BackgroundCommandExecutionHandle
from ambari_commons.os_check import OSConst, OSCheck
from Grep import Grep
import sys
from ambari_commons import shell
from ambari_commons.shell import shellRunner


logger = logging.getLogger()

class PythonExecutor(object):
  """
  Performs functionality for executing python scripts.
  Warning: class maintains internal state. As a result, instances should not be
  used as a singleton for a concurrent execution of python scripts
  """
  NO_ERROR = "none"

  def __init__(self, tmpDir, config):
    self.grep = Grep()
    self.event = threading.Event()
    self.python_process_has_been_killed = False
    self.tmpDir = tmpDir
    self.config = config
    pass


  def open_subprocess_files(self, tmpoutfile, tmperrfile, override_output_files, backup_log_files = True):
    if override_output_files: # Recreate files, existing files are backed up if backup_log_files is True
      if backup_log_files:
        self.back_up_log_file_if_exists(tmpoutfile)
        self.back_up_log_file_if_exists(tmperrfile)
      tmpout =  open(tmpoutfile, 'w')
      tmperr =  open(tmperrfile, 'w')
    else: # Append to files
      tmpout =  open(tmpoutfile, 'a')
      tmperr =  open(tmperrfile, 'a')
    return tmpout, tmperr

  def back_up_log_file_if_exists(self, file_path):
    if os.path.isfile(file_path):
      counter = 0
      while True:
        # Find backup name that is not used yet (saves logs
        # from multiple command retries)
        backup_name = file_path + "." + str(counter)
        if not os.path.isfile(backup_name):
          break
        counter += 1
      os.rename(file_path, backup_name)

  def run_file(self, script, script_params, tmpoutfile, tmperrfile,
               timeout, tmpstructedoutfile, callback, task_id,
               override_output_files = True, backup_log_files = True, handle = None,
               log_info_on_failure = True):
    """
    Executes the specified python file in a separate subprocess.
    Method returns only when the subprocess is finished.
    Params arg is a list of script parameters
    Timeout meaning: how many seconds should pass before script execution
    is forcibly terminated
    override_output_files option defines whether stdout/stderr files will be
    recreated or appended.
    The structured out file, however, is preserved during multiple invocations that use the same file.
    """
    pythonCommand = self.python_command(script, script_params)
    logger.debug("Running command " + pprint.pformat(pythonCommand))

    if handle is None:
      tmpout, tmperr = self.open_subprocess_files(tmpoutfile, tmperrfile, override_output_files, backup_log_files)

      process = self.launch_python_subprocess(pythonCommand, tmpout, tmperr)
      # map task_id to pid
      callback(task_id, process.pid)
      logger.debug("Launching watchdog thread")
      self.event.clear()
      self.python_process_has_been_killed = False
      thread = Thread(target =  self.python_watchdog_func, args = (process, timeout))
      thread.start()
      # Waiting for the process to be either finished or killed
      process.communicate()
      self.event.set()
      thread.join()
      result = self.prepare_process_result(process.returncode, tmpoutfile, tmperrfile, tmpstructedoutfile, timeout=timeout)

      if log_info_on_failure and result['exitcode']:
        self.on_failure(pythonCommand, result)

      return result
    else:
      holder = Holder(pythonCommand, tmpoutfile, tmperrfile, tmpstructedoutfile, handle)

      background = BackgroundThread(holder, self)
      background.start()
      return {"exitcode": 777}

  def on_failure(self, pythonCommand, result):
    """
    Log some useful information after task failure.
    """
    logger.info("Command " + pprint.pformat(pythonCommand) + " failed with exitcode=" + str(result['exitcode']))
    if OSCheck.is_windows_family():
      cmd_list = ["WMIC path win32_process get Caption,Processid,Commandline", "netstat -an"]
    else:
      cmd_list = ["ps faux", "netstat -tulpn"]

    shell_runner = shellRunner()

    for cmd in cmd_list:
      ret = shell_runner.run(cmd)
      logger.info("Command '{0}' returned {1}. {2}{3}".format(cmd, ret["exitCode"], ret["error"], ret["output"]))

  def prepare_process_result(self, returncode, tmpoutfile, tmperrfile, tmpstructedoutfile, timeout=None):
    out, error, structured_out = self.read_result_from_files(tmpoutfile, tmperrfile, tmpstructedoutfile)

    if self.python_process_has_been_killed:<commit_msg>AMBARI-19706. Slider Client Install failed (aonishuk)<commit_after>#!/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.
'''
import ambari_simplejson as json
import logging
import os
import subprocess
import pprint
import threading
import platform
from threading import Thread
import time
from BackgroundCommandExecutionHandle import BackgroundCommandExecutionHandle
from ambari_commons.os_check import OSConst, OSCheck
from Grep import Grep
import sys
from ambari_commons import shell
from ambari_commons.shell import shellRunner


logger = logging.getLogger()

class PythonExecutor(object):
  """
  Performs functionality for executing python scripts.
  Warning: class maintains internal state. As a result, instances should not be
  used as a singleton for a concurrent execution of python scripts
  """
  NO_ERROR = "none"

  def __init__(self, tmpDir, config):
    self.grep = Grep()
    self.event = threading.Event()
    self.python_process_has_been_killed = False
    self.tmpDir = tmpDir
    self.config = config
    pass


  def open_subprocess_files(self, tmpoutfile, tmperrfile, override_output_files, backup_log_files = True):
    if override_output_files: # Recreate files, existing files are backed up if backup_log_files is True
      if backup_log_files:
        self.back_up_log_file_if_exists(tmpoutfile)
        self.back_up_log_file_if_exists(tmperrfile)
      tmpout =  open(tmpoutfile, 'w')
      tmperr =  open(tmperrfile, 'w')
    else: # Append to files
      tmpout =  open(tmpoutfile, 'a')
      tmperr =  open(tmperrfile, 'a')
    return tmpout, tmperr

  def back_up_log_file_if_exists(self, file_path):
    if os.path.isfile(file_path):
      counter = 0
      while True:
        # Find backup name that is not used yet (saves logs
        # from multiple command retries)
        backup_name = file_path + "." + str(counter)
        if not os.path.isfile(backup_name):
          break
        counter += 1
      os.rename(file_path, backup_name)

  def run_file(self, script, script_params, tmpoutfile, tmperrfile,
               timeout, tmpstructedoutfile, callback, task_id,
               override_output_files = True, backup_log_files = True, handle = None,
               log_info_on_failure = True):
    """
    Executes the specified python file in a separate subprocess.
    Method returns only when the subprocess is finished.
    Params arg is a list of script parameters
    Timeout meaning: how many seconds should pass before script execution
    is forcibly terminated
    override_output_files option defines whether stdout/stderr files will be
    recreated or appended.
    The structured out file, however, is preserved during multiple invocations that use the same file.
    """
    pythonCommand = self.python_command(script, script_params)
    logger.debug("Running command " + pprint.pformat(pythonCommand))

    if handle is None:
      tmpout, tmperr = self.open_subprocess_files(tmpoutfile, tmperrfile, override_output_files, backup_log_files)

      process = self.launch_python_subprocess(pythonCommand, tmpout, tmperr)
      # map task_id to pid
      callback(task_id, process.pid)
      logger.debug("Launching watchdog thread")
      self.event.clear()
      self.python_process_has_been_killed = False
      thread = Thread(target =  self.python_watchdog_func, args = (process, timeout))
      thread.start()
      # Waiting for the process to be either finished or killed
      process.communicate()
      self.event.set()
      thread.join()
      result = self.prepare_process_result(process.returncode, tmpoutfile, tmperrfile, tmpstructedoutfile, timeout=timeout)

      if log_info_on_failure and result['exitcode']:
        self.on_failure(pythonCommand, result)

      return result
    else:
      holder = Holder(pythonCommand, tmpoutfile, tmperrfile, tmpstructedoutfile, handle)

      background = BackgroundThread(holder, self)
      background.start()
      return {"exitcode": 777}

  def on_failure(self, pythonCommand, result):
    """
    Log some useful information after task failure.
    """
    logger.info("Command " + pprint.pformat(pythonCommand) + " failed with exitcode=" + str(result['exitcode']))
    if OSCheck.is_windows_family():
      cmd_list = ["WMIC path win32_process get Caption,Processid,Commandline", "netstat -an"]
    else:
      cmd_list = ["export COLUMNS=9999 ; ps faux", "netstat -tulpn"]

    shell_runner = shellRunner()

    for cmd in cmd_list:
      ret = shell_runner.run(cmd)
      logger.info("Command '{0}' returned {1}. {2}{3}".format(cmd, ret["exitCode"], ret["error"], ret["output"]))

  def prepare_process_result(self, returncode, tmpoutfile, tmperrfile, tmpstructedoutfile, timeout=None):
    out, error, structured_out = self.read_result_from_files(tmpoutfile, tmperrfile, tmpstructedoutfile)

    if self.python_process_has_been_killed:
{'fraction_non_alphanumeric': 0.07659115426105717, 'fraction_numerical': 0.003505933117583603, 'mean_word_length': 3.830655666521928, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1369799', 'n_tokens_mistral': 3244, 'n_tokens_neox': 3159, 'n_words': 1030}