Пример #1
0
    def __init__(self, ps_data):
        """Construct the Posix thread object.

        :param ps_data: The data with which to initialise this object, as generated by execute_ps().
        :type ps_data: dict
        :return: None
        :rtype: None
        """
        super(PosixProcess, self).__init__(ps_data)
        self.threads = dict()  # A process is basically a container of threads
        if 'threads' in ps_data:

            try:
                for children in ps_data['threads']:
                    try:
                        if children['pid'] == children['lwp']:
                            thread = PosixProcess(children)
                        else:
                            thread = PosixThread(children)
                        self.threads[thread.lwp] = thread
                    except KeyError:
                        logger.warning('{0} is not valid thread/process data'.format(children))

            except Exception as e:
                logger.warning('Invalid thread information {0}, ignored.'.format(ps_data['threads']))
Пример #2
0
def execute_ps():
    """Execute the Linux 'ps' command and return the parsed result.

    :return: The parsed result from the ps command, basically a list of thread/process stats objects,
     PosixThread or PosixProcess.
    :rtype: [PosixThread]
    """
    global _ps_args
    ps_proc = subprocess.Popen(_ps_args, stdout=subprocess.PIPE, close_fds=True)
    try:
        ps_out = ps_proc.communicate()
        ps_stdout = str(ps_out[0])  # Using str() to convert the strings '\n' to actual new lines

        matches = _ps_pattern.finditer(ps_stdout)
        if matches:
            ret = []
            x = dict()
            for match in matches:
                raw_data = match.groups()
                ps_data = dict(zip(['user', 'pid', 'ppid', 'lwp', 'pcpu', 'pmem', 'vsz', 'rss', 'comm'], raw_data))
                if ps_data['pid'] == ps_data['lwp']:
                    ret.append(PosixProcess(ps_data))
                else:
                    ret.append(PosixThread(ps_data))

            return ret

        else:
            logger.warning("Didn't find any matches for the ps output {0}".format(ps_stdout))

    finally:
        ps_proc.wait()  # Preventing zombies

    return None
Пример #3
0
def format_process(level, proc, format):
    """Create a printable string representing a Posix process/thread object, recursively.

     The resulting string conforms to the JSON format.

    :param level: The indentation level
    :type level: int
    :param proc: The process object to be represented
    :type proc: com.mitel.pyrate.resmon_util.PosixThread
    :param format: The output format, must be one of 'json', 'pretty', or 'xml'
    :type format: str
    :return: The printable string representing the given process object
    :rtype: str
    """
    if format == 'json':
        return json.dumps(proc.export_data(), indent=2)

    elif format == 'xml':

        if isinstance(proc, PosixThread):
            top = proc.export_xml()
            return ElementTree.tostring(top)

        logger.warning('{0} is neither a process nor thread'.format(proc))
        return None

    else:  # Compact pretty print
        tabs = ''
        if level:
            tabs += ('  ' * level)

        ret = [tabs, '{']
        is_thread = False

        if proc.lwp != proc.pid:
            is_thread = True
            ret.append('lwp:')
            ret.append(repr(proc.lwp))
        else:
            ret.append('user:'******', pid:')
            ret.append(repr(proc.pid))

        ret.append(', %cpu:{0:6.2f}, %mem:{1:6.2f}, rss:{2}, comm:{3}'.format(proc.pcpu, proc.pmem, repr(proc.rss),
                                                                repr(proc.cmd)))

        if isinstance(proc, PosixProcess):
            if proc.threads:
                ret.append(', threads:[\n')
                for pid, subproc in proc.threads.items():
                    ret.append(format_process(level + 1, subproc, format) + ',\n')

                ret.append(tabs)
                ret.append(']')

        ret.append('}')
        return ''.join(ret)
Пример #4
0
    def __call__(self):
        """The run entry for the worker thread."""
        try:
            self.run()  # Sub-classes must implement this.

        except IOError as e:
            logger.warning('{0} encountered {1} at {2}'.format(repr(self), e, traceback.format_tb(sys.exc_info()[2])))

        except BaseException as e:
            logger.error("{0} encountered unexpected {1} at {2}".format(repr(self), e, traceback.format_tb(sys.exc_info()[2])))

        finally:
            # Make sure everything is properly reset
            with self._lock:
                self._runFlag = False

            self.stopWaiting.set()
Пример #5
0
def build_process_tree(pthreads):
    """Build a process tree from a given list of threads, recursively.

    :param pthreads: The list of threads to be processed, the kind generated by execute_ps()
    :type pthreads: list
    :return: The dictionary of processes, keyed by PIDs, created from the given thread list.
    :rtype: dict
    """
    processes = dict()

    # Start by building the process dictionary ...
    for pthread in pthreads:
        if isinstance(pthread, PosixProcess):
            processes[pthread.pid] = pthread

    # ... Then add the threads into the processes ...
    for pthread in pthreads:
        if pthread.pid == pthread.lwp:  # Skip processes
            continue

        try:
            process = processes[pthread.pid]
            process.add_thread(pthread)

        except KeyError as e:
            logger.warning('Cannot find process {0} for thread {1}'.format(pthread.pid, pthread))

    # ... Finally build the process trees and the return data
    ret = dict()  # The return is a dictionary of the root processes, which, in turn, contain all the other processes.
    for pid, proc in processes.items():
        if proc.ppid == 0:  # Root process, add it to the return set
            ret[proc.pid] = proc
            continue

        # If we are here then the process is not the root, and must therefore have a parent.
        try:
            parent = processes[proc.ppid]
            parent.add_thread(proc)

        except KeyError:
            logger.warning('Cannot find parent process {0} for {1}'.format(proc.ppid, proc))

    return ret
Пример #6
0
def main(args):
    """Main entry to program.

    :param args: Parsed command-line args.
    :return: Exit code.
    """
    try:

        start_ts = (int(time.time()) / _step) * _step  # TS should be aligned with the period

        # Step 1: Start the monitoring
        if (not os.path.exists(_base_name + 'cpu.rrd')) or (not os.path.exists(_base_name + 'mem.rrd')):
            fill_in_the_rrds(_base_name + 'cpu.rrd', _base_name + 'mem.rrd', start_ts - (24 * 3600), start_ts, _step)
        else:
            fill_in_the_rrds(_base_name + 'cpu.rrd', _base_name + 'mem.rrd',
                             int(rrdutil.get_last_update(_base_name + 'cpu.rrd')) + _step, start_ts, _step)

        monitor = resmon_util.start_monitor(_base_name, _step)

        try:

            prev_ts = start_ts
            for loop in range(0, 360):  # Run for an hour (360 loops of 10 seconds).

                # Step 2: Wait for some time (10s) for the monitor to gather data
                logger.info('Waiting for data, loop #{0}'.format(loop))
                time.sleep(10)

                # Step 3: Observe the result
                last_cpu_data = rrdutil.fetch_last_data(monitor.get_cpu_rrd(), start_ts=prev_ts, resolution=1)
                last_mem_data = rrdutil.fetch_last_data(monitor.get_mem_rrd(), start_ts=prev_ts)

                # Print out the result
                print('\nLAST CPU data {0}, curr time = {1}'.format(last_cpu_data[0], int(time.time())))
                print(rrdutil.format_cpu_stats(last_cpu_data))

                print('\nLAST Memory data {0}'.format(last_mem_data[0]))
                print(rrdutil.format_mem_stats(last_mem_data))

                prev_ts = (int(time.time()) / _step) * _step  # TS should be aligned with the period

            avg_data = rrdutil.fetch_avg_data(monitor.get_cpu_rrd(), start_ts=start_ts, resolution=10)
            print('\nAVG CPU data {0}'.format(avg_data[0]))
            print(rrdutil.format_cpu_stats(avg_data))

            avg_data = rrdutil.fetch_avg_data(monitor.get_mem_rrd(), start_ts=start_ts, resolution=10)
            print('\nAVG Memory data {0}'.format(avg_data[0]))
            print(rrdutil.format_mem_stats(avg_data))

        finally:

            # Don't forget to stop the monitor when finish
            logger.info('Stopping {0}'.format(monitor))
            monitor.stop()

            # Generate a graph
            end_graf = time.time()
            start_graf = end_graf - (2 * SECONDS_IN_HOUR)

            gen_graph('cpu.png', rrd_name=monitor.get_cpu_rrd(), title='CPU Utilisation', cf='AVERAGE',
                      start_ts=start_graf, end_ts=end_graf,
                      dataset=[('idle', '%Idle'), ('wait', '%Wait'), ('load', 'Load avg')])

            gen_graph('mem.png', rrd_name=monitor.get_mem_rrd(), title='Memory Utilisation', cf='AVERAGE',
                      start_ts=start_graf, end_ts=end_graf,
                      dataset=[('memtotal', 'Total'), ('memfree', 'Free'), ('buffers', 'Buffers'),
                               ('cached', 'Cached')])

    except (TypeError, ValueError) as e:
        logger.warning('Encountered invalid monitor period {0}, at {1}'.format(e, format_tb(sys.exc_info()[2])))

    except KeyboardInterrupt:
        logger.warning('User interruption at {0}'.format(format_tb(sys.exc_info()[2])))

    except BaseException as e:
        logger.error('Encountered unexpected exception {0} at {1}'.format(repr(e), format_tb(sys.exc_info()[2])))
Пример #7
0
def main(args):
    """Main entry to the gen_graph command.

    :param args: Command line argument list. A list of strings.
    :type args: list
    :return: The return code for the command, 0 is success, anything else for failure
    :rtype: int
    """
    exit_code = 0  # Default to success
    parser = None
    try:
        # Parse the given argument list
        parser = argparse.ArgumentParser(prog='fetch', description='Fetch some resource utilisation data from a resmon DB')
        parser.add_argument('-s', metavar='start_time', help='The start time, default to 24h before the end time',
                            type=int)
        parser.add_argument('-e', metavar='end_time', help='The end time, default to now', type=int)
        parser.add_argument('-c', help='Consolidation function: ave | last. Default to ave', type=str,
                            choices=['ave', 'last'], default='ave')
        parser.add_argument('-r', help='Resolution of the requested data, e.g. 1s, 30s, 1m, ...', type=str, required=True)
        parser.add_argument('-p', metavar='period/timeframe', help='If a start time is not specified then this will '
                            'specify the period of time against which the graph will cover. E.g. 23s, 5m, 6h, 1d, 2w',
                            type=str, default='1d')
        parser.add_argument('-f', metavar='RRD_file', help='Name of the RRD file', type=str, required=True)

        arg = parser.parse_args(args=args)

        # Calculating the end timestamp
        end_ts = int(time.time())
        if arg.e:  # End time
            end_ts = int(arg.e)

        # Calculating the start timestamp
        start_ts = end_ts - (24 * 3600)  # Default start time is one day old data
        if arg.s:  # Start time
            start_ts = int(arg.s)
        elif arg.p:  # Period is meaningful only when start time is not specified
            start_ts = end_ts - parse_timespec(arg.p)

        ret = []
        # Calculating the consolidation function
        if arg.c == 'last':
            ret += fetch_last_data(arg.f, start_ts=start_ts, end_ts=end_ts, resolution=parse_timespec(arg.r))
        else:
            ret += fetch_avg_data(arg.f, start_ts=start_ts, end_ts=end_ts, resolution=parse_timespec(arg.r))

        if len(ret) > 2:
            curr_ts = ret[0][0]
            inc = int(ret[0][2])
            print(rrd_fields_to_csv('ts', ret[1]))
            for line in ret[2]:
                print(rrd_fields_to_csv(curr_ts, line))
                curr_ts += inc

    except KeyboardInterrupt:
        tb = sys.exc_info()[2]
        logger.warning('User interruption at {0}'.format(format_tb(tb)))
        exit_code = errno.EINTR

    except ValueError as e:
        tb = sys.exc_info()[2]
        logger.warning('Encountered {0} at {1}'.format(e, format_tb(tb)))
        exit_code = errno.EINVAL

    except Exception as e:
        tb = sys.exc_info()[2]
        logger.warning('Encountered unexpected {0} at {1}'.format(e, format_tb(tb)))
        exit_code = errno.EINVAL

    return exit_code
Пример #8
0
def main(args):
    """Main entry to the gen_graph command.

    :param args: Command line argument list. A list of strings.
    :type args: list
    :return: The return code for the command, 0 is success, anything else for failure
    :rtype: int
    """
    exit_code = 0  # Default to success
    parser = None
    try:
        # Parse the given argument list
        parser = argparse.ArgumentParser(prog="gengraph.py", description="Generate resource utilisation graphs")
        parser.add_argument("image", metavar="img_file", help="Name of the image file", type=str)
        parser.add_argument(
            "-d",
            metavar="data_set",
            help="Space delimited dataset to be included in the graph",
            type=str,
            required=True,
        )
        parser.add_argument(
            "-s", metavar="start_time", help="The start time, default to 24h before the end time", type=int
        )
        parser.add_argument("-e", metavar="end_time", help="The end time, default to now", type=int)
        parser.add_argument(
            "-c",
            help="Consolidation function: ave | last. Default to ave",
            type=str,
            choices=["ave", "last"],
            default="ave",
        )
        parser.add_argument(
            "-p",
            metavar="period/timeframe",
            help="If a start time is not specified then this will "
            "specify the period of time against which the graph will cover. E.g. 23s, 5m, 6h, 1d, 2w",
            type=str,
            default="1d",
        )
        parser.add_argument("-f", metavar="RRD_file", help="Name of the RRD file", type=str, required=True)
        parser.add_argument(
            "-x",
            metavar="width",
            help="The width of the graph, in number of pixels (default=640)",
            type=int,
            default=640,
        )
        parser.add_argument(
            "-y",
            metavar="height",
            help="The height of the graph, in number of pixels (default=480)",
            type=int,
            default=480,
        )

        arg = parser.parse_args(args=args)

        # Calculating the end timestamp
        end_graf = int(time.time())
        if arg.e:  # End time
            end_graf = arg.e

        # Calculating the start timestamp
        start_graf = end_graf - (24 * 3600)  # Default start time is one day old data
        if arg.s:  # Start time
            start_graf = arg.s
        elif arg.p:  # Period is meaningful only when start time is not specified
            start_graf = end_graf - parse_timespec(arg.p)

        # Calculating the consolidation function
        cf = "AVERAGE"
        if arg.c == "last":
            cf = "LAST"

        execute_graph_request(
            rrd_name=arg.f,
            data_set=arg.d.split(),
            image_file=arg.image,
            start_ts=start_graf,
            end_ts=end_graf,
            consolidation_func=cf,
            width=arg.x,
            height=arg.y,
        )

    except KeyboardInterrupt:
        tb = sys.exc_info()[2]
        logger.warning("User interruption at {0}".format(format_tb(tb)))
        exit_code = errno.EINTR

    except ValueError as e:
        tb = sys.exc_info()[2]
        logger.warning("Encountered {0} at {1}".format(e, format_tb(tb)))
        exit_code = errno.EINVAL

    except Exception as e:
        tb = sys.exc_info()[2]
        logger.warning("Encountered unexpected {0} at {1}".format(e, format_tb(tb)))
        exit_code = errno.EINVAL

    return exit_code