diff options
Diffstat (limited to 'aprio.py')
-rwxr-xr-x | aprio.py | 334 |
1 files changed, 0 insertions, 334 deletions
diff --git a/aprio.py b/aprio.py deleted file mode 100755 index 41aec0e..0000000 --- a/aprio.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env python -# -# aprio is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# aprio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with aprio. If not, see <http://www.gnu.org/licenses/>. -""" aprio stands for "Automatic Priority" -""" - -import os -import logging, logging.config -import time -from datetime import timedelta - - -try: - import psutil -except ImportError: - print("psutil module not found!") - exit(1) - -try: - import daemon -except ImportError: - print("daemon module not found!") - exit(1) - - -try: - import argparse -except ImportError: - print("argparse module not found!") - exit(1) - - -class Transpire(object): - """Supply common time constants - """ - def __init__(self): - self.second = timedelta(seconds=1).total_seconds() - self.minute = timedelta(minutes=1).total_seconds() - self.hour = timedelta(hours=1).total_seconds() - self.day = timedelta(days=1).total_seconds() - self.week = timedelta(weeks=1).total_seconds() - self.month = timedelta(weeks=4).total_seconds() - - -ELAPSED = Transpire() -CONFIG = {} -CONFIG['LOAD_THRESHOLD'] = psutil.cpu_count() / 2 -if CONFIG['LOAD_THRESHOLD'] < 1: - CONFIG['LOAD_THRESHOLD'] = psutil.cpu_count() -CONFIG['CPU_THRESHOLD'] = 50.0 -CONFIG['CPUTIME_THRESHOLD'] = ELAPSED.second -CONFIG['POLL'] = 3 -CONFIG['TEST_MODE'] = False -CONFIG['VERBOSE'] = False -CONFIG['QUITE'] = False - - -def renice(proc, nice_value=0): - """Change the process priority of a psutil.Process object - """ - logger = logging.getLogger(__name__) - nice_current = 255 - try: - pid = proc.pid - nice_previous = proc.get_nice() - - if nice_previous < 0: - return - - if nice_value <= nice_previous: - return - - if not CONFIG['TEST_MODE']: - proc.set_nice(nice_value) - - if not CONFIG['TEST_MODE']: - nice_current = proc.get_nice() - else: - nice_current = nice_value - - logger.info("{0}:Priority modified ({1} -> {2})" - .format(pid, nice_previous, nice_current)) - except psutil.AccessDenied: - logger.warning("{0}:{1}:Permission denied setting nice to {2}" - .format(pid, proc.username(), nice_value)) - except psutil.NoSuchProcess: - return - return nice_current - - -def convert_nice(proc, **kwargs): - """Analyzes a process' total kernel time, or the time since the process - began. If the time meets or exceeds a defined threshold, an - appropriate nice value will be applied to the process. - - Keyword Arguments: - model -- (default 'relative') - 'kernel' = Total CPU time accumulated - 'relative' = Total time elapsed since process started - """ - logger = logging.getLogger(__name__) - model = 'relative' - if kwargs.has_key('model'): - model = kwargs['model'] - - if model == 'kernel': - time_user, time_system = proc.cpu_times() - total_time = time_user + time_system - elif model == 'relative': - total_time = time.time() - proc.create_time() - else: - raise ValueError('"{0}" is not a valid time model'.format(model)) - - logger.debug('Time model "{0}"'.format(model)) - nice = 0 - if total_time >= ELAPSED.month: - nice = 20 - elif total_time >= ELAPSED.week: - nice = 17 - elif total_time >= ELAPSED.day: - nice = 15 - elif total_time >= ELAPSED.day / 2: - nice = 11 - elif total_time >= ELAPSED.hour: - nice = 9 - elif total_time >= ELAPSED.hour / 2: - nice = 4 - elif total_time >= ELAPSED.minute: - nice = 2 - elif total_time >= ELAPSED.minute / 2: - nice = 1 - - return nice - - -def filter_processes(cpu_threshold=CONFIG['CPU_THRESHOLD'], - cputime_threshold=CONFIG['CPUTIME_THRESHOLD'], - **kwargs): - """Yield a filtered process list matching system usage criteria. - - Keyword ARGUMENTS: - cpu_threshold -- must exceed CPU% (default 50.0) - cputime_threshold -- must exceed CPU TIME in seconds (default 1.0) - user -- yield processes owned by a particular account - """ - logger = logging.getLogger(__name__) - user = "" - if kwargs.has_key('user'): - user = kwargs['user'] - - for proc in psutil.process_iter(): - try: - pid = proc.pid - username = proc.username() - user_time, system_time = proc.cpu_times() - cputime_total = user_time + system_time - uid, euid, _ = proc.uids() - - if uid == 0 or euid == 0: - continue - - - cpu = proc.get_cpu_percent(interval=0.05) - if cpu > cpu_threshold: - logger.debug("{0}:cpu_threshold ({1}% > {2}%)" - .format(pid, cpu, cpu_threshold)) - if cputime_total > cputime_threshold: - logger.debug("{0}:cputime_threshold ({1} > {2})" - .format(pid, cputime_total, cputime_threshold)) - if user: - if user != username: - continue - yield proc - - except psutil.NoSuchProcess as ex: - logger.debug("{0}:disappeared".format(ex.pid)) - - -def main(args): - """ Poll system for bad processes - """ - logger = logging.getLogger(__name__) - CONFIG['POLL'] = args.poll - CONFIG['VERBOSE'] = args.verbose - CONFIG['QUIET'] = args.quiet - CONFIG['TEST_MODE'] = args.test - CONFIG['CPU_THRESHOLD'] = args.cpu_threshold - CONFIG['CPUTIME_THRESHOLD'] = args.cputime_threshold - CONFIG['LOAD_THRESHOLD'] = args.load_threshold - CONFIG['DAEMON'] = args.daemon - user = args.user - - - load_sleep = False - load_warn = False - - while(True): - load = os.getloadavg() - load = sum(load) / len(load) - if load < CONFIG['LOAD_THRESHOLD']: - load_sleep = True - load_warn = False - if load_sleep: - logger.debug("load_threshold nominal ({0} < {1})" - .format(load, CONFIG['LOAD_THRESHOLD'])) - load_sleep = False - time.sleep(CONFIG['POLL']) - continue - else: - load_warn = True - - if load_warn: - logger.debug("load_threshold exceeded ({0} > {1})" - .format(load, CONFIG['LOAD_THRESHOLD'])) - - for bad in filter_processes(CONFIG['CPU_THRESHOLD'], - CONFIG['CPUTIME_THRESHOLD'], - user=user): - try: - nice = convert_nice(bad, model='kernel') - - if not nice: - nice = convert_nice(bad) - - if nice != 0: - renice(bad, nice) - - except psutil.NoSuchProcess: - continue - time.sleep(CONFIG['POLL']) - - -if __name__ == "__main__": - - PARSER = argparse.ArgumentParser() - PARSER.add_argument('--daemon', - '-d', - action='store_true', - help="Fork into background") - - PARSER.add_argument('--logfile', - '-L', - action='store', - default="", - type=str, - help="Log output to filename") - - PARSER.add_argument('--user', - '-u', - default="", - type=str, - help='Limit to specific user') - - PARSER.add_argument('--cpu-threshold', - '-c', - default=CONFIG['CPU_THRESHOLD'], - type=float, - help='Trigger after n%%') - - PARSER.add_argument('--cputime-threshold', - '-t', - default=CONFIG['CPUTIME_THRESHOLD'], - type=float, - help='Trigger after n%%') - - PARSER.add_argument('--load-threshold', - '-l', - default=CONFIG['LOAD_THRESHOLD'], - type=float, - help='Trigger after n load average') - - PARSER.add_argument('--poll', - '-p', - default=CONFIG['POLL'], - type=float, - help='Wait n seconds between polling processes') - - PARSER.add_argument('--test', - '-T', - action='store_true', - default=False, - help='Do not modify processes; report only.') - - PARSER.add_argument('--verbose', - '-v', - action='store_true', - default=False, - help='Verbose output') - - PARSER.add_argument('--quiet', - '-q', - action='store_true', - default=False, - help='Suppress output') - - ARGUMENTS = PARSER.parse_args() - - FORMAT = "%(levelname)s:%(asctime)s:%(funcName)s:%(message)s" - if ARGUMENTS.logfile: - logging.basicConfig(filename=os.path.abspath(ARGUMENTS.logfile), - format=FORMAT) - else: - logging.basicConfig(format=FORMAT) - - logging.basicConfig(level=logging.INFO) - LOGGER = logging.getLogger(__name__) - LOGGER.setLevel(logging.INFO) - - - if ARGUMENTS.verbose: - LOGGER.setLevel(logging.DEBUG) - - if ARGUMENTS.test: - LOGGER.debug('Test mode (processes will not be modified)') - - if ARGUMENTS.daemon: - LOGGER.debug('Daemon mode') - with daemon.DaemonContext(): - main(ARGUMENTS) - else: - LOGGER.debug('Foreground mode') - main(ARGUMENTS) - |