blob: 731c5b91ae06ab949663caa05df3d391a3b75668 [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
"""Implementation of tr-181 Device.DeviceInfo object.
Handles the Device.DeviceInfo portion of TR-181, as described
__author__ = ' (Denton Gentry)'
import abc
import glob
import os
import tornado.ioloop
import tr.core
import tr.session
import tr.x_catawampus_tr098_1_0
import tr.x_catawampus_tr181_2_0
import tr.cwmptypes
import temperature
BASE98 = tr.x_catawampus_tr098_1_0.X_CATAWAMPUS_ORG_InternetGatewayDevice_v1_0
BASE98IGD = BASE98.InternetGatewayDevice
BASE181 = tr.x_catawampus_tr181_2_0.X_CATAWAMPUS_ORG_Device_v2_0
BASE181DEVICE = BASE181.Device
# Unit tests can override these with fake data
PERIODICCALL = tornado.ioloop.PeriodicCallback
PROC_MEMINFO = '/proc/meminfo'
PROC_NET_DEV = '/proc/net/dev'
PROC_UPTIME = '/proc/uptime'
PROC_STAT = '/proc/stat'
SLASH_PROC = '/proc'
class DeviceIdMeta(object):
"""Class to provide platform-specific fields for DeviceInfo.
Each platform is expected to subclass DeviceIdMeta and supply concrete
implementations of all methods. We use a Python Abstract Base Class
to protect against future versions. If we add fields to this class,
any existing platform implementations will be prompted to add implementations
(because they will fail to startup when their DeviceId fails to
__metaclass__ = abc.ABCMeta
def Manufacturer(self):
return None
def ManufacturerOUI(self):
return None
def ModelName(self):
return None
def Description(self):
return None
def SerialNumber(self):
return None
def HardwareVersion(self):
return None
def AdditionalHardwareVersion(self):
return None
def SoftwareVersion(self):
return None
def AdditionalSoftwareVersion(self):
return None
def ProductClass(self):
return None
def ModemFirmwareVersion(self):
return None
def _GetUptime():
"""Return the number of seconds since boot."""
uptime = float(open(PROC_UPTIME).read().split()[0])
return int(uptime)
class DeviceInfo181Linux26(BASE181DEVICE.DeviceInfo):
"""Implements tr-181 DeviceInfo for Linux 2.6 and similar systems."""
def __init__(self, device_id, ioloop=None):
super(DeviceInfo181Linux26, self).__init__()
assert isinstance(device_id, DeviceIdMeta)
self.ioloop = ioloop or tornado.ioloop.IOLoop.instance()
self._device_id = device_id
self.Unexport(['FirstUseDate', 'ProvisioningCode'])
self.Unexport(objects=['NetworkProperties', 'ProxierInfo'])
self.LocationList = {}
self.ProcessStatus = ProcessStatusLinux26(ioloop=ioloop)
self.TemperatureStatus = temperature.TemperatureStatus()
self.VendorLogFileList = {}
self.VendorConfigFileList = {}
self.SupportedDataModelList = {}
self.ProcessorList = {}
self.X_CATAWAMPUS_ORG_LedStatusList = {}
self._next_led_number = 1
def __getattr__(self, name):
"""Allows passthrough of parameters to the platform-supplied device_id."""
return getattr(self._device_id, name)
def MemoryStatus(self):
return MemoryStatusLinux26()
def UpTime(self):
return _GetUptime()
def VendorLogFileNumberOfEntries(self):
return len(self.VendorLogFileList)
def VendorConfigFileNumberOfEntries(self):
return len(self.VendorConfigFileList)
def LocationNumberOfEntries(self):
return len(self.LocationList)
def ProcessorNumberOfEntries(self):
return len(self.ProcessorList)
def SupportedDataModelNumberOfEntries(self):
return len(self.SupportedDataModelList)
def X_CATAWAMPUS_ORG_LedStatusNumberOfEntries(self):
return len(self.X_CATAWAMPUS_ORG_LedStatusList)
def AddLedStatus(self, led):
self.X_CATAWAMPUS_ORG_LedStatusList[self._next_led_number] = led
self._next_led_number += 1
class MemoryStatusLinux26(BASE181DEVICE.DeviceInfo.MemoryStatus):
"""Abstraction to get memory information from the underlying platform.
Reads /proc/meminfo to find TotalMem and FreeMem.
Free = tr.cwmptypes.ReadOnlyUnsigned(0)
Total = tr.cwmptypes.ReadOnlyUnsigned(0)
def __init__(self):
super(MemoryStatusLinux26, self).__init__()
(total, free) = self._GetMemInfo()
type(self).Free.Set(self, free)
type(self).Total.Set(self, total)
def _GetMemInfo(self):
"""Fetch TotalMem and FreeMem from the underlying platform.
a list of two integers, (totalmem, freemem)
totalmem = 0
freemem = 0
with open(PROC_MEMINFO) as pfile:
for line in pfile:
fields = line.split()
name = fields[0]
value = fields[1]
if name == 'MemTotal:':
totalmem = int(value)
elif name == 'MemFree:':
freemem = int(value)
return (totalmem, freemem)
class ProcessStatusLinux26(BASE181DEVICE.DeviceInfo.ProcessStatus):
"""Get information about running processes on Linux 2.6.
Reads /proc/<pid> to get information about processes.
# Field ordering in /proc/<pid>/stat
_PID = 0
_COMM = 1
_STATE = 2
_UTIME = 13
_STIME = 14
_PRIO = 17
_RSS = 23
Process = tr.core.Extensible(BASE181DEVICE.DeviceInfo.ProcessStatus.Process)
def __init__(self, ioloop=None):
super(ProcessStatusLinux26, self).__init__()
tick = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
self._msec_per_jiffy = 1000.0 / tick
self.ioloop = ioloop or tornado.ioloop.IOLoop.instance()
self.scheduler = PERIODICCALL(self.CpuUsageTimer, 300 * 1000,
self.cpu_usage = 0.0
self.cpu_used = 0
self.cpu_total = 0
self.ProcessList = tr.core.AutoDict('ProcessList',
def _LinuxStateToTr181(self, linux_state):
"""Maps Linux process states to TR-181 process state names.
linux_state: One letter describing the state of the linux process,
as described in proc(5). One of "RSDZTW"
the tr-181 string describing the process state.
mapping = {
'R': 'Running',
'S': 'Sleeping',
'D': 'Uninterruptible',
'Z': 'Zombie',
'T': 'Stopped',
'W': 'Uninterruptible'}
return mapping.get(linux_state, 'Sleeping')
def _JiffiesToMsec(self, utime, stime):
ticks = int(utime) + int(stime)
msecs = ticks * self._msec_per_jiffy
return int(msecs)
def _RemoveParens(self, command):
return command[1:-1]
def _ProcFileName(self, pid):
return '%s/%s/stat' % (SLASH_PROC, pid)
def _ParseProcStat(self):
"""Compute CPU utilization using /proc/stat.
(used, total)
used: number of jiffies where CPU was active
total: total number of jiffies including idle
with open(PROC_STAT) as f:
for line in f:
fields = line.split()
if fields[0] == 'cpu':
user = float(fields[1])
nice = float(fields[2])
syst = float(fields[3])
idle = float(fields[4])
iowt = float(fields[5])
irq = float(fields[6])
sirq = float(fields[7])
total = user + nice + syst + idle + iowt + irq + sirq
used = total - idle
return (used, total)
return (0, 0)
def CpuUsageTimer(self):
"""Called periodically to compute CPU utilization since last call."""
(new_used, new_total) = self._ParseProcStat()
total = new_total - self.cpu_total
used = new_used - self.cpu_used
if total == 0:
self.cpu_usage = 0.0
self.cpu_usage = (used / total) * 100.0
self.cpu_total = new_total
self.cpu_used = new_used
def CPUUsage(self):
return int(self.cpu_usage)
def ProcessNumberOfEntries(self):
return len(self.ProcessList)
def GetProcess(self, pid):
"""Get a self.Process() object for the given pid."""
with open(self._ProcFileName(pid)) as f:
fields =
p = self.Process(PID=int(fields[self._PID]),
except IOError:
# This isn't an error. We have a list of files which existed the
# moment the glob.glob was run. If a process exits before we get
# around to reading it, its /proc files will go away.
p = self.Process(PID=pid, Command='<exited>', Size=0, Priority=0,
CPUTime=0, State='X_CATAWAMPUS-ORG_Exited')
return p
def IterProcesses(self):
"""Walks through /proc/<pid>/stat to return a list of all processes."""
for filename in glob.glob(self._ProcFileName('[0123456789]*')):
pid = int(filename.split('/')[-2])
proc = self.GetProcess(pid)
yield pid, proc
class LedStatusReadFromFile(
"""X_CATAWAMPUS-ORG_LedStatus implementation to read a line from a file."""
Name = tr.cwmptypes.ReadOnlyString('')
def __init__(self, name, filename):
super(LedStatusReadFromFile, self).__init__()
type(self).Name.Set(self, name)
self._filename = filename
def Status(self):
return open(self._filename).readline().strip()
class DeviceInfo98Linux26(BASE98IGD.DeviceInfo):
"""Implementation of tr-98 DeviceInfo for Linux."""
SpecVersion = tr.cwmptypes.ReadOnlyString('1.0')
def __init__(self, device_id):
super(DeviceInfo98Linux26, self).__init__()
assert isinstance(device_id, DeviceIdMeta)
self._device_id = device_id
self.Unexport(params=['DeviceLog', 'EnabledOptions', 'FirstUseDate',
self.VendorConfigFileList = {}
def UpTime(self):
return _GetUptime()
def VendorConfigFileNumberOfEntries(self):
return len(self.VendorConfigFileList)
def __getattr__(self, name):
if hasattr(self._device_id, name):
return getattr(self._device_id, name)
raise AttributeError('No such attribute %s' % name)
def main():
dp = DeviceInfo181Linux26('deviceid')
# print tr.handle.DumpSchema(dp)
print tr.handle.Dump(dp)
if __name__ == '__main__':