#!/usr/bin/env pmpython
#
# Copyright (c) 2012,2018-2019 Red Hat.
# Copyright (c) 2008,2012 Aconex.  All Rights Reserved.
# Copyright (c) 2004 Silicon Graphics, Inc.  All Rights Reserved.
#
# 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.
#

""" PCP lmsensors Performance Metrics Domain Agent """

# pylint: disable=bad-continuation,line-too-long,consider-using-dict-items
# pylint: disable=too-few-public-methods,too-many-nested-blocks

import json
import subprocess
import re
import argparse
import sys
import os.path
from pcp.pmda import PMDA, pmdaMetric
from pcp.pmapi import pmUnits, pmContext as PCP
import cpmapi as c_api

sensorvalues = {}   # lookup sensorname -> sensorvalue (temp/fan)
sensornames = {}    # lookup sensornumber -> sensorname
basename = "lmsensors"

def lmsensors_get():
    ''' read sensor data '''

    if args.inject:
        # we will inject sensor data from a file
        f = open(args.inject.name, args.inject.mode)
        output = f.read()
    else:
        # we will read real sensor data
        with open(os.devnull, 'w') as devnull:
            p = subprocess.Popen(["/usr/bin/sensors", "-j"], stdout=subprocess.PIPE, stderr=devnull)
            output = json.loads(re.sub("[0-9]_", "_", p.communicate()[0].decode("utf-8")))

        if args.debug_value and args.debug_value >= 0:
            print("function lmsensors_get(), json object after removing redundant naming:\n", output)

        for lvl0 in output.keys():
            if args.debug_value and args.debug_value == 1:
                print("level 0 keys:", output[lvl0].keys())
            for lvl1 in output[lvl0].keys():
                adapter = output[lvl0]["Adapter"].lower().replace(" ", "_")
                if lvl1 != "Adapter":
                    for lvl2 in output[lvl0][lvl1]:
                        if "_input" in lvl2:
                            sensorname = (lvl0+"."+adapter+"."+lvl1.replace(".", ",")).lower().replace(" ", "_").replace('-', '_')
                            sensorvalues[sensorname] = output[lvl0][lvl1][lvl2]

        if args.debug_value and args.debug_value == 2:
            print("final array:", sensorvalues)

class LmsensorsPMDA(PMDA):
    ''' lmsensors performance metrics domain agent '''

    def lmsensors_fetch_callback(self, cluster, item, inst):
        ''' Returns a list of value,status (single pair) for one metric '''

        lmsensors_get()
        if cluster == 0:
            # sanity checking: sort out temperatures below -127.
            # some sensors report -128 in error, from time to time.
            if re.search(r'temp', sensornames[item]):
                if int(sensorvalues[sensornames[item]]) < -127:
                    return None
            # sanity checking: sort out negative values from fans
            if re.search(r'fan', sensornames[item]):
                if int(sensorvalues[sensornames[item]]) < 0:
                    return None

            # if we made it until here, plainly return the read value
            return [sensorvalues[sensornames[item]], 1]
            # return [42, 1]
        return [c_api.PM_ERR_PMID, 0]

    def __init__(self, name, domain):
        ''' Initialisation - register metrics, callbacks, drop privileges '''

        PMDA.__init__(self, name, domain)
        self.connect_pmcd()

        sensorcounter = 0
        for k in sensorvalues:
            keysplit = k.split(".")
            self.add_metric(basename + "." + keysplit[0] + "." + keysplit[2],   # metric name
                pmdaMetric(self.pmid(0, sensorcounter),                         # ID
                c_api.PM_TYPE_FLOAT, c_api.PM_INDOM_NULL, c_api.PM_SEM_INSTANT, # type, indom, semantics
                pmUnits(0, 0, 0, 0, 0, 0)),                                     # and units.
                'sensor values from "sensors -u"',                              # short
                'sensor values from ' + k)                                      # long help
            sensornames[sensorcounter] = k
            sensorcounter += 1

        self.set_fetch_callback(self.lmsensors_fetch_callback)
        self.set_user(PCP.pmGetConfig('PCP_USER'))
        if args.debug_value:
            print("debug from __init__:")
            for k in sensorvalues:
                print("  sensorvalues key", k, " : value ", sensorvalues[k])
            for k in sensornames:
                print("  sensornames key", k, " : value ", sensornames[k])


# main
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--inject", type=argparse.FileType('r'),
                    help="inject data from file instead of using sensors")
parser.add_argument("-d", "--debug", dest="debug_value", type=int, choices=[0, 1, 2],
                    help="change debug level, 0 is default")
args = parser.parse_args()

# if args.inject:

if not os.path.isfile('/usr/bin/sensors'):
    print("/usr/bin/sensors not found!  Is lm_sensors installed?")
    sys.exit(1)

lmsensors_get()

if args.debug_value:
    for key in sensorvalues:
        print("dict sensorvalues: ", key, " : ", sensorvalues[key])
if __name__ == '__main__':
    LmsensorsPMDA('lmsensors', 74).run()
