Source code for glideinwms.lib.condorExe
# SPDX-FileCopyrightText: 2009 Fermi Research Alliance, LLC
# SPDX-License-Identifier: Apache-2.0
# Description:
# This module implements the functions to execute condor commands
import os
from subprocess import CalledProcessError
from . import logSupport, subprocessSupport
[docs]
class CondorExeError(RuntimeError):
"""Base class for condorExe module errors"""
def __init__(self, err_str):
RuntimeError.__init__(self, err_str)
[docs]
class UnconfigError(CondorExeError):
def __init__(self, err_str):
CondorExeError.__init__(self, err_str)
[docs]
class ExeError(CondorExeError):
def __init__(self, err_str):
CondorExeError.__init__(self, err_str)
#
# Configuration
#
[docs]
def set_path(new_condor_bin_path, new_condor_sbin_path=None):
"""Set path to condor binaries, if needed
Works changing the global variables condor_bin_path and condor_sbin_path
Args:
new_condor_bin_path (str): directory where the HTCondor binaries are located
new_condor_sbin_path (str): directory where the HTCondor system binaries are located
"""
global condor_bin_path, condor_sbin_path
condor_bin_path = new_condor_bin_path
if new_condor_sbin_path is not None:
condor_sbin_path = new_condor_sbin_path
[docs]
def exe_cmd(condor_exe, args, stdin_data=None, env={}):
"""Execute an arbitrary condor command and return its output as a list of lines
Fails if stderr is not empty
Args:
condor_exe (str): condor_exe uses a relative path to $CONDOR_BIN
args (str): arguments for the command
stdin_data (str): Data that will be fed to the command via stdin
env (dict): Environment to be set before execution
Returns:
Lines of stdout from the command
Raises:
UnconfigError:
ExeError:
"""
global condor_bin_path
if condor_bin_path is None:
raise UnconfigError("condor_bin_path is undefined!")
condor_exe_path = os.path.join(condor_bin_path, condor_exe)
cmd = f"{condor_exe_path} {args}"
return iexe_cmd(cmd, stdin_data, env)
[docs]
def exe_cmd_sbin(condor_exe, args, stdin_data=None, env={}):
global condor_sbin_path
if condor_sbin_path is None:
raise UnconfigError("condor_sbin_path is undefined!")
condor_exe_path = os.path.join(condor_sbin_path, condor_exe)
cmd = f"{condor_exe_path} {args}"
return iexe_cmd(cmd, stdin_data, env)
############################################################
#
# P R I V A T E, do not use
#
############################################################
[docs]
def generate_bash_script(cmd, environment):
"""Print to a string a shell script setting the environment in 'environment' and running 'cmd'
If 'cmd' last argument is a file it will be printed as well in the string
Args:
cmd (str): command string
environment (dict): environment as a dictionary
Returns:
str: multi-line string with environment, command and eventually the input file
"""
script = ["script to reproduce failure:", "-" * 20 + " begin script " + "-" * 20, "#!/bin/bash"]
# FROM:migration_3_1, 3 lines
# script = ['script to reproduce failure:']
# script.append('-' * 20 + ' begin script ' + '-' * 20)
# script.append('#!/bin/bash')
script += [f"{k}={v}" for k, v in environment.items()]
script.append(cmd)
script.append("-" * 20 + " end script " + "-" * 20)
cmd_list = cmd.split()
if len(cmd_list) > 1:
last_par = cmd_list[-1]
if last_par and os.path.isfile(last_par):
script.append("-" * 20 + " parameter file: %s " % last_par + "-" * 20)
try:
with open(last_par) as f:
script += f.read().splitlines()
except OSError:
pass
script.append("-" * 20 + " end parameter file " + "-" * 20)
return "\n".join(script)
[docs]
def iexe_cmd(cmd, stdin_data=None, child_env=None, log=None):
"""Fork a process and execute cmd - rewritten to use select to avoid filling
up stderr and stdout queues.
Args:
cmd (str): Sting containing the entire command including all arguments
stdin_data (str): Data that will be fed to the command via stdin
child_env (dict): Environment to be set before execution
Returns:
list of str: Lines of stdout from the command
Raises:
ExeError
"""
stdout_data = ""
if log is None:
log = logSupport.log
try:
# invoking subprocessSupport.iexe_cmd w/ text=True (default), stdin_data and returned output are str
stdout_data = subprocessSupport.iexe_cmd(cmd, stdin_data=stdin_data, child_env=child_env)
except CalledProcessError as ex:
msg = f"Failed condor command '{cmd}'. Exit code: {ex.returncode}. Stdout: {ex.stdout}. Stderr: {ex.stderr}"
try:
if log is not None:
log.error(msg)
log.debug(generate_bash_script(cmd, os.environ))
except Exception:
# log may be missing
pass
raise ExeError(msg) from ex
except Exception as ex:
msg = f"Unexpected Error running '{cmd}'. Details: {ex}. Stdout: {stdout_data}"
try:
if log is not None:
log.error(msg)
log.debug(generate_bash_script(cmd, os.environ))
except Exception:
# log may be missing
pass
raise ExeError(msg) from ex
return stdout_data.splitlines()
#########################
# Module initialization
#
[docs]
def init1():
"""Set condor_bin_path"""
global condor_bin_path
# try using condor commands to find it out
try:
condor_bin_path = iexe_cmd("condor_config_val BIN")[0].strip() # remove trailing newline
except ExeError:
# try to find the RELEASE_DIR, and append bin
try:
release_path = iexe_cmd("condor_config_val RELEASE_DIR")
condor_bin_path = os.path.join(release_path[0].strip(), "bin")
except ExeError:
# try condor_q in the path
try:
condorq_bin_path = iexe_cmd("which condor_q")
condor_bin_path = os.path.dirname(condorq_bin_path[0].strip())
except ExeError:
# look for condor_config in /etc
if "CONDOR_CONFIG" in os.environ:
condor_config = os.environ["CONDOR_CONFIG"]
else:
condor_config = "/etc/condor/condor_config"
try:
# BIN = <path>
bin_def = iexe_cmd('grep "^ *BIN" %s' % condor_config)
condor_bin_path = bin_def[0].strip().split()[2]
except ExeError:
try:
# RELEASE_DIR = <path>
release_def = iexe_cmd('grep "^ *RELEASE_DIR" %s' % condor_config)
condor_bin_path = os.path.join(release_def[0].strip().split()[2], "bin")
except ExeError:
pass # don't know what else to try
[docs]
def init2():
"""Set condor_sbin_path"""
global condor_sbin_path
# try using condor commands to find it out
try:
condor_sbin_path = iexe_cmd("condor_config_val SBIN")[0].strip() # remove trailing newline
except ExeError:
# try to find the RELEASE_DIR, and append bin
try:
release_path = iexe_cmd("condor_config_val RELEASE_DIR")
condor_sbin_path = os.path.join(release_path[0].strip(), "sbin")
except ExeError:
# try condor_q in the path
try:
condora_sbin_path = iexe_cmd("which condor_advertise")
condor_sbin_path = os.path.dirname(condora_sbin_path[0].strip())
except ExeError:
# look for condor_config in /etc
if "CONDOR_CONFIG" in os.environ:
condor_config = os.environ["CONDOR_CONFIG"]
else:
condor_config = "/etc/condor/condor_config"
try:
# BIN = <path>
bin_def = iexe_cmd('grep "^ *SBIN" %s' % condor_config)
condor_sbin_path = bin_def[0].strip().split()[2]
except ExeError:
try:
# RELEASE_DIR = <path>
release_def = iexe_cmd('grep "^ *RELEASE_DIR" %s' % condor_config)
condor_sbin_path = os.path.join(release_def[0].strip().split()[2], "sbin")
except ExeError:
pass # don't know what else to try
[docs]
def init():
"""Set both Set condor_bin_path and condor_sbin_path"""
init1()
init2()
# This way we know that it is undefined
condor_bin_path = None
condor_sbin_path = None
init()