#!/usr/bin/env python
"""
SNMP agent to parse CPU and ACPI TZ sensors from sysctl info on FreeBSD,
updating every 30 seconds

This implementation uses linux 'lm-sensors' OID tree
"""

from subprocess import Popen, PIPE
from seine.snmp.agent import SNMPAgent, Item

from systematic.smart import SmartCtlClient, SmartError

OID_PREFIX = '1.3.6.1.4.1.2021.13.18'


class SmartStatusMonitorAgent(SNMPAgent):
    def __init__(self):
        super(SmartStatusMonitorAgent, self).__init__(OID_PREFIX, reload_interval=30)

        self.index_tree = self.register_tree('{0}.2.1.1'.format(OID_PREFIX))
        self.names_tree = self.register_tree('{0}.2.1.2'.format(OID_PREFIX))
        self.model_tree = self.register_tree('{0}.2.1.3'.format(OID_PREFIX))
        self.serial_tree = self.register_tree('{0}.2.1.4'.format(OID_PREFIX))
        self.size_tree = self.register_tree('{0}.2.1.5'.format(OID_PREFIX))
        self.health_tree = self.register_tree('{0}.2.1.6'.format(OID_PREFIX))
        self.hours_tree = self.register_tree('{0}.2.1.7'.format(OID_PREFIX))
        self.temperature_tree = self.register_tree('{0}.2.1.8'.format(OID_PREFIX))
        self.power_cycles_tree = self.register_tree('{0}.2.1.9'.format(OID_PREFIX))
        self.reallocated_sectors_tree = self.register_tree('{0}.2.1.10'.format(OID_PREFIX))
        self.crc_errors_tree = self.register_tree('{0}.2.1.11'.format(OID_PREFIX))
        self.wear_levelings_tree = self.register_tree('{0}.2.1.12'.format(OID_PREFIX))
        self.total_lbas_written_tree = self.register_tree('{0}.2.1.13'.format(OID_PREFIX))

        self.client = SmartCtlClient()
        self.reload()

    def check_drives(self):
        self.client.scan()

        for drive in self.client.drives:

            info = drive.get_info()
            if not info:
                continue

            attributes = drive.get_attributes()
            if not attributes:
                continue

            self.index_tree.add_values('integer', [self.drive_index])
            self.names_tree.add_values('string', ['{0}'.format(drive.device)])
            self.health_tree.add_values('string', ['{0}'.format(drive.is_healthy and 'HEALTHY' or 'ERROR' )])

            self.model_tree.add_values('string', [info['Device model'].value])
            self.serial_tree.add_values('string', [info['Serial number'].value])
            self.size_tree.add_values('integer', [info['User capacity'].value / 1000 / 1000 / 1000])

            try:
                hours = attributes['power_on_hours']
            except KeyError:
                hours = -1
            self.hours_tree.add_values('integer', [hours['raw_value']])

            for attr in ( 'temperature_celsius',  'airflow_temperature_cel', ):
                try:
                    temperature = int(attributes[attr]['raw_value'])
                    self.temperature_tree.add_values('integer', [temperature])
                    break
                except ValueError:
                    pass
                except KeyError:
                    pass

            try:
                power_cycles = int(attributes['power_cycle_count']['raw_value'])
                self.power_cycles_tree.add_values('integer', [power_cycles])
            except ValueError:
                self.power_cycles_tree.add_values('integer', [-1])
            except KeyError:
                self.power_cycles_tree.add_values('integer', [-1])

            try:
                reallocated = int(attributes['reallocated_sector_ct']['raw_value'])
                self.reallocated_sectors_tree.add_values('integer', [reallocated])
            except ValueError:
                self.reallocated_sectors_tree.add_values('integer', [-1])
            except KeyError:
                self.reallocated_sectors_tree.add_values('integer', [-1])

            try:
                crc_errors = int(attributes['crc_error_count']['raw_value'])
                self.crc_errors_tree.add_values('integer', [crc_errors])
            except ValueError:
                self.crc_errors_tree.add_values('integer', [-1])
            except KeyError:
                self.crc_errors_tree.add_values('integer', [-1])

            try:
                wear_levelings = int(attributes['wear_leveling_count']['raw_value'])
                self.wear_levelings_tree.add_values('integer', [wear_levelings])
            except ValueError:
                self.wear_levelings_tree.add_values('integer', [-1])
            except KeyError:
                self.wear_levelings_tree.add_values('integer', [-1])

            try:
                lbas_written = int(attributes['total_lbas_written']['raw_value'])
                self.total_lbas_written_tree.add_values('integer', [lbas_written])
            except ValueError:
                self.total_lbas_written_tree.add_values('integer', [-1])
            except KeyError:
                self.total_lbas_written_tree.add_values('integer', [-1])

            self.drive_index += 1

    def reload(self):
        self.clear()
        self.drive_index = 1
        self.check_drives()

SmartStatusMonitorAgent().run()
