qca83xx_ethernet: re-implement using JSON files.

Read in JSON files produced by qcs83XXd instead of using
the Python bindings to the QCA SSDK.

Change-Id: I43c892a4b835d717813c2dc385e68a65e959d921
diff --git a/dm/host_integration_test.py b/dm/host_integration_test.py
index 288fabb..a969788 100644
--- a/dm/host_integration_test.py
+++ b/dm/host_integration_test.py
@@ -92,7 +92,7 @@
     self.InterfaceList = {
         '1': ethernet.EthernetInterfaceLinux26(ifname='eth0'),
         '2': qca83xx_ethernet.EthernetInterfaceQca83xx(
-            portnum=1, mac='00:00:00:00:00:00', ifname='lan0')
+            portnum=1, mac='00:00:00:00:00:00')
     }
 
 
@@ -104,31 +104,6 @@
     self.InterfaceList = {'1': brcmmoca2.BrcmMocaInterface(ifname='moca0')}
 
 
-class MockQca83xxPort(object):
-  """A virtual QCA83xx switch port."""
-
-  def __init__(self, port):
-    self.port = port
-
-  def CableDiag(self):
-    return [('normal', 1), ('normal', 1), ('normal', 1), ('normal', 1)]
-
-  def Duplex(self, duplex=None):
-    return 'half'
-
-  def Fdb(self):
-    return [{'PhysAddress': '00:01:02:33:00:01', 'Ports': ['qca83xx_1']}]
-
-  def IsLinkUp(self):
-    return False
-
-  def Speed(self, speed=None):
-    return 10
-
-  def Stats(self):
-    return {}
-
-
 class InternetGatewayDevice(tr.core.Exporter):
 
   def __init__(self):
@@ -159,8 +134,8 @@
     dnsmasq.DNSMASQLEASES[0] = 'testdata/host_integration/dnsmasq.leases'
     self.old_MOCAP = brcmmoca2.MOCAP
     brcmmoca2.MOCAP = 'testdata/host_integration/mocap'
-    self.old_QCAPORT = qca83xx_ethernet.QCAPORT
-    qca83xx_ethernet.QCAPORT = MockQca83xxPort
+    self.old_QCA83XX_JSON = qca83xx_ethernet.QCA83XX_JSON
+    qca83xx_ethernet.QCA83XX_JSON[0] = 'testdata/host_integration/qca83xx.json'
     self.old_WL_EXE = brcmwifi.WL_EXE
     brcmwifi.WL_EXE = 'testdata/host_integration/wl'
     self.dmroot = TestDeviceModelRoot()
@@ -172,7 +147,7 @@
     host.SYS_CLASS_NET_PATH = self.old_SYS_CLASS_NET_PATH
     dnsmasq.DNSMASQLEASES[0] = self.old_DNSMASQLEASES
     brcmmoca2.MOCAP = self.old_MOCAP
-    qca83xx_ethernet.QCAPORT = self.old_QCAPORT
+    qca83xx_ethernet.QCA83XX_JSON = self.old_QCA83XX_JSON
     brcmwifi.WL_EXE = self.old_WL_EXE
 
   def testHosts(self):
diff --git a/dm/qca83xx_ethernet.py b/dm/qca83xx_ethernet.py
index e151611..220fd1d 100755
--- a/dm/qca83xx_ethernet.py
+++ b/dm/qca83xx_ethernet.py
@@ -24,40 +24,70 @@
 
 __author__ = 'dgentry@google.com (Denton Gentry)'
 
-import sys
+import datetime
+import json
 import tr.basemodel
 import tr.cwmptypes
-
-
-QCAPORT = None
-try:
-  import qca83xx  # pylint:disable=g-import-not-at-top
-  QCAPORT = qca83xx.Port
-except ImportError:
-  sys.stderr.write('No qca83xx module; continuing for unit test support.')
+import tr.session
 
 
 ETHERNET = tr.basemodel.Device.Ethernet
+QCA83XX_JSON = ['/tmp/qca83xx.json']
 
 
 class EthernetInterfaceStatsQca83xx(ETHERNET.Interface.Stats):
   """tr181 Ethernet.Interface.{i}.Stats implementation for qca83xx."""
 
+  BroadcastPacketsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  BroadcastPacketsSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  BytesReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  BytesSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  DiscardPacketsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  DiscardPacketsSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  ErrorsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  ErrorsSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  MulticastPacketsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  MulticastPacketsSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  PacketsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  PacketsSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  UnicastPacketsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  UnicastPacketsSent = tr.cwmptypes.ReadOnlyUnsigned(0)
+  UnknownProtoPacketsReceived = tr.cwmptypes.ReadOnlyUnsigned(0)
+  X_CATAWAMPUS_ORG_DiscardFrameCnts = tr.cwmptypes.ReadOnlyUnsigned(0)
+
   def __init__(self, stats):
     super(EthernetInterfaceStatsQca83xx, self).__init__()
-    self.stats = stats
+    self.Unexport(['X_CATAWAMPUS-ORG_DiscardFrameCnts',
+                   'X_CATAWAMPUS-ORG_DiscardPacketsReceivedHipri'])
+    type(self).BytesReceived.Set(self, stats.get('BytesReceived', 0))
+    rx_unicast_pkts = stats.get('UnicastPacketsReceived', 0)
+    rx_multicast_pkts = stats.get('MulticastPacketsReceived', 0)
+    rx_broadcast_pkts = stats.get('BroadcastPacketsReceived', 0)
+    rx_pkts = rx_unicast_pkts + rx_multicast_pkts + rx_broadcast_pkts
+    type(self).PacketsReceived.Set(self, rx_pkts)
+    type(self).UnicastPacketsReceived.Set(self, rx_unicast_pkts)
+    type(self).MulticastPacketsReceived.Set(self, rx_multicast_pkts)
+    type(self).BroadcastPacketsReceived.Set(self, rx_broadcast_pkts)
+    type(self).ErrorsReceived.Set(self, stats.get('ErrorsReceived', 0))
 
-  def __getattr__(self, name):
-    return self.stats.get(name, 0)
+    type(self).BytesSent.Set(self, stats.get('BytesSent', 0))
+    tx_unicast_pkts = stats.get('UnicastPacketsSent', 0)
+    tx_multicast_pkts = stats.get('MulticastPacketsSent', 0)
+    tx_broadcast_pkts = stats.get('BroadcastPacketsSent', 0)
+    tx_pkts = tx_unicast_pkts + tx_multicast_pkts + tx_broadcast_pkts
+    type(self).PacketsSent.Set(self, tx_pkts)
+    type(self).UnicastPacketsSent.Set(self, tx_unicast_pkts)
+    type(self).MulticastPacketsSent.Set(self, tx_multicast_pkts)
+    type(self).BroadcastPacketsSent.Set(self, tx_broadcast_pkts)
+    type(self).ErrorsSent.Set(self, stats.get('ErrorsSent', 0))
 
 
 class EthernetInterfaceQca83xx(ETHERNET.Interface):
   """Handling for a QCA83xx switch port.
 
   Args:
-    portnum: the 0-based port number on the switch chip.
+    portnum: the 1-based port number on the switch chip.
     mac: the MAC address of this port. The QCA83xx doesn't know it.
-    ifname: the Linux netdev handling this switch port
     upstream: whether the port faces the WAN (unlikely).
   """
 
@@ -67,120 +97,124 @@
   Name = tr.cwmptypes.ReadOnlyString('')
   Upstream = tr.cwmptypes.ReadOnlyBool(False)
 
-  def __init__(self, portnum, mac, ifname, upstream=False):
+  def __init__(self, portnum, mac, upstream=False):
     super(EthernetInterfaceQca83xx, self).__init__()
     self._portnum = portnum
-    self._port = QCAPORT(portnum)
-    self._ifname = ifname
-    self.Unexport(['Alias', 'X_CATAWAMPUS-ORG_ActualBitRate',
-                   'X_CATAWAMPUS-ORG_ActualDuplexMode'])
+    self.Unexport(['Alias'])
     type(self).MACAddress.Set(self, mac)
-    type(self).Name.Set(self, 'qca83xx_' + str(portnum))
+    type(self).Name.Set(self, 'lan0:' + str(portnum))
     type(self).Upstream.Set(self, upstream)
     self.stats = {}
 
+  @tr.session.cache
+  def _GetJsonPortInfo(self):
+    """Read in information about this port from JSON.
+
+      The Ports section of the file looks like:
+      # cat /tmp/qca83xx.json
+      {
+        "Ports": [
+          {
+            "PortName": "lan0:1",
+            "LinkSpeed": 0,
+            "LinkDuplex": "Unknown",
+            "LinkStatus": "Down",
+            "LastChanged": 0,
+            "BytesReceived": 0,
+            "ErrorsReceived": 0,
+            "UnicastPacketsReceived": 0,
+            "MulticastPacketsReceived": 0,
+            "BroadcastPacketsReceived": 0,
+            "BytesSent": 0,
+            "ErrorsSent": 0,
+            "UnicastPacketsSent": 0,
+            "MulticastPacketsSent": 0,
+            "BroadcastPacketsSent": 0,
+            "CableStatus": "OK",
+            "CableLength": 0
+          },
+
+    Returns:
+        a dict of the contents of this port in the JSON file.
+    """
+    try:
+      js = json.load(open(QCA83XX_JSON[0]))
+    except (IOError, ValueError):
+      return {}
+    ports = js.get('Ports', [])
+    for port in ports:
+      if port.get('PortName', '') == self.Name:
+        return port
+    return {}
+
   @property
   def LastChange(self):
-    return tr.cwmpdate.format(0)
+    seconds_since_epoch = self._GetJsonPortInfo().get('LastChanged', 0)
+    dt = datetime.datetime.utcfromtimestamp(seconds_since_epoch)
+    return tr.cwmpdate.format(dt)
 
   @property
   def Status(self):
-    if self._port.IsLinkUp():
+    linkstatus = self._GetJsonPortInfo().get('LinkStatus', 'Error')
+    if linkstatus == 'Up':
       return 'Up'
-    for (cable_status, unused_cable_len) in self._port.CableDiag():
-      if cable_status == 'shorted':
-        return 'Error'
+    cablestatus = self._GetJsonPortInfo().get('CableStatus', 'OK')
+    if cablestatus == 'SH' or cablestatus == 'IV':
+      return 'Error'
     return 'Down'
 
   @property
   def Stats(self):
-    s = self._UpdateStats()
-    return EthernetInterfaceStatsQca83xx(s)
+    return EthernetInterfaceStatsQca83xx(self._GetJsonPortInfo())
 
-  def GetMaxBitRate(self):
-    return self._port.Speed()
+  @property
+  def MaxBitRate(self):
+    return self._GetJsonPortInfo().get('LinkSpeed', 0)
 
-  def SetMaxBitRate(self, val):
-    self._port.Speed(speed=int(val))
+  @property
+  def X_CATAWAMPUS_ORG_ActualBitRate(self):
+    return self._GetJsonPortInfo().get('LinkSpeed', 0)
 
-  MaxBitRate = property(GetMaxBitRate, SetMaxBitRate, None,
-                        'Device.Ethernet.Interface.MaxBitRate')
+  @property
+  def DuplexMode(self):
+    return self._GetJsonPortInfo().get('LinkDuplex', 'Half')
 
-  def GetDuplexMode(self):
-    return self._port.Duplex()
-
-  def SetDuplexMode(self, val):
-    self._port.Duplex(duplex=val)
-
-  DuplexMode = property(GetDuplexMode, SetDuplexMode, None,
-                        'Device.Ethernet.Interface.DuplexMode')
+  @property
+  def X_CATAWAMPUS_ORG_ActualDuplexMode(self):
+    return self._GetJsonPortInfo().get('LinkDuplex', 'Half')
 
   def GetAssociatedDevices(self):
     """Return a list of known clients of this interface.
 
+      The Fdb section of the JSON file looks like:
+      "Fdb": [
+        {
+          "PhysAddress": "00:01:02:03:04:05",
+          "PortList": [ "lan0:2" ]
+        },
+        {
+          "PhysAddress": "00:01:02:03:04:06",
+          "PortList": [ "lan0:3" ]
+        },
+
     Returns:
-      a list of dicts, where the dict contains:
-      1. a 'PhysAddress' key with the MAC address.
-      2. an 'IPv4Address' key with a list of IP addresses
-         for this mac known by ARP. This list may be empty.
+      a list of dicts, where the dict contains a
+      'PhysAddress' key with the MAC address.
     """
+    js = {}
     result = []
-    for entry in self._port.Fdb():
-      mac = entry['PhysAddress']
-      octets = mac.split(':')
-      b1 = int(octets[0], 16)
-      if not b1 & 0x01:
-        # only report unicast addresses, not multicast
-        result.append({'PhysAddress': mac})
+    try:
+      js = json.load(open(QCA83XX_JSON[0]))
+    except (IOError, ValueError):
+      return {}
+    fdb = js.get('Fdb', [])
+    for station in fdb:
+      macaddr = station.get('PhysAddress', '')
+      portlist = station.get('PortList', [])
+      if macaddr and self.Name in portlist:
+        octets = macaddr.split(':')
+        b1 = int(octets[0], 16)
+        if not b1 & 0x01:
+          # only report unicast addresses, not multicast
+          result.append({'PhysAddress': macaddr})
     return result
-
-  def _UpdateStats(self):
-    """Accumulate MIB counters from the hardware.
-
-    The QCA83xx clears its MIB counters on read, so we accumulate them
-    in software. The hardware has a large number of counters for various
-    events, which map to a somewhat smaller number of tr-181 Stats.
-
-    Returns:
-      a dict of statistics.
-    """
-    st = self.stats
-    hs = self._port.Stats()
-    self._UpdateStat(st, hs, 'BytesSent', 'TxBytes')
-    self._UpdateStat(st, hs, 'BytesReceived', 'RxGoodBytes')
-    self._UpdateStat(st, hs, 'PacketsSent', 'TxBroadcastPackets')
-    self._UpdateStat(st, hs, 'PacketsSent', 'TxMulticastPackets')
-    self._UpdateStat(st, hs, 'PacketsSent', 'TxUnicastPackets')
-    self._UpdateStat(st, hs, 'PacketsReceived', 'RxBroadcastPackets')
-    self._UpdateStat(st, hs, 'PacketsReceived', 'RxMulticastPackets')
-    self._UpdateStat(st, hs, 'PacketsReceived', 'RxUnicastPackets')
-    self._UpdateStat(st, hs, 'ErrorsSent', 'TxUnderRuns')
-    self._UpdateStat(st, hs, 'ErrorsSent', 'TxOverSizePackets')
-    self._UpdateStat(st, hs, 'ErrorsSent', 'TxLateCollisions')
-    self._UpdateStat(st, hs, 'ErrorsSent', 'TxExcessiveDeferrals')
-    self._UpdateStat(st, hs, 'ErrorsReceived', 'RxFcsErrors')
-    self._UpdateStat(st, hs, 'ErrorsReceived', 'RxAlignmentErrors')
-    self._UpdateStat(st, hs, 'ErrorsReceived', 'RxRuntPackets')
-    self._UpdateStat(st, hs, 'ErrorsReceived', 'RxFragments')
-    self._UpdateStat(st, hs, 'ErrorsReceived', 'RxTooLongPackets')
-    self._UpdateStat(st, hs, 'ErrorsReceived', 'RxOverFlows')
-    self._UpdateStat(st, hs, 'UnicastPacketsSent', 'TxUnicastPackets')
-    self._UpdateStat(st, hs, 'UnicastPacketsReceived', 'RxUnicastPackets')
-    self._UpdateStat(st, hs, 'MulticastPacketsSent', 'TxMulticastPackets')
-    self._UpdateStat(st, hs, 'MulticastPacketsReceived', 'RxMulticastPackets')
-    self._UpdateStat(st, hs, 'BroadcastPacketsSent', 'TxBroadcastPackets')
-    self._UpdateStat(st, hs, 'BroadcastPacketsReceived', 'RxBroadcastPackets')
-    return st
-
-  def _UpdateStat(self, swstats, hwstats, swname, hwname):
-    """Update accumulator from hardware counter.
-
-    Args:
-      swstats: a dict containing the accumulated values
-      hwstats: a dict of values read from hardware counters
-      swname: name of the tr-181 Stat to accumulate into
-      hwname: name of the hardware counter to accumulate from
-    """
-    s = swstats.get(swname, 0L)
-    h = long(hwstats.get(hwname, 0))
-    swstats[swname] = s + h
diff --git a/dm/qca83xx_ethernet_test.py b/dm/qca83xx_ethernet_test.py
index 30e98be..d87beeb 100644
--- a/dm/qca83xx_ethernet_test.py
+++ b/dm/qca83xx_ethernet_test.py
@@ -33,30 +33,27 @@
   """Tests for qca83xx_ethernet.py."""
 
   def setUp(self):
-    self.old_QCAPORT = qca83xx_ethernet.QCAPORT
+    self.old_QCA83XX_JSON = qca83xx_ethernet.QCA83XX_JSON
+    qca83xx_ethernet.QCA83XX_JSON[0] = 'testdata/qca83xx_ethernet/qca83xx.json'
     self.mac = 'f8:8f:ca:ff:ff:02'
     MOCKPORTSETFIELDS.clear()
 
   def tearDown(self):
-    qca83xx_ethernet.QCAPORT = self.old_QCAPORT
+    qca83xx_ethernet.QCA83XX_JSON = self.old_QCA83XX_JSON
     self.eth = None
 
   def testValidateExports(self):
-    qca83xx_ethernet.QCAPORT = MockPortStats
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
     tr.handle.ValidateExports(eth)
 
   def testStats(self):
-    qca83xx_ethernet.QCAPORT = MockPortStats
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
     self.assertEqual(eth.Stats.BytesSent, 17000000000000000L)
     self.assertEqual(eth.Stats.BytesReceived, 36000000000000000L)
     self.assertEqual(eth.Stats.PacketsSent, 15 + 19 + 25)
     self.assertEqual(eth.Stats.PacketsReceived, 31 + 34 + 39)
-    self.assertEqual(eth.Stats.ErrorsSent, 24 + 14 + 13 + 23)
-    self.assertEqual(eth.Stats.ErrorsReceived, 37 + 41 + 30 + 40 + 29 + 35)
+    self.assertEqual(eth.Stats.ErrorsSent, 98)
+    self.assertEqual(eth.Stats.ErrorsReceived, 99)
     self.assertEqual(eth.Stats.UnicastPacketsSent, 25)
     self.assertEqual(eth.Stats.UnicastPacketsReceived, 31)
     self.assertEqual(eth.Stats.MulticastPacketsSent, 15)
@@ -68,46 +65,59 @@
     self.assertEqual(eth.Stats.UnknownProtoPacketsReceived, 0)
 
   def testFields(self):
-    qca83xx_ethernet.QCAPORT = MockPortStats
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=2, mac=self.mac)
     self.assertEqual(eth.MaxBitRate, 100)
-    self.assertEqual(eth.DuplexMode, 'full')
+    self.assertEqual(eth.DuplexMode, 'Full')
     self.assertEqual(eth.Status, 'Up')
     self.assertEqual(eth.MACAddress, self.mac)
     self.assertFalse(eth.Upstream)
 
   def testStatus(self):
-    qca83xx_ethernet.QCAPORT = MockPortLinkFault
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=3, mac=self.mac)
     self.assertEqual(eth.Status, 'Error')
-    qca83xx_ethernet.QCAPORT = MockPortLinkDown
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=4, mac=self.mac)
     self.assertEqual(eth.Status, 'Down')
 
-  def testWrite(self):
-    qca83xx_ethernet.QCAPORT = MockPortLinkDown
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
-    eth.DuplexMode = 'full'
-    self.assertTrue('duplex' in MOCKPORTSETFIELDS)
-    self.assertEqual(MOCKPORTSETFIELDS['duplex'], 'full')
-    eth.MaxBitRate = 1000
-    self.assertTrue('speed' in MOCKPORTSETFIELDS)
-    self.assertEqual(MOCKPORTSETFIELDS['speed'], 1000)
-
   def testGetAssociatedDevices(self):
-    qca83xx_ethernet.QCAPORT = MockPortLinkDown
-    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac,
-                                                    ifname='foo0')
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
     ac = eth.GetAssociatedDevices()
     self.assertEqual(len(ac), 3)
     self.assertEqual(ac[0]['PhysAddress'], 'f8:8f:ca:00:00:01')
     self.assertEqual(ac[1]['PhysAddress'], 'f8:8f:ca:00:00:02')
     self.assertEqual(ac[2]['PhysAddress'], 'f8:8f:ca:00:00:03')
 
+  def testEmptyFile(self):
+    qca83xx_ethernet.QCA83XX_JSON[0] = 'testdata/qca83xx_ethernet/empty'
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
+    ac = eth.GetAssociatedDevices()
+    self.assertEqual(len(ac), 0)
+    self.assertEqual(eth.Stats.BytesSent, 0)
+    self.assertEqual(eth.Stats.BytesReceived, 0)
+
+  def testEmptyJson(self):
+    qca83xx_ethernet.QCA83XX_JSON[0] = 'testdata/qca83xx_ethernet/empty.json'
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
+    ac = eth.GetAssociatedDevices()
+    self.assertEqual(len(ac), 0)
+    self.assertEqual(eth.Stats.BytesSent, 0)
+    self.assertEqual(eth.Stats.BytesReceived, 0)
+
+  def testCorruptJson(self):
+    qca83xx_ethernet.QCA83XX_JSON[0] = 'testdata/qca83xx_ethernet/empty.json'
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
+    ac = eth.GetAssociatedDevices()
+    self.assertEqual(len(ac), 0)
+    self.assertEqual(eth.Stats.BytesSent, 0)
+    self.assertEqual(eth.Stats.BytesReceived, 0)
+
+  def testMissingFile(self):
+    qca83xx_ethernet.QCA83XX_JSON[0] = '/nosuchfile'
+    eth = qca83xx_ethernet.EthernetInterfaceQca83xx(portnum=1, mac=self.mac)
+    ac = eth.GetAssociatedDevices()
+    self.assertEqual(len(ac), 0)
+    self.assertEqual(eth.Stats.BytesSent, 0)
+    self.assertEqual(eth.Stats.BytesReceived, 0)
+
 
 class MockPortLinkDown(object):
 
diff --git a/dm/testdata/host_integration/qca83xx.json b/dm/testdata/host_integration/qca83xx.json
new file mode 100644
index 0000000..cfbcacd
--- /dev/null
+++ b/dm/testdata/host_integration/qca83xx.json
@@ -0,0 +1,30 @@
+{
+  "Ports": [
+    {
+      "PortName": "lan0:1",
+      "LinkSpeed": 1000,
+      "LinkDuplex": "Full",
+      "LinkStatus": "Up",
+      "LastChanged": 1483070000,
+      "BytesReceived": 1,
+      "ErrorsReceived": 2,
+      "UnicastPacketsReceived": 3,
+      "MulticastPacketsReceived": 4,
+      "BroadcastPacketsReceived": 5,
+      "BytesSent": 6,
+      "ErrorsSent": 7,
+      "UnicastPacketsSent": 8,
+      "MulticastPacketsSent": 9,
+      "BroadcastPacketsSent": 10
+    },
+    {}
+  ],
+  "Fdb": [
+    {
+      "PhysAddress": "00:01:02:33:00:01",
+      "PortList": [ "lan0:1" ]
+    },
+    {}
+  ],
+  "dummy": {}
+}
diff --git a/dm/testdata/qca83xx/proc_net_arp b/dm/testdata/qca83xx/proc_net_arp
deleted file mode 100644
index e6fa6b1..0000000
--- a/dm/testdata/qca83xx/proc_net_arp
+++ /dev/null
@@ -1,4 +0,0 @@
-IP address       HW type     Flags       HW address            Mask     Device
-192.168.1.1      0x1         0x2         f8:8f:ca:00:00:01     *        foo0
-192.168.1.2      0x1         0x2         f8:8f:ca:00:00:02     *        foo0
-192.168.1.3      0x1         0x2         f8:8f:ca:00:00:03     *        foo0
diff --git a/dm/testdata/qca83xx_ethernet/corrupt.json b/dm/testdata/qca83xx_ethernet/corrupt.json
new file mode 100644
index 0000000..0355947
--- /dev/null
+++ b/dm/testdata/qca83xx_ethernet/corrupt.json
@@ -0,0 +1 @@
+Not a valid JSON file.
diff --git a/dm/testdata/qca83xx_ethernet/empty b/dm/testdata/qca83xx_ethernet/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dm/testdata/qca83xx_ethernet/empty
diff --git a/dm/testdata/qca83xx_ethernet/empty.json b/dm/testdata/qca83xx_ethernet/empty.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dm/testdata/qca83xx_ethernet/empty.json
@@ -0,0 +1 @@
+{}
diff --git a/dm/testdata/qca83xx_ethernet/qca83xx.json b/dm/testdata/qca83xx_ethernet/qca83xx.json
new file mode 100644
index 0000000..196750a
--- /dev/null
+++ b/dm/testdata/qca83xx_ethernet/qca83xx.json
@@ -0,0 +1,105 @@
+{
+  "Ports": [
+    {
+      "PortName": "lan0:1",
+      "LinkSpeed": 1000,
+      "LinkDuplex": "Full",
+      "LinkStatus": "Up",
+      "LastChanged": 1483074480,
+      "BytesReceived": 36000000000000000,
+      "ErrorsReceived": 99,
+      "UnicastPacketsReceived": 31,
+      "MulticastPacketsReceived": 39,
+      "BroadcastPacketsReceived": 34,
+      "BytesSent": 17000000000000000,
+      "ErrorsSent": 98,
+      "UnicastPacketsSent": 25,
+      "MulticastPacketsSent": 15,
+      "BroadcastPacketsSent": 19,
+      "CableStatus": "OK",
+      "CableLength": 13
+    },
+    {
+      "PortName": "lan0:2",
+      "LinkSpeed": 100,
+      "LinkDuplex": "Full",
+      "LinkStatus": "Up",
+      "LastChanged": 0,
+      "BytesReceived": 0,
+      "ErrorsReceived": 0,
+      "UnicastPacketsReceived": 0,
+      "MulticastPacketsReceived": 0,
+      "BroadcastPacketsReceived": 0,
+      "BytesSent": 0,
+      "ErrorsSent": 0,
+      "UnicastPacketsSent": 0,
+      "MulticastPacketsSent": 0,
+      "BroadcastPacketsSent": 0,
+      "CableStatus": "OK",
+      "CableLength": 7
+    },
+    {
+      "PortName": "lan0:3",
+      "LinkSpeed": 0,
+      "LinkDuplex": "Full",
+      "LinkStatus": "Down",
+      "LastChanged": 0,
+      "BytesReceived": 0,
+      "ErrorsReceived": 0,
+      "UnicastPacketsReceived": 0,
+      "MulticastPacketsReceived": 0,
+      "BroadcastPacketsReceived": 0,
+      "BytesSent": 0,
+      "ErrorsSent": 0,
+      "UnicastPacketsSent": 0,
+      "MulticastPacketsSent": 0,
+      "BroadcastPacketsSent": 0,
+      "CableStatus": "SH",
+      "CableLength": 7
+    },
+    {
+      "PortName": "lan0:4",
+      "LinkSpeed": 10,
+      "LinkDuplex": "Full",
+      "LinkStatus": "Down",
+      "LastChanged": 0,
+      "BytesReceived": 0,
+      "ErrorsReceived": 0,
+      "UnicastPacketsReceived": 0,
+      "MulticastPacketsReceived": 0,
+      "BroadcastPacketsReceived": 0,
+      "BytesSent": 0,
+      "ErrorsSent": 0,
+      "UnicastPacketsSent": 0,
+      "MulticastPacketsSent": 0,
+      "BroadcastPacketsSent": 0,
+      "CableStatus": "OK",
+      "CableLength": 0
+    },
+    {}
+  ],
+  "Fdb": [
+    {
+      "PhysAddress": "f8:8f:ca:00:00:01",
+      "PortList": [ "lan0:1" ]
+    },
+    {
+      "PhysAddress": "f8:8f:ca:00:00:02",
+      "PortList": [ "lan0:1" ]
+    },
+    {
+      "PhysAddress": "f8:8f:ca:00:00:03",
+      "PortList": [ "lan0:1" ]
+    },
+    {
+      "PhysAddress": "b0:34:95:05:da:9c",
+      "PortList": [ "lan0:4" ]
+    },
+    {
+      "PhysAddress": "f2:00:00:00:00:00",
+      "PortList": [ ]
+    },
+    {}
+  ],
+  "dummy": {}
+}
diff --git a/platform/gfmedia/device.py b/platform/gfmedia/device.py
index 2c5a441..8f8b633 100755
--- a/platform/gfmedia/device.py
+++ b/platform/gfmedia/device.py
@@ -358,7 +358,7 @@
     if QCASWITCHPORT is not None:
       mac = PYNETIFCONF('lan0').get_mac()
       for port in range(1, 5):
-        q = QCASWITCHPORT(portnum=port, mac=mac, ifname='lan0')
+        q = QCASWITCHPORT(portnum=port, mac=mac)
         self.InterfaceList[2 + port] = q
 
     if _DoesInterfaceExist('br0'):