#!/usr/bin/env pmpython
#
# Copyright (C) 2016 fujitsu (wulm.fnst@cn.fujitsu.com).
#
# 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,bad-whitespace
""" Provide information on IPC facilities """

import sys
from pcp import pmapi
from cpmapi import PM_TYPE_U32

class Ipcs(object):
    """ provides information on the inter-process communication
        facilities for which the calling process has read access.
    """

    def __init__(self):
        """ Construct object - prepare for command line handling """
        self.show_limit = 0
        self.show_summary = 0
        self.opts = self.options()
        self.context = None

    def options(self):
        """ Setup default command line argument option handling """
        opts = pmapi.pmOptions()
        opts.pmSetOptionCallback(self.option)
        opts.pmSetShortOptions("luV?")
        opts.pmSetLongOptionHeader("Options")
        # other options will to do(wulm.fnst@cn.fujitsu.com)
        opts.pmSetLongOption("limits", 0, 'l', '', "show resource limits")
        opts.pmSetLongOption("summary", 0, 'u', '', "show status summary")
        opts.pmSetLongOptionVersion()
        opts.pmSetLongOptionHelp()
        return opts

    def option(self, opt, optarg, index):
        """ Perform setup for an individual command line option """
        if opt == 'l':
            self.show_limit = 1
        elif opt == 'u':
            self.show_summary = 1

    def extract(self, descs, result):
        """ Extract the set of metric values from a given pmResult """
        values = []
        for index, desc in enumerate(descs):
            if result.contents.get_numval(index) > 0:
                atom = self.context.pmExtractValue(
                                result.contents.get_valfmt(index),
                                result.contents.get_vlist(index, 0),
                                desc.contents.type, PM_TYPE_U32)
                values.append(int(atom.ul))
            else:
                values.append(int(0))
        return values

    def execute(self):
        """ Using a PMAPI context (could be either host or archive),
            fetch and report a fixed set of values related to ipc.
        """
        metrics_limit = ('ipc.msg.max_msgqid',  'ipc.msg.max_msgsz',
                         'ipc.msg.max_defmsgq', 'ipc.shm.max_seg',
                         'ipc.shm.max_segsz',   'ipc.shm.max_shmsys',
                         'ipc.shm.min_segsz',   'ipc.sem.max_semid',
                         'ipc.sem.max_perid',   'ipc.sem.num_undo',
                         'ipc.sem.max_ops',     'ipc.sem.max_semval')

        metrics_summary = ('ipc.msg.used_queues', 'ipc.msg.tot_msg',
                           'ipc.msg.tot_bytes', 'ipc.shm.used_ids',
                           'ipc.shm.tot',       'ipc.shm.rss',
                           'ipc.shm.swp',       'ipc.shm.swap_attempts',
                           'ipc.shm.swap_successes',
                           'ipc.sem.used_sem',  'ipc.sem.tot_sem')
        pmids_limit = self.context.pmLookupName(metrics_limit)
        descs_limit = self.context.pmLookupDescs(pmids_limit)

        pmids_summary = self.context.pmLookupName(metrics_summary)
        descs_summary = self.context.pmLookupDescs(pmids_summary)

        if self.show_limit == 1:
            result_limit = self.context.pmFetch(pmids_limit)
            values_limit = self.extract(descs_limit, result_limit)
            self.context.pmFreeResult(result_limit)
            self.report(values_limit)

        elif self.show_summary == 1:
            result_summary = self.context.pmFetch(pmids_summary)
            values_summary = self.extract(descs_summary, result_summary)
            self.context.pmFreeResult(result_summary)
            self.report(values_summary)

    def get_pagesize(self):
        metric_pagesize = "hinv.pagesize"
        pmid_pagesize = self.context.pmLookupName(metric_pagesize)
        desc_pagesize = self.context.pmLookupDescs(pmid_pagesize)
        result = self.context.pmFetch(pmid_pagesize)
        pagesize = self.extract(desc_pagesize, result)
        return pagesize[0]

    def report(self, values):
        kb = 1024
        pagesize = self.get_pagesize()
        pagesize_kb = pagesize / kb
        if self.show_limit == 1:
            print("\n------ Messages Limits --------")
            print("max queues system wide = %u" %values[0])
            print("max size of message (bytes) = %u" %values[1])
            print("default max size of queue (bytes) = %u" %values[2])
            print("\n------ Shared Memory Limits --------")
            print("max number of segments = %u" %values[3])
            print("max seg size (kbytes) = %u" %(values[4] / kb))
            print("max total shared memory (kbytes) = %u" %(values[5] * pagesize_kb))
            print("min seg size (bytes) = %u" %values[6])
            print("\n------ Semaphore Limits --------")
            print("max number of arrays = %u" %values[7])
            print("max semaphores per array = %u" %values[8])
            print("max semaphores system wide = %u" %values[9])
            print("max ops per semop call = %u" %values[10])
            print("semaphore max value = %u" %values[11])
        elif self.show_summary == 1:
            print("\n------ Messages Status --------")
            print("allocated queues = %u" %values[0])
            print("used headers = %u" %values[1])
            print("used space = %u  bytes" %values[2])
            print("\n------ Shared Memory Status --------")
            print("segments allocated   %u" %values[3])
            print("pages allocated    %u" %(values[4] / pagesize))
            print("pages resident   %u" %(values[5] / pagesize))
            print("pages swapped   %u" %values[6])
            print("Swap performance: %u attempts     %u successes" %(values[7], values[8]))
            print("\n------ Semaphore Status --------")
            print("used arrays = %u" %values[9])
            print("allocated semaphores = %u" %values[10])

    def connect(self):
        """ Establish a PMAPI context to archive, host or local, via args """
        self.context = pmapi.pmContext.fromOptions(self.opts, sys.argv)

if __name__ == '__main__':
    try:
        IPCS = Ipcs()
        IPCS.connect()
        IPCS.execute()
    except pmapi.pmErr as error:
        print("%s: %s" % (error.progname(), error.message()))
    except pmapi.pmUsageErr as usage:
        usage.message()
    except KeyboardInterrupt:
        pass
