#!/usr/bin/env pmpython
#
# Copyright (C) 2014-2018 Red Hat.
#
# This program 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 2 of the License, or (at your
# option) any later version.
#
# This program 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.
#
# pylint: disable=bad-continuation,consider-using-enumerate
#
""" Display NUMA memory allocation statistucs """

import os
import re
import signal
import sys
import time

from pcp import pmapi
from pcp import pmcc
from cpmapi import PM_CONTEXT_ARCHIVE

if sys.version >= '3':
    long = int  # python2 to python3 portability (no long() in python3)
    xrange = range  # more back-compat (xrange() is range() in python3)

NUMA_METRICS = [
    "mem.numa.alloc.hit",
    "mem.numa.alloc.miss",
    "mem.numa.alloc.foreign",
    "mem.numa.alloc.interleave_hit",
    "mem.numa.alloc.local_node",
    "mem.numa.alloc.other_node",
]

MEM_METRICS = [
    "mem.numa.util.total",
    "mem.numa.util.free",
    "mem.numa.util.used",
    "mem.numa.util.active",
    "mem.numa.util.inactive",
    "mem.numa.util.active_anon",
    "mem.numa.util.inactive_anon",
    "mem.numa.util.active_file",
    "mem.numa.util.inactive_file",
    "mem.numa.util.unevictable",
    "mem.numa.util.mlocked",
    "mem.numa.util.dirty",
    "mem.numa.util.writeback",
    "mem.numa.util.filePages",
    "mem.numa.util.mapped",
    "mem.numa.util.anonpages",
    "mem.numa.util.shmem",
    "mem.numa.util.kernelStack",
    "mem.numa.util.pageTables",
    "mem.numa.util.NFS_Unstable",
    "mem.numa.util.bounce",
    "mem.numa.util.writebackTmp",
    "mem.numa.util.filehugepages",
    "mem.numa.util.filepmdmapped",
    "mem.numa.util.slab",
    "mem.numa.util.slabReclaimable",
    "mem.numa.util.slabUnreclaimable",
    "mem.numa.util.anonhugepages",
    "mem.numa.util.shmemhugepages",
    "mem.numa.util.shmempmdmapped",
    "mem.numa.util.hugepagesTotal",
    "mem.numa.util.hugepagesFree",
    "mem.numa.util.hugepagesSurp",
    "mem.numa.util.swapCached",
    "mem.numa.util.kreclaimable",
]

SYS_METRICS = [
    'kernel.uname.nodename',
    'kernel.uname.release',
    'kernel.uname.sysname',
    'kernel.uname.machine',
    'hinv.ncpu',
]

ALL_METRICS = NUMA_METRICS + MEM_METRICS

PROCESS_METRICS = [
    "proc.psinfo.pid",
    "proc.psinfo.cmd",
    "proc.psinfo.psargs",
    "proc.numa_maps.hugepage",
    "proc.numa_maps.heap",
    "proc.numa_maps.stack",
    "proc.numa_maps.private",
]

PROCESS_NUMA_METRICS = [
    ("Huge", "proc.numa_maps.hugepage"),
    ("Heap", "proc.numa_maps.heap"),
    ("Stack", "proc.numa_maps.stack"),
    ("Private", "proc.numa_maps.private"),
]

def prefix(metric):
    last_part = metric.split('.')[-1]
    result = last_part[0].upper() + last_part[1:]
    return result

class MetricRepository:
    def __init__(self, group):
        self.group = group
        self.current_cached_values = {}
        self.previous_cached_values = {}
        self.current_cached_instance_names = {}

    def _fetch_current_values(self, metric, instance):
        if instance is not None:
            return dict(
                map(lambda x: (x[0].inst, x[2]), self.group[metric].netValues)
            )
        else:
            if self.group[metric].netValues == []:
                return None
            else:
                return self.group[metric].netValues[0][2]
    def current_values(self, metric_name):
        if self.group.get(metric_name, None) is None:
            return None
        if self.current_cached_values.get(metric_name, None) is None:
            self.current_cached_values[
                metric_name
            ] = self._fetch_current_values(metric_name, True)
        return self.current_cached_values.get(metric_name, None)

class NUMAStat:

    def __init__(self, group):
        self.group = group
        self.repo = MetricRepository(group)

    def resize(self, width):
        """ Find a suitable display width limit """
        if width == 0:
            if not sys.stdout.isatty():
                width = 1000000000        # mimic numastat(1) here
            else:
                # popen() is SAFE, command is a literal string
                (_, width) = os.popen('stty size', 'r').read().split()
                width = int(width)
            width = int(os.getenv('NUMASTAT_WIDTH', str(width)))
        return max(width, 32)

    def __format_table(self, width, nodes, data):
        null_output = False
        if not nodes:
            null_output = True
            nodes = [(0, 'Node ')]

        if "numastat" in data:
            metrics = NUMA_METRICS
            title = "NUMA memory allocation statistics (pages)"
        else:
            metrics = MEM_METRICS
            title = "Per-node system memory usage (KB)"

        total_w = max(42, int(width))
        print(title[:total_w])

        width = self.resize(width)
        maxnodes = int((width - 16) / 16)
        if maxnodes > len(nodes):        # just an initial header suffices
            header = '%30s' % ''
            for _, node in nodes:
                header += '%-12s' % node
            print(header)

        for m in metrics:
            if not null_output:
                vals = self.repo.current_values(m)
            done = 0  # reset for each metric

            # Loop through nodes in chunks of 'maxnodes'
            while done < len(nodes):
                header = '%-30s' % ''
                window = '%-20s : ' % prefix(m)

                # Slice the range we'll print in this batch
                chunk = nodes[done:done + maxnodes]

                for i, ( _, name) in enumerate(chunk):
                    header += '%-12s' % name
                    if not null_output:
                        window += '%12s' % vals[done + i]
                    else:
                        window += '%12s' % "NA"

                # Print header o                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              