# coding=utf-8

"""
The NetworkCollector class collects metrics on network interface usage
using /proc/net/dev.

#### Dependencies

 * /proc/net/dev

"""

import diamond.collector
import diamond.convertor
import os
import re

try:
    import psutil
except ImportError:
    psutil = None


class NetworkCollector(diamond.collector.Collector):

    PROC = '/proc/net/dev'

    def get_default_config_help(self):
        config_help = super(NetworkCollector, self).get_default_config_help()
        config_help.update({
            'interfaces': 'List of interface types to collect',
            'greedy': 'Greedy match interfaces',
        })
        return config_help

    def get_default_config(self):
        """
        Returns the default collector settings
        """
        config = super(NetworkCollector, self).get_default_config()
        config.update({
            'path':         'network',
            'interfaces':   ['eth', 'bond', 'em', 'p1p'],
            'byte_unit':    ['bit', 'byte'],
            'greedy':       'true',
        })
        return config

    def collect(self):
        """
        Collect network interface stats.
        """

        # Initialize results
        results = {}

        if os.access(self.PROC, os.R_OK):

            # Open File
            file = open(self.PROC)
            # Build Regular Expression
            greed = ''
            if self.config['greedy'].lower() == 'true':
                greed = '\S*'

            exp = ('^(?:\s*)((?:%s)%s):(?:\s*)'
                   + '(?P<rx_bytes>\d+)(?:\s*)'
                   + '(?P<rx_packets>\w+)(?:\s*)'
                   + '(?P<rx_errors>\d+)(?:\s*)'
                   + '(?P<rx_drop>\d+)(?:\s*)'
                   + '(?P<rx_fifo>\d+)(?:\s*)'
                   + '(?P<rx_frame>\d+)(?:\s*)'
                   + '(?P<rx_compressed>\d+)(?:\s*)'
                   + '(?P<rx_multicast>\d+)(?:\s*)'
                   + '(?P<tx_bytes>\d+)(?:\s*)'
                   + '(?P<tx_packets>\w+)(?:\s*)'
                   + '(?P<tx_errors>\d+)(?:\s*)'
                   + '(?P<tx_drop>\d+)(?:\s*)'
                   + '(?P<tx_fifo>\d+)(?:\s*)'
                   + '(?P<tx_frame>\d+)(?:\s*)'
                   + '(?P<tx_compressed>\d+)(?:\s*)'
                   + '(?P<tx_multicast>\d+)(?:.*)$') % (
                ('|'.join(self.config['interfaces'])), greed)
            reg = re.compile(exp)
            # Match Interfaces
            for line in file:
                match = reg.match(line)
                if match:
                    device = match.group(1)
                    results[device] = match.groupdict()
            # Close File
            file.close()
        else:
            if not psutil:
                self.log.error('Unable to import psutil')
                self.log.error('No network metrics retrieved')
                return None

            network_stats = psutil.network_io_counters(True)
            for device in network_stats.keys():
                network_stat = network_stats[device]
                results[device] = {}
                results[device]['rx_bytes'] = network_stat.bytes_recv
                results[device]['tx_bytes'] = network_stat.bytes_sent
                results[device]['rx_packets'] = network_stat.packets_recv
                results[device]['tx_packets'] = network_stat.packets_sent

        for device in results:
            stats = results[device]
            for s, v in stats.items():
                # Get Metric Name
                metric_name = '.'.join([device, s])
                # Get Metric Value
                metric_value = self.derivative(metric_name,
                                               long(v),
                                               diamond.collector.MAX_COUNTER)

                # Convert rx_bytes and tx_bytes
                if s == 'rx_bytes' or s == 'tx_bytes':
                    convertor = diamond.convertor.binary(value=metric_value,
                                                         unit='byte')

                    for u in self.config['byte_unit']:
                        # Public Converted Metric
                        self.publish(metric_name.replace('bytes', u),
                                     convertor.get(unit=u), precision=2)
                else:
                    # Publish Metric Derivative
                    self.publish(metric_name, metric_value)

        return None

@@ -623,16 +623,93 @@
 faces',%0A
+            'fields': 'List or comma-separated string of metrics to collect'%0A
         
@@ -1009,32 +1009,8 @@
   %5B'
-eth', 'bond', 'em', 'p1p
 '%5D,%0A
@@ -1090,16 +1090,93 @@
 'true',%0A
+            'fields':       %5B'rx_errors', 'rx_drop', 'tx_errors', 'tx_drop'%5D%0A
         
@@ -1290,16 +1290,280 @@
    %22%22%22%0A%0A
+        # Initialize fields list%0A        if isinstance(self.config%5B'fields'%5D, basestring):%0A            fields = %5Bf.strip() for f in self.config%5B'fields'%5D.split(',')%5D%0A        elif isinstance(self.config%5B'fields'%5D, list):%0A            fields = self.config%5B'fields'%5D%0A%0A
         
@@ -1981,34 +1981,38 @@
 '(?P%3Crx_packets%3E
+%5B%5Cd
 %5Cw
+%5D
 +)(?:%5Cs*)'%0A     
@@ -2387,18 +2387,22 @@
 packets%3E
+%5B%5Cd
 %5Cw
+%5D
 +)(?:%5Cs*
@@ -2576,21 +2576,21 @@
 '(?P%3Ctx_
-frame
+colls
 %3E%5Cd+)(?:
@@ -2625,25 +2625,22 @@
 (?P%3Ctx_c
-ompressed
+arrier
 %3E%5Cd+)(?:
@@ -2674,25 +2674,26 @@
 '(?P%3Ctx_
-multicast
+compressed
 %3E%5Cd+)(?:
@@ -3843,30 +3843,55 @@
 or s
-, v in stats.items():%0A
+ in fields:%0A                if s in stats:%0A    
     
@@ -3932,24 +3932,28 @@
             
+   
+ 
 metric_name 
@@ -3984,32 +3984,36 @@
                 
+    
 # Get Metric Val
@@ -4015,16 +4015,20 @@
 c Value%0A
+    
         
@@ -4130,17 +4130,32 @@
     
+    
 long(
-v),%0A
+stats%5Bs%5D),%0A    
     
@@ -4249,619 +4249,199 @@
     
-# Convert rx_bytes and tx_bytes%0A                if s == 'rx_bytes' or s == 'tx_bytes':%0A                    convertor = diamond.convertor.binary(value=metric_value,%0A                                                         unit='byte')%0A%0A                    for u in self.config%5B'byte_unit'%5D:%0A                        # Public Converted Metric%0A                        self.publish(metric_name.replace('bytes', u),%0A                                     convertor.get(unit=u), precision=2)%0A                else:%0A                    # Publish Metric Derivative%0A                    self.publish(metric_name, metric_value
+    # Publish Metric Derivative%0A                    self.publish(metric_name, metric_value)%0A                else:%0A                    self.log.warn('%7B0%7D is not a valid metric field'.format(s)
 )%0A%0A 

Fix NetworkCollector to report errors by default