blob: 8f8b63339425ebaad3b5cf0c82b04a3deac489f7 [file] [log] [blame]
# Copyright 2011 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.
# TR-069 has mandatory attribute names that don't comply with policy
# pylint:disable=invalid-name
"""tr-181 Device implementations for supported platforms."""
__author__ = ' (Denton Gentry)'
import fcntl
import glob
import os
import subprocess
import traceback
import google3
import dm.binwifi
import dm.brcmmoca
import dm.brcmmoca2
import dm.brcmwifi
import dm.captive_portal
import dm.device_info
import dm.dns
import dm.dnsmasq
import dm.ethernet
import dm.igd_time
import dm.ipinterface
import dm.miniupnp
import dm.nat
import dm.periodic_statistics
import dm.temperature
import dm.traceroute
import platform_config
import pynetlinux
import tornado.ioloop
import tr.basemodel
import tr.core
import tr.handle
import tr.helpers
import tr.session
import stbservice
import qca83xx # pylint:disable=g-import-not-at-top
if qca83xx.IsQCA8337():
import dm.qca83xx_ethernet # pylint:disable=g-import-not-at-top
QCASWITCHPORT = dm.qca83xx_ethernet.EthernetInterfaceQca83xx
except ImportError:
# Not an error, several platforms don't compile in qca83xx.
except qca83xx.SdkError:
print 'Continuing catawampus startup'
PYNETIFCONF = pynetlinux.ifconfig.Interface
# tr-69 error codes
# Unit tests can override these with fake data
ACTIVEWAN = 'activewan'
AUXTEMP = '/tmp/gpio/aux1_temperature'
CONFIGDIR = '/fiber/config/tr69'
GINSTALL = 'ginstall'
HNVRAM = 'hnvram'
ISNETWORKBOX = 'is-network-box'
LEDSTATUS = '/tmp/gpio/ledstate'
NAND_MB = '/proc/sys/dev/repartition/nand_size_mb'
PROC_CPUINFO = '/proc/cpuinfo'
REBOOT = 'tr69_reboot'
REPOMANIFEST = '/etc/manifest'
VERSIONFILE = '/etc/version'
def _DoesInterfaceExist(ifcname):
return True
except IOError:
return False
class PlatformConfig(platform_config.PlatformConfigMeta):
"""PlatformConfig for GFMedia devices."""
def __init__(self, ioloop=None):
super(PlatformConfig, self).__init__()
def ConfigDir(self):
def DownloadDir(self):
if os.path.isdir('/var/media/swimage'):
return '/var/media/swimage'
elif os.path.isdir('/user/swimage'):
return '/user/swimage'
elif os.path.isdir('/tmp/swimage'):
return '/tmp/swimage'
return '/tmp'
class DeviceId(dm.device_info.DeviceIdMeta):
"""Fetch the DeviceInfo parameters from NVRAM."""
AdditionalHardwareVersion = tr.cwmptypes.ReadOnlyString('')
AdditionalSoftwareVersion = tr.cwmptypes.ReadOnlyString('')
Description = tr.cwmptypes.ReadOnlyString(
'Set top box for Google Fiber network')
HardwareVersion = tr.cwmptypes.ReadOnlyString('')
Manufacturer = tr.cwmptypes.ReadOnlyString('Google Fiber')
ManufacturerOUI = tr.cwmptypes.ReadOnlyString('F88FCA')
ModelName = tr.cwmptypes.ReadOnlyString('')
ModemFirmwareVersion = tr.cwmptypes.ReadOnlyString('0')
ProductClass = tr.cwmptypes.ReadOnlyString('0')
SerialNumber = tr.cwmptypes.ReadOnlyString('')
SoftwareVersion = tr.cwmptypes.ReadOnlyString('')
def __init__(self):
super(DeviceId, self).__init__()
addlhwvers = self._GetNvramParam('GPN', default='')
type(self).AdditionalHardwareVersion.Set(self, addlhwvers)
addlswvers = self._GetOneLine(REPOMANIFEST, '')
type(self).AdditionalSoftwareVersion.Set(self, addlswvers)
type(self).HardwareVersion.Set(self, self._HardwareVersion())
modelname = self._GetNvramParam('PLATFORM_NAME', default='UnknownModel')
type(self).ModelName.Set(self, modelname)
product_class = self._GetNvramParam('PLATFORM_NAME', default='UnknownModel')
type(self).ProductClass.Set(self, product_class)
type(self).SerialNumber.Set(self, self._SerialNumber())
swvers = self._GetOneLine(VERSIONFILE, '0')
type(self).SoftwareVersion.Set(self, swvers)
def _GetOneLine(self, filename, default):
with open(filename, 'r') as f:
return f.readline().strip()
except IOError:
return default
def _GetNvramParam(self, param, default=''):
"""Return a parameter from NVRAM, like the serial number.
param: string name of the parameter to fetch. This must match the
predefined names supported by /bin/hnvram
default: value to return if the parameter is not present in NVRAM.
A string value of the contents.
cmd = [HNVRAM, '-r', param]
devnull = open('/dev/null', 'w')
hnvram = subprocess.Popen(cmd, stdin=devnull, stderr=devnull,
out, _ = hnvram.communicate()
if hnvram.returncode != 0:
# Treat failure to run hnvram same as not having the field populated
out = ''
except OSError:
out = ''
outlist = out.strip().split('=')
# HNVRAM does not distinguish between "value not present" and
# "value present, and is empty." Treat empty values as invalid.
if len(outlist) > 1 and outlist[1].strip():
return outlist[1].strip()
return default
def _SerialNumber(self):
serial = self._GetNvramParam('1ST_SERIAL_NUMBER', default=None)
if serial is None:
serial = self._GetNvramParam('SERIAL_NO', default='000000000000')
return serial
def _HardwareVersion(self):
"""Return NVRAM HW_VER, inferring one if not present."""
hw_ver = self._GetNvramParam('HW_VER', default=None)
if hw_ver:
return hw_ver
# initial builds with no HW_VER; infer a version.
cpu = open(PROC_CPUINFO).read()
if cpu.find('BCM7425B2') > 0:
# B2 chip with 4 Gig MLC flash == rev1. 1 Gig SLC flash == rev2.
siz = int(open(NAND_MB).read())
except (OSError, IOError):
return '?'
if siz == 4096:
return '1'
if siz == 1024:
return '2'
return '0'
class Installer(
"""Installer class used by tr/"""
def __init__(self, url, ioloop=None):
self.url = url
self._install_cb = None
self._ioloop = ioloop or tornado.ioloop.IOLoop.instance()
def _call_callback(self, faultcode, faultstring):
if self._install_cb:
self._install_cb(faultcode, faultstring, must_reboot=True)
def Install(self, file_type, target_filename, callback):
"""Install self.url to system, then call callback."""
print 'Installing: %r %r' % (file_type, target_filename)
ftype = file_type.split()
if ftype and ftype[0] != '1':
'Unsupported file_type {0}'.format(ftype[0]))
return False
self._install_cb = callback
cmd = [GINSTALL, '--once', '--tar=%s' % self.url, '--partition=other']
self._ginstall = subprocess.Popen(cmd, stdout=subprocess.PIPE)
except (OSError, subprocess.CalledProcessError):
self._call_callback(INTERNAL_ERROR, 'Unable to start installer process')
print 'Failed to start ginstall: %s' % str(cmd)
return False
fd = self._ginstall.stdout.fileno()
fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
self._ioloop.add_handler(fd, self.on_stdout, self._ioloop.READ)
return True
def Reboot(self):
cmd = [REBOOT]
def on_stdout(self, fd, unused_events):
"""Called whenever the ginstall process prints to stdout."""
# drain the pipe
inp = ''
inp =, 4096)
except OSError: # returns EWOULDBLOCK
if inp and inp.strip() != '.':
print 'ginstall: %s' % inp.strip()
if self._ginstall.poll() >= 0:
if self._ginstall.returncode == 0:
self._call_callback(0, '')
print 'ginstall: exit code %d' % self._ginstall.poll()
self._call_callback(INTERNAL_ERROR, 'Unable to install image.')
class Services(tr.basemodel.Device.Services):
"""Implements tr-181 Device.Services."""
def __init__(self):
super(Services, self).__init__()
self.StorageServices =
self.STBServiceList = {'1': stbservice.STBService()}
def STBServiceNumberOfEntries(self):
return len(self.STBServiceList)
def _AddStorageDevices(self):
num = 0
for drive in ['sda', 'sdb', 'sdc', 'sdd', 'sde', 'sdf']:
if os.stat('/sys/block/' + drive):
phys =, 'SATA/300')
self.StorageServices.PhysicalMediumList[str(num)] = phys
num += 1
except OSError:
num = 0
for i in range(32):
ubiname = 'ubi' + str(i)
if os.stat('/sys/class/ubi/' + ubiname):
ubi =
self.StorageServices.X_CATAWAMPUS_ORG_FlashMediaList[str(num)] = ubi
num += 1
except OSError:
def activewan(ifname):
"""Returns 'Down' if ifname is not the active WAN port."""
out = tr.helpers.Activewan(ACTIVEWAN)
if not out or out == ifname:
return ''
return 'Down'
class Ethernet(tr.basemodel.Device.Ethernet):
"""Implementation of tr-181 Device.Ethernet for GFMedia platforms."""
InterfaceNumberOfEntries = tr.cwmptypes.NumberOf('InterfaceList')
LinkNumberOfEntries = tr.cwmptypes.NumberOf('LinkList')
RMONStatsNumberOfEntries = tr.cwmptypes.NumberOf('RMONStatsList')
VLANTerminationNumberOfEntries = tr.cwmptypes.NumberOf('VLANTerminationList')
def __init__(self):
super(Ethernet, self).__init__()
self.InterfaceList = {}
self.LinkList = {}
self.RMONStatsList = {}
self.VLANTerminationList = {}
has_wan_port = False
if _DoesInterfaceExist('eth0'):
qprefix = '/sys/kernel/debug/bcmgenet/eth0/bcmgenet_discard_cnt_q'
qglob = glob.glob(qprefix + '*')
self.InterfaceList[1] = dm.ethernet.EthernetInterfaceLinux26(
qfiles=(qprefix + '%d') if qglob else None,
hipriq=1 if qglob else 0)
elif _DoesInterfaceExist('wan0'):
has_wan_port = True
self.InterfaceList[1] = dm.ethernet.EthernetInterfaceLinux26(
ifname='wan0', qfiles=None, numq=0, hipriq=0,
status_fcn=lambda: activewan('wan0'))
if _DoesInterfaceExist('wan0.2'):
has_wan_port = True
self.InterfaceList[2] = dm.ethernet.EthernetInterfaceLinux26(
ifname='wan0.2', qfiles=None, numq=0, hipriq=0,
status_fcn=lambda: activewan('wan0.2'))
if QCASWITCHPORT is not None:
mac = PYNETIFCONF('lan0').get_mac()
for port in range(1, 5):
q = QCASWITCHPORT(portnum=port, mac=mac)
self.InterfaceList[2 + port] = q
if _DoesInterfaceExist('br0'):
e = dm.ethernet.EthernetInterfaceLinux26(ifname='br0')
if has_wan_port:
# though all platforms have a br0 interface, it is used
# very differently on Network Boxes versus not. On
# Network Box it is the LAN port, and ACS should assign
# an internal address to it. On non-Network Box, it is
# the uplink. We give them different numbers to avoid
# accidentally mistaking one use for the other.
self.InterfaceList[254] = e
# For a transition period, support both 254 and 256
self.InterfaceList[256] = e
self.InterfaceList[256] = e
if _DoesInterfaceExist('lan0'):
e = dm.ethernet.EthernetInterfaceLinux26(ifname='lan0')
self.InterfaceList[255] = e
# Do not use idx 254, it is used for br0 above if has_wan_port
class Moca(tr.basemodel.Device.MoCA):
"""Implementation of tr-181 Device.MoCA for GFMedia platforms."""
def __init__(self):
super(Moca, self).__init__()
ifname = 'moca0' if _DoesInterfaceExist('moca0') else 'eth1'
if os.path.exists('/sys/kernel/debug/bcmgenet'):
qfiles = (
'/sys/kernel/debug/bcmgenet/%s/bcmgenet_discard_cnt_q%%d' % ifname)
numq = 17
hipriq = 1
qfiles = None
numq = hipriq = 0
self.InterfaceList = {}
if dm.brcmmoca2.IsMoca2_0():
self.InterfaceList['1'] = dm.brcmmoca2.BrcmMocaInterface(
ifname=ifname, qfiles=qfiles, numq=numq, hipriq=hipriq)
elif dm.brcmmoca.IsMoca1_1():
self.InterfaceList['1'] = dm.brcmmoca.BrcmMocaInterface(
ifname=ifname, qfiles=qfiles, numq=numq, hipriq=hipriq)
def InterfaceNumberOfEntries(self):
return len(self.InterfaceList)
class FanReadGpio(
"""Implementation of Fan object, reading rev/sec from a file."""
Name = tr.cwmptypes.ReadOnlyString('')
def __init__(self, name='Fan', speed_filename='/tmp/gpio/fanspeed',
super(FanReadGpio, self).__init__()
type(self).Name.Set(self, name)
self._speed_filename = speed_filename
self._percent_filename = percent_filename
def RPM(self):
f = open(self._speed_filename, 'r')
except IOError as e:
print 'Fan speed file %r: %s' % (self._speed_filename, e)
return -1
rps = int(
return rps * 60
except ValueError as e:
print 'FanReadGpio RPM %r: %s' % (self._speed_filename, e)
return -1
def DesiredPercentage(self):
f = open(self._percent_filename, 'r')
except IOError as e:
print 'Fan percent file %r: %s' % (self._percent_filename, e)
return -1
return int(
except ValueError as e:
print 'FanReadGpio DesiredPercentage %r: %s' % (self._percent_filename, e)
return -1
class IP(tr.basemodel.Device.IP):
"""tr-181 Device.IP implementation for Google Fiber media platforms."""
# Enable fields are supposed to be writeable, but we don't support that.
IPv4Capable = tr.cwmptypes.ReadOnlyBool(True)
IPv4Enable = tr.cwmptypes.ReadOnlyBool(True)
IPv4Status = tr.cwmptypes.ReadOnlyString('Enabled')
IPv6Capable = tr.cwmptypes.ReadOnlyBool(True)
IPv6Enable = tr.cwmptypes.ReadOnlyBool(True)
IPv6Status = tr.cwmptypes.ReadOnlyString('Enabled')
def __init__(self):
super(IP, self).__init__()
self.InterfaceList = {}
if _DoesInterfaceExist('br0'):
self.InterfaceList[256] = dm.ipinterface.IPInterfaceLinux26(
ifname='br0', lowerlayers='Device.Ethernet.Interface.256')
# Maintain numbering consistency between GFHD100 and GFRG200.
# The LAN interface is first, then the MoCA interface, then the WAN
# interfaces (if any) and then wifi (if any).
if _DoesInterfaceExist('lan0'):
self.InterfaceList[1] = dm.ipinterface.IPInterfaceLinux26(
ifname='lan0', lowerlayers='Device.Ethernet.Interface.255')
elif _DoesInterfaceExist('eth0'):
self.InterfaceList[1] = dm.ipinterface.IPInterfaceLinux26(
ifname='eth0', lowerlayers='Device.Ethernet.Interface.1')
if _DoesInterfaceExist('moca0'):
self.InterfaceList[2] = dm.ipinterface.IPInterfaceLinux26(
ifname='moca0', lowerlayers='Device.MoCA.Interface.1')
elif _DoesInterfaceExist('eth1'):
self.InterfaceList[2] = dm.ipinterface.IPInterfaceLinux26(
ifname='eth1', lowerlayers='Device.MoCA.Interface.1')
if _DoesInterfaceExist('wan0'):
self.InterfaceList[4] = dm.ipinterface.IPInterfaceLinux26(
ifname='wan0', lowerlayers='Device.Ethernet.Interface.1')
if _DoesInterfaceExist('wan0.2'):
self.InterfaceList[5] = dm.ipinterface.IPInterfaceLinux26(
ifname='wan0.2', lowerlayers='Device.Ethernet.Interface.2')
if _DoesInterfaceExist('wlan0'):
self.InterfaceList[6] = dm.ipinterface.IPInterfaceLinux26(
ifname='wlan0', lowerlayers='')
if _DoesInterfaceExist('wlan1'):
self.InterfaceList[7] = dm.ipinterface.IPInterfaceLinux26(
ifname='wlan1', lowerlayers='')
if _DoesInterfaceExist('eth2'):
self.InterfaceList[8] = dm.ipinterface.IPInterfaceLinux26(
ifname='eth2', lowerlayers='')
# The ACS treats Device.IP.Interface.9. as the interface for the captive
# portal / provisioning network. It will assign an IP address for it based
# on, but (hopefully!) not overlapping with, the configured primary IP
# address range.
# Nothing else will assign an IP address to the captive portal network, so
# this needs to be kept up to date if the on-device network design changes.
if _DoesInterfaceExist('br1'):
self.InterfaceList[9] = dm.ipinterface.IPInterfaceLinux26(
ifname='br1', lowerlayers='')
if _DoesInterfaceExist('wlan0_portal'):
self.InterfaceList[10] = dm.ipinterface.IPInterfaceLinux26(
ifname='wlan0_portal', lowerlayers='')
if _DoesInterfaceExist('wlan1_portal'):
self.InterfaceList[11] = dm.ipinterface.IPInterfaceLinux26(
ifname='wlan1_portal', lowerlayers='')
if _DoesInterfaceExist('wcli0'):
self.InterfaceList[12] = dm.ipinterface.IPInterfaceLinux26(
ifname='wcli0', lowerlayers='')
if _DoesInterfaceExist('wcli1'):
self.InterfaceList[13] = dm.ipinterface.IPInterfaceLinux26(
ifname='wcli1', lowerlayers='')
self.ActivePortList = {}
def InterfaceNumberOfEntries(self):
return len(self.InterfaceList)
def ActivePortNumberOfEntries(self):
return len(self.ActivePortList)
class Device(tr.basemodel.Device):
"""tr-181 Device implementation for Google Fiber media platforms."""
RootDataModelVersion = tr.cwmptypes.ReadOnlyString('2.6')
InterfaceStackNumberOfEntries = tr.cwmptypes.NumberOf('InterfaceStackList')
def __init__(self, device_id, periodic_stats, dmroot):
super(Device, self).__init__()
'ATM', 'Bridging', 'BulkData',
'DHCPv6', 'DLNA', 'DSL', 'DSLite', 'FaultMgmt',
'ETSIM2M', 'FAP', 'Firewall', 'GatewayInfo', 'Ghn', 'HPNA',
'HomePlug', 'IEEE8021x', 'IPsec', 'IPv6rd', 'LANConfigSecurity',
'NeighborDiscovery', 'Optical', 'PPP', 'PTM', 'QoS',
'RouterAdvertisement', 'Routing', 'Security',
'SelfTestDiagnostics', 'SoftwareModules',
'SmartCardReaders', 'Time',
'UPA', 'USB', 'UserInterface', 'Users', 'WiFi'])
self.DeviceInfo = dm.device_info.DeviceInfo181Linux26(device_id)
led = dm.device_info.LedStatusReadFromFile('LED', LEDSTATUS)
self.DHCPv4 = dm.dnsmasq.DHCPv4(dmroot=dmroot)
self.DNS = dm.dns.DNS()
self.Ethernet = Ethernet()
self.IP = IP()
self.ManagementServer = tr.core.TODO() # higher level code splices this in
self.MoCA = Moca()
self.NAT = dm.nat.NAT(dmroot=dmroot)
self.Services = Services()
self.InterfaceStackList = {}
self.PeriodicStatistics = periodic_stats
self.UPnP = dm.miniupnp.UPnP()
self.CaptivePortal = dm.captive_portal.CaptivePortal()
ts = self.DeviceInfo.TemperatureStatus
if os.path.exists('/tmp/gpio/fanspeed'):
# Add platform temperature sensors.
# GFHD100 & GFMS100 both monitor CPU temperature.
# GFMS100 also monitors hard drive temperature.
ts.AddSensor(name='CPU temperature',
if os.path.exists(AUXTEMP):
ts.AddSensor(name='AUX temperature',
for drive in ['sda', 'sdb', 'sdc', 'sdd', 'sde', 'sdf']:
if os.stat('/sys/block/' + drive):
ts.AddSensor(name='Hard drive temperature ' + drive,
except OSError:
# Add platform host entries.
# this is just a lookup table. It is harmless to have extra interfaces,
# like Wifi interfaces on GFMS100 (which has no wifi).
iflookup = {
'eth1.0': 'Device.MoCA.Interface.1',
'moca0.0': 'Device.MoCA.Interface.1',
self.Hosts =
iflookup=iflookup, bridgename='br0', dmroot=tr.handle.Handle(dmroot))
class Radio(object):
"""Holds radio-level properties shared by interfaces on that radio."""
Channel = tr.cwmptypes.TriggerInt()
def __init__(self):
self.InterfaceList = set()
def Triggered(self):
print self.InterfaceList
for interface in self.InterfaceList:
class LANDevice(tr.basemodel.InternetGatewayDevice.LANDevice):
"""tr-98 InternetGatewayDevice for Google Fiber media platforms."""
LANWLANConfigurationNumberOfEntries = tr.cwmptypes.NumberOf(
LANEthernetInterfaceNumberOfEntries = tr.cwmptypes.NumberOf(
LANUSBInterfaceNumberOfEntries = tr.cwmptypes.NumberOf(
def __init__(self, if_suffix, bridge, radio_list):
super(LANDevice, self).__init__()
self.Unexport(objects=['Hosts', 'LANHostConfigManagement'])
self.LANEthernetInterfaceConfigList = {}
self.LANUSBInterfaceConfigList = {}
self.WLANConfigurationList = {}
if _DoesInterfaceExist('eth2' + if_suffix):
wifi = dm.brcmwifi.BrcmWifiWlanConfiguration('eth2' + if_suffix)
self.WLANConfigurationList['1'] = wifi
if (_DoesInterfaceExist('wlan0' + if_suffix)
and _DoesInterfaceExist('wlan1' + if_suffix)):
# Two radios, instantiate both with fixed bands
wifi = dm.binwifi.WlanConfiguration('wlan0', if_suffix, bridge,
radio_list['radio0'], '2.4')
self.WLANConfigurationList['1'] = wifi
wifi = dm.binwifi.WlanConfiguration('wlan1', if_suffix, bridge,
radio_list['radio1'], '5',
standard='ac', width_5g=80,
self.WLANConfigurationList['2'] = wifi
elif _DoesInterfaceExist('wlan0' + if_suffix):
# One radio, allow switching bands
kwargs = {'width_5g': 40, 'autochan': 'LOW'}
# Quantenna devices cannot switch bands, and should use 802.11ac and 80
# MHz bandwidth.
if 'wlan0' + if_suffix in self._get_quantenna_interfaces():
kwargs['standard'] = 'ac'
kwargs['width_5g'] = 80
wifi = dm.binwifi.WlanConfiguration('wlan0', if_suffix, bridge,
radio_list['radio0'], **kwargs)
self.WLANConfigurationList['1'] = wifi
def _get_quantenna_interfaces(self):
return subprocess.check_output(['get-quantenna-interfaces']).split()
except (OSError, subprocess.CalledProcessError):
print 'calling get-quantenna-interfaces failed; assuming not Quantenna'
return []
class InternetGatewayDevice(tr.basemodel.InternetGatewayDevice):
"""Implements tr-98 InternetGatewayDevice (deprecated but heavily used)."""
def __init__(self, device_id, periodic_stats):
super(InternetGatewayDevice, self).__init__()
self.Unexport(params=['DeviceSummary', 'UserNumberOfEntries',
'Capabilities', 'CaptivePortal', 'DeviceConfig',
'DLNA', 'DNS', 'DownloadAvailability',
'DownloadDiagnostics', 'FAP', 'FaultMgmt',
'LANConfigSecurity', 'LANInterfaces',
'Layer2Bridging', 'Layer3Forwarding',
'QueueManagement', 'Security', 'Services',
'UDPEchoConfig', 'UploadDiagnostics',
'UPnP', 'USBHosts',
lists=['WANDevice', 'SmartCardReader', 'User'])
self.RadioList = {'radio0': Radio(), 'radio1': Radio()}
self.LANDeviceList = {'1': LANDevice('', 'br0', self.RadioList),
'2': LANDevice('_portal', 'br1', self.RadioList)}
self.ManagementServer = tr.core.TODO() # higher level code splices this in
self.DeviceInfo = dm.device_info.DeviceInfo98Linux26(device_id)
self.Time = dm.igd_time.TimeTZ()
self.PeriodicStatistics = periodic_stats
def LANDeviceNumberOfEntries(self):
return len(self.LANDeviceList)
def WANDeviceNumberOfEntries(self):
return 0
# pylint:disable=unused-argument
def PlatformInit(name, device_model_root):
"""Create platform-specific device models and initialize platform."""
root = device_model_root = Installer
params = []
objects = []
dev_id = DeviceId()
periodic_stats = dm.periodic_statistics.PeriodicStatistics()
root.Device = Device(dev_id, periodic_stats, dmroot=root)
root.InternetGatewayDevice = InternetGatewayDevice(dev_id, periodic_stats)
return (params, objects)