[sysmon] Add duplex versions of IO metrics

BUG=chromium:716679
TEST=Run bin/run_tests chromite.scripts.sysmon

Change-Id: If16a060b12f729012c8593aa0fedee2e91aa9dda
Reviewed-on: https://chromium-review.googlesource.com/496627
Reviewed-by: Aviv Keshet <akeshet@chromium.org>
Tested-by: Allen Li <ayatane@chromium.org>
diff --git a/scripts/sysmon/net_metrics.py b/scripts/sysmon/net_metrics.py
index 1472a1d..3a8c3b3 100644
--- a/scripts/sysmon/net_metrics.py
+++ b/scripts/sysmon/net_metrics.py
@@ -8,6 +8,8 @@
 from __future__ import print_function
 from __future__ import unicode_literals
 
+import collections
+
 import psutil
 
 from chromite.lib import cros_logging as logging
@@ -43,6 +45,21 @@
     description='Total number of incoming '
     'packets that have been dropped.')
 
+_net_bytes_metric = ts_mon.CounterMetric(
+    'dev/net/bytes', start_time=_BOOT_TIME,
+    description='Number of bytes up/down on interface.',
+    units=ts_mon.MetricsDataUnits.BYTES)
+_net_packets_metric = ts_mon.CounterMetric(
+    'dev/net/packets', start_time=_BOOT_TIME,
+    description='Number of packets up/down on interface.',
+    units=ts_mon.MetricsDataUnits.BYTES)
+_net_errors_metric = ts_mon.CounterMetric(
+    'dev/net/errors', start_time=_BOOT_TIME,
+    description='Total number of errors up/down on interface.')
+_net_dropped_metric = ts_mon.CounterMetric(
+    'dev/net/dropped', start_time=_BOOT_TIME,
+    description='Total number of dropped packages up/down on interface.')
+
 _net_if_isup_metric = ts_mon.BooleanMetric(
     'dev/net/isup',
     description='Whether interface is up or down.')
@@ -60,9 +77,47 @@
 def collect_net_info():
   """Collect network metrics."""
   _collect_net_io_counters()
+  _collect_net_io_duplex_counters()
   _collect_net_if_stats()
 
 
+# Network IO metrics to collect
+_IOMetric = collections.namedtuple('_IOMetric', ['metric', 'up_counter_name',
+                                                 'down_counter_name'])
+
+_net_io_duplex_metrics = (
+  _IOMetric(metric=_net_bytes_metric,
+            up_counter_name='bytes_sent',
+            down_counter_name='bytes_recv'),
+  _IOMetric(metric=_net_packets_metric,
+            up_counter_name='packets_sent',
+            down_counter_name='packets_recv'),
+  _IOMetric(metric=_net_errors_metric,
+            up_counter_name='errout',
+            down_counter_name='errin'),
+  _IOMetric(metric=_net_drop_up_metric,
+            up_counter_name='dropout',
+            down_counter_name='dropin'),
+)
+
+
+def _collect_net_io_duplex_counters():
+  """Collect metrics for network IO duplex counters."""
+  for nic, counters in _net_io_iter():
+    fields = {'interface': nic}
+    for metric, up_counter_name, down_counter_name in _net_io_duplex_metrics:
+      try:
+        metric.set(getattr(counters, up_counter_name),
+                   fields=dict(direction='up', **fields))
+        metric.set(getattr(counters, down_counter_name),
+                   fields=dict(direction='down', **fields))
+      except ts_mon.MonitoringDecreasingValueError as ex:
+        # This normally shouldn't happen, but might if the network
+        # driver module is reloaded, so log an error and continue
+        # instead of raising an exception.
+        logger.warning(str(ex))
+
+
 _net_io_metrics = (
   (_net_up_metric, 'bytes_sent'),
   (_net_down_metric, 'bytes_recv'),