#!/usr/bin/env python # encoding: utf-8 """ $Id: adl_common.py 2196 2014-07-15 13:43:48Z scottm $ Purpose: Common routines for ADL XDR handling and ancillary data caching. Requires: adl_asc Created Oct 2011 by R.K.Garcia <rayg@ssec.wisc.edu> Copyright (c) 2011 University of Wisconsin Regents. Licensed under GNU GPLv3. """ import os import sys import logging import log_common import time import types import fileinput from subprocess import Popen, CalledProcessError, call, PIPE import datetime LOG = logging.getLogger(__name__) PROFILING_ENABLED = os.environ.get('CSPP_PROFILE', None) is not None STRACE_ENABLED = os.environ.get('CSPP_STRACE', None) is not None class SingleLevelFilter(logging.Filter): """ ref: http://stackoverflow.com/questions/1383254/logging-streamhandler-and-standard-streams """ def __init__(self, passlevels, reject): """ :rtype : object :param passlevels: :param reject: """ super(SingleLevelFilter, self).__init__() self.passlevels = set(passlevels) self.reject = reject def filter(self, record): """ :param record: :return: """ if self.reject: return record.levelno not in self.passlevels else: return record.levelno in self.passlevels def split_search_path(s): """break a colon-separated list of directories into a list of directories, else empty-list""" if not s: return [] back_list = [] for path in s.split(':'): back_list.append(os.path.abspath(path)) return back_list def _replaceAll(intputfile, searchExp, replaceExp): """ :param intputfile: :param searchExp: :param replaceExp: """ for line in fileinput.input(intputfile, inplace=1): if searchExp in line: line = line.replace(searchExp, replaceExp) sys.stdout.write(line) fileinput.close() # ("RangeDateTime" DATETIMERANGE EQ "2014-01-13 11:22:39.900000" "2014-01-13 11:22:59.900000") class AscLineParser(object): def time_range(self, ascLine): """ :param ascLine: :return: """ day, time = self.extract_time_range_tokens(ascLine) return self.time_from_tokens(day, time) def extract_time_range_tokens(self, ascLine): return ascLine.split('"')[3:4][0].split(' ') def time_from_tokens(self, day, time): dt = datetime.datetime.strptime(day + time, '%Y-%m-%d%H:%M:%S.%f') return dt def _testParser(): """ """ dt = AscLineParser().time_range( '("RangeDateTime" DATETIMERANGE EQ "2014-01-13 11:22:39.900000" "2014-01-13 11:22:59.900000")') print (dt) class CsppEnvironment(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) def check_and_convert_path(key, a_path, check_write=False): """ Make sure the path or paths specified exist Return the path or list of absolute paths that exist """ try: basestring except NameError: basestring = str abs_locations = [] if ":" in a_path: paths = a_path.split(":") elif isinstance(a_path, basestring): # elif isinstance(a_path, types.StringTypes): paths = [a_path] else: paths = a_path for path in paths: if not os.path.exists(path): if key: msg = "Environment variable %s refers to a path that does not exists. %s=%s" % (key, key, path) else: msg = "Required path %s does not exist. " % (path) raise CsppEnvironment(msg) else: LOG.debug("Found: %s at %s %s" % (key, path, os.path.abspath(path))) abs_locations.append(os.path.abspath(path)) if check_write: if not os.access(path, os.W_OK): msg = "Path exists but is not writable %s=%s" % (key, path) raise CsppEnvironment(msg) sys.exit(2) # return a string if only one and an array if more if len(abs_locations) == 1: return abs_locations[0] else: # return abs_locations # return a :-joined string for use in an env variable return ':'.join(abs_locations) def check_existing_env_var(varname, default_value=None): """ Check for vaiable if it exists use vale otherwise use default """ if varname in os.environ: value = os.environ.get(varname) else: if default_value is not None: value = default_value else: print("ERROR: %s is not set, please update environment and re-try" % varname) >> sys.stderr LOG.error("Environment variable missing. %s" % varname) sys.exit(9) return value def check_and_convert_env_var(varname, check_write=False, default_value=None): value = check_existing_env_var(varname, default_value=default_value) path = check_and_convert_path(varname, value, check_write=check_write) return path def what_package_am_i(): path = os.path.dirname(os.path.abspath(__file__)) cspp_x = path.split("/common") cspp_x_home = cspp_x[0] return cspp_x_home def _ldd_verify(exe): """check that a program is ready to run""" rc = call(['ldd', exe], stdout=os.tmpfile(), stderr=os.tmpfile()) return rc == 0 def check_env(): """ Check that needed environment variables are set""" for key in EXTERNAL_BINARY.iterkeys(): if not _ldd_verify(EXTERNAL_BINARY[key]): LOG.warning("%r executable is unlikely to run, is LD_LIBRARY_PATH set?" % EXTERNAL_BINARY[key]) def env(**kv): """augment environment with new values""" zult = dict(os.environ) zult.update(kv) return zult def execute_binary_captured_io(work_dir, cmd, **kv): """ Execute the specifed ancillary script. process the ouptut and return the file names. """ LOG.debug('executing %r with kv=%r' % (cmd, kv)) pop = Popen(cmd, cwd=work_dir, env=env(**kv), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) anc_stdout, anc_stderr = pop.communicate() rc = pop.returncode if rc == 0: LOG.debug("success " + cmd) LOG.info(anc_stdout.strip()) elif rc == 1: LOG.info(anc_stdout) LOG.info(anc_stderr) LOG.error("stderr:" + anc_stderr) else: LOG.warn("what " + cmd) if rc != 0: LOG.debug(anc_stdout) LOG.error(anc_stderr) return None def simple_sh(cmd, log_execution=True, *args, **kwargs): """like subprocess.check_call, but returning the pid the process was given""" if STRACE_ENABLED: strace = open('strace.log', 'at') print ("= " * 32) >> strace print(repr(cmd)) >> strace cmd = ['strace'] + list(cmd) pop = Popen(cmd, *args, stderr=strace, **kwargs) else: pop = Popen(cmd, *args, **kwargs) pid = pop.pid startTime = time.time() rc = pop.wait() endTime = time.time() delta = endTime - startTime LOG.debug('statistics for "%s"' % ' '.join(cmd)) if log_execution: log_common.status_line('Execution Time: %f Sec Cmd "%s"' % (delta, ' '.join(cmd))) if rc != 0: exc = CalledProcessError(rc, cmd) exc.pid = pid raise exc return pid def profiled_sh(cmd, log_execution=True, *args, **kwargs): """ like subprocess.check_call, but returning the pid the process was given and logging as INFO the final content of /proc/PID/stat :param cmd: :param log_execution: :param args: :param kwargs: """ pop = Popen(cmd, *args, **kwargs) pid = pop.pid fn = '/proc/%d/status' % pid LOG.debug('retrieving %s statistics to caller dictionary' % fn) proc_stats = '-- no /proc/PID/status data --' rc = 0 startTime = time.time() while True: time.sleep(1.0) rc = pop.poll() if rc is not None: break try: proc = file(fn, 'rt') proc_stats = proc.read() proc.close() del proc except IOError: LOG.warning('unable to get stats from %s' % fn) endTime = time.time() delta = endTime - startTime LOG.debug('statistics for "%s"' % ' '.join(cmd)) if log_execution: log_common.status_line('Execution Time: "%f" Sec Cmd "%s"' % (delta, ' '.join(cmd))) LOG.debug(proc_stats) if rc != 0: exc = CalledProcessError(rc, cmd) exc.pid = pid raise exc return pid # default sh() is to profile on linux systems if os.path.exists('/proc') and PROFILING_ENABLED: sh = profiled_sh else: sh = simple_sh def hello(): """ """ print('hello') test_vars={'fish':'food','hair':'brush'} work_dir='/work' cmd='echo "hello"' execute_binary_captured_io(work_dir, cmd,**test_vars) if __name__ == '__main__': log_common.configure_logging(level=logging.DEBUG) hello() # logging.basicConfig(level=logging.DEBUG) we don't want basicConfig anymore # log_common.configure_logging(level=logging.DEBUG, FILE="testlog.log")