Merge bruno-penguin-1 into vudu_moca2
diff --git a/DataModel_requirement.cfg b/DataModel_requirement.cfg
new file mode 100644
index 0000000..1f0dac6
--- /dev/null
+++ b/DataModel_requirement.cfg
@@ -0,0 +1,279 @@
+Device.DeviceInfo.AdditionalHardwareVersion
+Device.DeviceInfo.AdditionalSoftwareVersion
+Device.DeviceInfo.Description
+Device.DeviceInfo.HardwareVersion
+Device.DeviceInfo.Manufacturer
+Device.DeviceInfo.ManufacturerOUI
+Device.DeviceInfo.MemoryStatus.Free
+Device.DeviceInfo.MemoryStatus.Total
+Device.DeviceInfo.ModelName
+Device.DeviceInfo.ProcessStatus.CPUUsage
+Device.DeviceInfo.ProcessStatus.Process.{i}.CPUTime
+Device.DeviceInfo.ProcessStatus.Process.{i}.Command
+Device.DeviceInfo.ProcessStatus.Process.{i}.PID
+Device.DeviceInfo.ProcessStatus.Process.{i}.Priority
+Device.DeviceInfo.ProcessStatus.Process.{i}.Size
+Device.DeviceInfo.ProcessStatus.Process.{i}.State
+Device.DeviceInfo.ProcessStatus.ProcessNumberOfEntries
+Device.DeviceInfo.ProcessorNumberOfEntries
+Device.DeviceInfo.ProductClass
+Device.DeviceInfo.SerialNumber
+Device.DeviceInfo.SoftwareVersion
+Device.DeviceInfo.SupportedDataModelNumberOfEntries
+Device.DeviceInfo.TemperatureStatus.TemperatureSensorNumberOfEntries
+Device.DeviceInfo.UpTime
+Device.DeviceInfo.VendorConfigFileNumberOfEntries
+Device.DeviceInfo.VendorLogFileNumberOfEntries
+Device.Ethernet.Interface.{i}.DuplexMode
+Device.Ethernet.Interface.{i}.Enable
+Device.Ethernet.Interface.{i}.LastChange
+Device.Ethernet.Interface.{i}.LowerLayers
+Device.Ethernet.Interface.{i}.MACAddress
+Device.Ethernet.Interface.{i}.MaxBitRate
+Device.Ethernet.Interface.{i}.Name
+Device.Ethernet.Interface.{i}.Stats.BroadcastPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.BroadcastPacketsSent
+Device.Ethernet.Interface.{i}.Stats.BytesReceived
+Device.Ethernet.Interface.{i}.Stats.BytesSent
+Device.Ethernet.Interface.{i}.Stats.DiscardPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.DiscardPacketsSent
+Device.Ethernet.Interface.{i}.Stats.ErrorsReceived
+Device.Ethernet.Interface.{i}.Stats.ErrorsSent
+Device.Ethernet.Interface.{i}.Stats.MulticastPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.MulticastPacketsSent
+Device.Ethernet.Interface.{i}.Stats.PacketsReceived
+Device.Ethernet.Interface.{i}.Stats.PacketsSent
+Device.Ethernet.Interface.{i}.Stats.UnicastPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.UnicastPacketsSent
+Device.Ethernet.Interface.{i}.Stats.UnknownProtoPacketsReceived
+Device.Ethernet.Interface.{i}.Status
+Device.Ethernet.Interface.{i}.Upstream
+Device.Ethernet.Interface.{i}.X_CATAWAMPUS-ORG_ActualBitRate
+Device.Ethernet.Interface.{i}.X_CATAWAMPUS-ORG_ActualDuplexMode
+Device.Ethernet.InterfaceNumberOfEntries
+Device.Ethernet.LinkNumberOfEntries
+Device.Ethernet.VLANTerminationNumberOfEntries
+Device.InterfaceStackNumberOfEntries
+Device.ManagementServer.CWMPRetryIntervalMultiplier
+Device.ManagementServer.CWMPRetryMinimumWaitInterval
+Device.ManagementServer.ConnectionRequestPassword
+Device.ManagementServer.ConnectionRequestURL
+Device.ManagementServer.ConnectionRequestUsername
+Device.ManagementServer.DefaultActiveNotificationThrottle
+Device.ManagementServer.EnableCWMP
+Device.ManagementServer.ManageableDeviceNumberOfEntries
+Device.ManagementServer.ParameterKey
+Device.ManagementServer.Password
+Device.ManagementServer.PeriodicInformEnable
+Device.ManagementServer.PeriodicInformInterval
+Device.ManagementServer.PeriodicInformTime
+Device.ManagementServer.STUNEnable
+Device.ManagementServer.URL
+Device.ManagementServer.UpgradesManaged
+Device.ManagementServer.Username
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.MACAddress
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.NodeID
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PHYRxRate
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PHYTxRate
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PacketAggregationCapability
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PreferredNC
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.QAM256Capable
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxBcastPowerLevel
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxErroredAndMissedPackets
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxPackets
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxPowerLevel
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxSNR
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.TxBcastRate
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.TxPackets
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.TxPowerControlReduction
+Device.MoCA.Interface.{i}.AssociatedDeviceNumberOfEntries
+Device.MoCA.Interface.{i}.BackupNC
+Device.MoCA.Interface.{i}.CurrentOperFreq
+Device.MoCA.Interface.{i}.CurrentVersion
+Device.MoCA.Interface.{i}.Enable
+Device.MoCA.Interface.{i}.FirmwareVersion
+Device.MoCA.Interface.{i}.HighestVersion
+Device.MoCA.Interface.{i}.LastChange
+Device.MoCA.Interface.{i}.LastOperFreq
+Device.MoCA.Interface.{i}.LowerLayers
+Device.MoCA.Interface.{i}.MACAddress
+Device.MoCA.Interface.{i}.Name
+Device.MoCA.Interface.{i}.NetworkCoordinator
+Device.MoCA.Interface.{i}.NodeID
+Device.MoCA.Interface.{i}.PacketAggregationCapability
+Device.MoCA.Interface.{i}.PrivacyEnabled
+Device.MoCA.Interface.{i}.QAM256Capable
+Device.MoCA.Interface.{i}.Stats.BroadcastPacketsReceived
+Device.MoCA.Interface.{i}.Stats.BroadcastPacketsSent
+Device.MoCA.Interface.{i}.Stats.BytesReceived
+Device.MoCA.Interface.{i}.Stats.BytesSent
+Device.MoCA.Interface.{i}.Stats.DiscardPacketsReceived
+Device.MoCA.Interface.{i}.Stats.DiscardPacketsSent
+Device.MoCA.Interface.{i}.Stats.ErrorsReceived
+Device.MoCA.Interface.{i}.Stats.ErrorsSent
+Device.MoCA.Interface.{i}.Stats.MulticastPacketsReceived
+Device.MoCA.Interface.{i}.Stats.MulticastPacketsSent
+Device.MoCA.Interface.{i}.Stats.PacketsReceived
+Device.MoCA.Interface.{i}.Stats.PacketsSent
+Device.MoCA.Interface.{i}.Stats.UnicastPacketsReceived
+Device.MoCA.Interface.{i}.Stats.UnicastPacketsSent
+Device.MoCA.Interface.{i}.Stats.UnknownProtoPacketsReceived
+Device.MoCA.Interface.{i}.Status
+Device.MoCA.Interface.{i}.Upstream
+Device.MoCA.InterfaceNumberOfEntries
+Device.PeriodicStatistics.MaxReportSamples
+Device.PeriodicStatistics.MinSampleInterval
+Device.PeriodicStatistics.SampleSet.{i}.Enable
+Device.PeriodicStatistics.SampleSet.{i}.FetchSamples
+Device.PeriodicStatistics.SampleSet.{i}.Name
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.CalculationMode
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Enable
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Failures
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.HighThreshold
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.LowThreshold
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Reference
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleMode
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleSeconds
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SuspectData
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Values
+Device.PeriodicStatistics.SampleSet.{i}.ParameterNumberOfEntries
+Device.PeriodicStatistics.SampleSet.{i}.ReportEndTime
+Device.PeriodicStatistics.SampleSet.{i}.ReportSamples
+Device.PeriodicStatistics.SampleSet.{i}.ReportStartTime
+Device.PeriodicStatistics.SampleSet.{i}.SampleInterval
+Device.PeriodicStatistics.SampleSet.{i}.SampleSeconds
+Device.PeriodicStatistics.SampleSet.{i}.Status
+Device.PeriodicStatistics.SampleSet.{i}.TimeReference
+Device.PeriodicStatistics.SampleSetNumberOfEntries
+Device.Services.STBService.{i}.Components.FrontEnd.{i}.IP.IGMP.ClientGroup.{i}.GroupAddress
+Device.Services.STBService.{i}.Components.FrontEnd.{i}.IP.IGMP.ClientGroupNumberOfEntries
+Device.Services.STBService.{i}.Components.FrontEndNumberOfEntries
+Device.Services.STBServiceNumberOfEntries
+Device.Services.StorageServices.Capabilities.FTPCapable
+Device.Services.StorageServices.Capabilities.HTTPCapable
+Device.Services.StorageServices.Capabilities.HTTPSCapable
+Device.Services.StorageServices.Capabilities.HTTPWritable
+Device.Services.StorageServices.Capabilities.SFTPCapable
+Device.Services.StorageServices.Capabilities.SupportedFileSystemTypes
+Device.Services.StorageServices.Capabilities.SupportedNetworkProtocols
+Device.Services.StorageServices.Capabilities.SupportedRaidTypes
+Device.Services.StorageServices.Capabilities.VolumeEncryptionCapable
+Device.Services.StorageServices.Enable
+Device.Services.StorageServices.LogicalVolume.{i}.Capacity
+Device.Services.StorageServices.LogicalVolume.{i}.Enable
+Device.Services.StorageServices.LogicalVolume.{i}.FileSystem
+Device.Services.StorageServices.LogicalVolume.{i}.FolderNumberOfEntries
+Device.Services.StorageServices.LogicalVolume.{i}.Name
+Device.Services.StorageServices.LogicalVolume.{i}.Status
+Device.Services.StorageServices.LogicalVolume.{i}.ThresholdLimit
+Device.Services.StorageServices.LogicalVolume.{i}.UsedSpace
+Device.Services.StorageServices.LogicalVolume.{i}.X_CATAWAMPUS-ORG_ReadOnly
+Device.Services.StorageServices.LogicalVolumeNumberOfEntries
+Device.Services.StorageServices.PhysicalMedium.{i}.Capacity
+Device.Services.StorageServices.PhysicalMedium.{i}.ConnectionType
+Device.Services.StorageServices.PhysicalMedium.{i}.FirmwareVersion
+Device.Services.StorageServices.PhysicalMedium.{i}.Health
+Device.Services.StorageServices.PhysicalMedium.{i}.HotSwappable
+Device.Services.StorageServices.PhysicalMedium.{i}.Model
+Device.Services.StorageServices.PhysicalMedium.{i}.Name
+Device.Services.StorageServices.PhysicalMedium.{i}.Removable
+Device.Services.StorageServices.PhysicalMedium.{i}.SMARTCapable
+Device.Services.StorageServices.PhysicalMedium.{i}.SerialNumber
+Device.Services.StorageServices.PhysicalMedium.{i}.Vendor
+Device.Services.StorageServices.PhysicalMediumNumberOfEntries
+Device.Services.StorageServices.StorageArrayNumberOfEntries
+Device.Services.StorageServices.UserAccountNumberOfEntries
+Device.Services.StorageServices.UserGroupNumberOfEntries
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.BadEraseBlocks
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.CorrectedErrors
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.EraseBlockSize
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.IOSize
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.MaxEraseCount
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.Name
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.ReservedEraseBlocks
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolume.{i}.DataBytes
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolume.{i}.Name
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolume.{i}.Status
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolumeNumberOfEntries
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.TotalEraseBlocks
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.UncorrectedErrors
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMediaNumberOfEntries
+InternetGatewayDevice.DeviceInfo.AdditionalHardwareVersion
+InternetGatewayDevice.DeviceInfo.AdditionalSoftwareVersion
+InternetGatewayDevice.DeviceInfo.Description
+InternetGatewayDevice.DeviceInfo.HardwareVersion
+InternetGatewayDevice.DeviceInfo.Manufacturer
+InternetGatewayDevice.DeviceInfo.ManufacturerOUI
+InternetGatewayDevice.DeviceInfo.ModelName
+InternetGatewayDevice.DeviceInfo.ModemFirmwareVersion
+InternetGatewayDevice.DeviceInfo.ProductClass
+InternetGatewayDevice.DeviceInfo.SerialNumber
+InternetGatewayDevice.DeviceInfo.SoftwareVersion
+InternetGatewayDevice.DeviceInfo.SpecVersion
+InternetGatewayDevice.DeviceInfo.UpTime
+InternetGatewayDevice.DeviceInfo.VendorConfigFileNumberOfEntries
+InternetGatewayDevice.LANDevice.{i}.LANEthernetInterfaceNumberOfEntries
+InternetGatewayDevice.LANDevice.{i}.LANUSBInterfaceNumberOfEntries
+InternetGatewayDevice.LANDevice.{i}.LANWLANConfigurationNumberOfEntries
+InternetGatewayDevice.LANDeviceNumberOfEntries
+InternetGatewayDevice.ManagementServer.ConnectionRequestPassword
+InternetGatewayDevice.ManagementServer.ConnectionRequestURL
+InternetGatewayDevice.ManagementServer.ConnectionRequestUsername
+InternetGatewayDevice.ManagementServer.DefaultActiveNotificationThrottle
+InternetGatewayDevice.ManagementServer.EnableCWMP
+InternetGatewayDevice.ManagementServer.ParameterKey
+InternetGatewayDevice.ManagementServer.Password
+InternetGatewayDevice.ManagementServer.PeriodicInformEnable
+InternetGatewayDevice.ManagementServer.PeriodicInformInterval
+InternetGatewayDevice.ManagementServer.PeriodicInformTime
+InternetGatewayDevice.ManagementServer.URL
+InternetGatewayDevice.ManagementServer.UpgradesManaged
+InternetGatewayDevice.ManagementServer.Username
+InternetGatewayDevice.PeriodicStatistics.MaxReportSamples
+InternetGatewayDevice.PeriodicStatistics.MinSampleInterval
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Enable
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.FetchSamples
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Name
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.CalculationMode
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Enable
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Failures
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.HighThreshold
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.LowThreshold
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Reference
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleMode
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleSeconds
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SuspectData
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Values
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ParameterNumberOfEntries
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ReportEndTime
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ReportSamples
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ReportStartTime
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.SampleInterval
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.SampleSeconds
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Status
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.TimeReference
+InternetGatewayDevice.PeriodicStatistics.SampleSetNumberOfEntries
+InternetGatewayDevice.Time.CurrentLocalTime
+InternetGatewayDevice.Time.Enable
+InternetGatewayDevice.Time.LocalTimeZoneName
+InternetGatewayDevice.WANDeviceNumberOfEntries
+TraceRoute.DSCP
+TraceRoute.DataBlockSize
+TraceRoute.DiagnosticsState
+TraceRoute.Host
+TraceRoute.Interface
+TraceRoute.MaxHopCount
+TraceRoute.NumberOfTries
+TraceRoute.ResponseTime
+TraceRoute.RouteHopsNumberOfEntries
+TraceRoute.Timeout
+X_CATAWAMPUS-ORG_CATAWAMPUS.RuntimeEnvInfo
+X_GOOGLE-COM_GFIBERTV.Mailbox.Name
+X_GOOGLE-COM_GFIBERTV.Mailbox.Node
+X_GOOGLE-COM_GFIBERTV.Mailbox.NodeList
+X_GOOGLE-COM_GFIBERTV.Mailbox.Value
+X_GOOGLE-COM_GVSB.EpgPrimary
+X_GOOGLE-COM_GVSB.EpgSecondary
+X_GOOGLE-COM_GVSB.GvsbChannelLineup
+X_GOOGLE-COM_GVSB.GvsbKick
+X_GOOGLE-COM_GVSB.GvsbServer
diff --git a/acs.py b/acs.py
new file mode 100755
index 0000000..8725e02
--- /dev/null
+++ b/acs.py
@@ -0,0 +1,183 @@
+#!/usr/bin/python
+# Copyright 2012 Google Inc. All Rights Reserved.
+
+"""This File executes create/modify/delete operations on ACS datastore.
+
+This class is used to interact with scripts that directly modifies
+ACS datastore configuration. These ACS related actions may require
+google3 dependencies
+"""
+
+__author__ = 'Lehan Meng (lmeng@google.com)'
+
+
+import os
+import re
+
+
+class ACS(object):
+ """This class executes operations that change Device profiles on ACS."""
+
+ def __init__(self, expected_version=None, **kwargs):
+ """Constructor for this Class."""
+ self.acs_info = {
+ 'url': None,
+ # expected image version ID on ACS
+ 'imageVersion': expected_version,
+ 'acs_path': None,
+ 'outfile': 'cmdOutput.txt'}
+ for s in kwargs:
+ self.acs_info[s] = kwargs[s]
+
+ def SetLogging(self, log):
+ """Setup the logging file(s)."""
+ self.log = log
+
+ def SetImageVersion(self, expected_version):
+ """Set the expected image version that needs to be configured at ACS.
+
+ Args:
+ expected_version: the version string of the expected image.
+ """
+ m = re.match('\d+', expected_version)
+ if m is None:
+ info = self.log.CreateErrorInfo(
+ 'critical',
+ 'Error! Please use software id as expected image version value')
+ self.log.SendLine(None, info)
+ else:
+ self.acs_info['imageVersion'] = expected_version
+
+ def ParseAppIDFromACSURL(self, url='https://acs.e2e.gfsvc.com/cwmp'):
+ """Parse app_id from ACS instance host name."""
+ pass
+
+ def SaveConfiguration(self, profile_id, version_id, app_id,
+ host='https://acs.e2e.gfsvc.com/cwmp'):
+ """Run the ACS configuration script from Google3 location."""
+ if not version_id:
+ v_id = self.acs_info['imageVersion']
+ else:
+ v_id = version_id
+
+ cur_dir = os.getcwd()
+ print '============' + cur_dir
+ # the acs_path is the google3 repository where upgrade_test.py
+ # locates. This test depends on it to modify ACS datastore.
+ os.chdir(self.acs_info['acs_path'])
+ print '============' + os.getcwd()
+ os.system(
+ 'blaze test --notest_loasd --test_arg=--imageVersion='
+ + v_id + ' --test_arg=--profile_id='
+ + profile_id + ' --test_arg=--app_id=' + app_id
+ + ' --test_arg=--host=' + host
+ + ' --test_strategy=local :upgrade_test ')
+ os.chdir(cur_dir)
+ print '============' + os.getcwd()
+
+ def StubbyClientCall(self, cmd=None):
+ """This is the nbi_client stubby call to get parameter names and values."""
+ p = os.popen(cmd, 'r')
+ log_list = []
+ while 1:
+ line = p.readline()
+ if not line: break
+ log_list.append(line.strip())
+ return log_list
+
+ def ParseParameterAccessStates(self, cmd_list):
+ """Find out if a parameter is 'writable' or 'read-write'.
+
+ This method process the nbi_client getParameterNames() call result
+ to find out the access state of the parameters.
+ Args:
+ cmd_list: the getParameterNames() call returned result list
+ Returns:
+ a dictionary: parameter name as index, and its access state
+ as value.
+ """
+ if ('FAILED' in cmd_list[0]) or (not 'SUCCESS' in cmd_list[0]):
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'nbi_clienet call failed. ' + str(cmd_list[0]))
+ self.log.SendLine(None, info)
+ return False
+
+ para_dict = {}
+ for line in cmd_list:
+ if 'name:' in line:
+ name = re.sub('.*name:\s*\"(.*)\".*', r'\1', line)
+ index = cmd_list.index(line)+1
+ access = re.sub('.*writable:\s*([a-zA-Z]+)', r'\1', cmd_list[index])
+ para_dict.update({name: access})
+ return para_dict
+
+ def CheckCmdCall(self, cmd_list):
+ """Check if the RPC call is a success or failure."""
+ if ('FAILED' in cmd_list[0]) or (not 'SUCCESS' in cmd_list[0]):
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'nbi_client call failed. ' + str(cmd_list[0]))
+ self.log.SendLine(None, info)
+ return False
+ else:
+ return True
+
+ def ParseManagementURL(self, cmd_list):
+ """Parse Management URL from GetParameterValues query reply message.
+
+ Reply message is in the following format:
+ #########################################################
+ SUCCESS! Response: parameter_value_list {
+ parameter_value_struct {
+ name: "InternetGatewayDevice.ManagementServer.URL"
+ string_value: "https://acs.e2e.gfsvc.com/cwmp"
+ }
+ }
+ #########################################################
+
+ Args:
+ cmd_list: the ACS url information obtained from device log or file
+ Returns:
+ returns the management url found, or return false if url not found
+ """
+ if not cmd_list:
+ return False
+ for line in cmd_list:
+ if ('name:' in line and
+ 'InternetGatewayDevice.ManagementServer.URL' in line):
+ s = cmd_list[cmd_list.index(line)+1]
+ m = re.search('http[s]?://(\w*\.)+com/cwmp', s)
+ if not m:
+ info = self.log.CreateErrorInfo(
+ 'Warning', 'No management server URL found in GetParameterValues '
+ 'query reply.')
+ self.log.SendLine(None, info)
+ return False
+ else:
+ return m.group(0)
+
+ def ParseParameterNames(self, cmd_list, replace_index=False):
+ """Parse the get values RPC call response, retrieve parameter name list.
+
+ Args:
+ cmd_list: the command reply message from nbi_client
+ replace_index: if True, the index digits in parameter names will be
+ replaced with '.{i}.'
+ Returns:
+ return the parsed parameter name list
+ """
+ if not self.CheckCmdCall(cmd_list):
+ return False
+
+ chk_list = []
+ for line in cmd_list:
+ if 'name:' in line:
+ output = re.sub('.*\"(.*)\".*', r'\1', line)
+ if replace_index:
+ output = re.sub(r'(\.)[\d]+(\.)', r'\1{i}\2', output)
+ if chk_list.count(output) < 1:
+ chk_list.append(output)
+ return chk_list
+
+
+if __name__ == '__main__':
+ pass
diff --git a/basic_TR69.py b/basic_TR69.py
new file mode 100644
index 0000000..0b9bfae
--- /dev/null
+++ b/basic_TR69.py
@@ -0,0 +1,651 @@
+#!/usr/bin/python
+# Copyright 2012 Google Inc. All Rights Reserved.
+
+"""This class implements some basic TR069 test cases.
+
+ This class extends the TestCase Class.
+ This file implements some basic TR069 test cases to verify Bruno management
+ features. In the following test cases, only those that are defined in
+ 'config.cfg' file will be executed.
+
+ Basic TR069 test cases include the following:
+ testID: 11865129 7.6.1 ACS Connection via IPv6
+ testID: 11721375 7.6.10 Verify there is a way to reset to factory default
+ if user modifies the local configuration
+ '/etc/init.d/S05factoryreset later'
+ testID: 14319146 7.6.19 Default ACS URL
+ testID: 11721371 7.6.2 ACS URL Pass to Bruno via Moca
+ testID: 14312009 7.6.21 Restrict the ability to locally configure ACS URL
+ testID: 14186028 7.6.22 Security of the link between the DHCPv6 server
+ and the CPE
+ testID: 14312010 7.6.23 CPE Connection Initiation
+ testID: 14247012 7.6.24.1 Support of Connection Request notification
+ mechanism
+ testID: 14186029 7.6.24.2 InternetGatewayDevice.ManagementServer.
+ ConnectionRequestURL
+ testID: 14312011 7.6.24.3 InternetGatewayDevice.ManagementServer.
+ ConnectionRequestURL format
+ testID: 14293012 7.6.24.4 No data is conveyed in the Connection
+ Request HTTP Get notification
+ testID: 14165037 7.6.24.5 Restrict the number of Connection Request
+ notifications
+ testID: 14315010 7.6.24.6 Connection Request Notification does not
+ terminate current session
+ testID: 14247013 7.6.25.1 MaxEnvelopes
+ testID: 14312012 7.6.25.2 Cookie support (optional)
+ testID: 14293013 7.6.25.4 Authentication
+ testID: 14311008 7.6.26.1 SOAP namespace identifier
+ testID: 14293014 7.6.26.2 SOAP Fault element
+ testID: 14311009 7.6.26.3 SOAP future extensibility
+ testID: 14186030 7.6.27 Fault Handling
+ testID: 14319148 7.6.27.1 SOAP requests respond
+ testID: 14266497 7.6.27.2 Session Termination
+ testID: 14312013 7.6.28 Correctly handle concurrent downloads in catawampus
+ testID: 11874098 7.6.3.1 TLS (1.2)/SSL based Transport for TR-069 on IPv6
+ testID: 14315007 7.6.3.2 Support for encryption algorithms
+ testID: 14247014 7.6.32 Persist TR-069 config across reboots
+ testID: 14293015 7.6.33 Send notification when disk is full
+ testID: 14293016 7.6.34 Support for multiple SSIDs in WLANConfiguration
+ testID: 11865130 7.6.4 TR069-ACS Validation
+ testID: 11722370 7.6.5 TR069-Inform Request
+ testID: 11874103 7.6.7 Digest Authentication
+ testID: 11843139 7.6.8.1 Support of RPCs: REBOOT
+"""
+
+__author__ = 'Lehan Meng (lmeng@google.com)'
+
+
+import os
+import re
+import time
+
+from IPy import IP
+
+import device
+import ssh
+import testCase
+
+
+class BasicTR069Test(testCase.TestCase):
+ """Class for test cases: Bruno basic TR069 features.
+
+ A range of basic TR069 test cases are defined in this Class
+ """
+
+ def Init(self):
+ """Initialize the networking feature tests."""
+ # Get all device information.
+ info = self.log.CreateResultInfo(
+ '---', 'Networking test verification started...')
+ self.log.SendLine(self.test_info, info)
+ self.bruno = device.Bruno(user=self.params['user'],
+ addr=self.params['addr'],
+ bruno_prompt=self.params['bruno_prompt'],
+ addr_ipv6=self.params['addr_ipv6'])
+ self.bruno.SetLogging(self.log)
+ if not self.p_ssh:
+ self.p_ssh = ssh.SSH(user=self.params['user'],
+ addr=self.params['addr'],
+ bruno_prompt=self.params['bruno_prompt'],
+ addr_ipv6=self.params['addr_ipv6'])
+ self.bruno.dev_ssh = self.p_ssh
+ self.p_ssh.SetLogging(self.log)
+
+ self.__delay_5_sec = 5
+ self.__delay_5_min = 300
+ self.__delay_15_min = 900
+ return
+
+ def SshTunnel(self):
+ """Create ssh tunnel for the test."""
+ self.p_ssh.SshRetry(5, 15)
+ return
+
+ def VerifyDuplicatedAddress(self, ip_addr):
+ """Verify device can detect duplicated ipv6 address existing in network.
+
+ Set a duplicated ip address on the device.
+ Verify that the device can detect duplicated ip address
+
+ Args:
+ ip_addr: the duplicated address
+ Returns:
+ True: if duplicated detected
+ False: if duplicated address is not detected
+ """
+ time_stamp = self.bruno.GetTimeStamp()
+ self.bruno.dev_ssh.SendCmd('ip -6 addr add ' + ip_addr + '/64 dev br0')
+ time.sleep(1)
+ self.bruno.dev_ssh.SendCmd('dmesg')
+ log_list = self.bruno.dev_ssh.GetCmdOutput()
+
+ pattern_line = (
+ '\[\d*\.\d{3}\]\s*\w*\d:\s* IPv6 duplicate address\s*('
+ + ip_addr + ')\s*[/\d+]*\s*detected!')
+ self.bruno.dev_ssh.SendCmd('ip -6 addr del ' + ip_addr + '/64 dev br0')
+
+ if self.bruno.FindLineInLog(log_list, time_stamp, pattern_line):
+ info = self.log.CreateResultInfo('Pass', 'Duplicated IPv6 address '
+ 'detected: ' + ip_addr)
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateResultInfo('Failed',
+ 'Duplicated IPv6 address not detected: '
+ + ip_addr + '. Verification Failed!')
+ self.log.SendLine(self.test_info, info)
+ #[132344.324] br0: IPv6 duplicate address fe80:... detected!
+
+ def VerifyExpectedVersion(self):
+ """Verify if device is running on expected software version."""
+ if not self.bruno.VerifyCurrentVersion(self.params['expected_version']):
+ info = self.log.CreateErrorInfo('critical',
+ 'Device software version is not expected!'
+ ' Exit!')
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+
+ def VerifyACSWithIPv6(self):
+ """Test ID: 11865129 - Verify IPv6 connection to ACS.
+
+ To verify device connects to ACS via IPv6, use tcpdump to capture
+ network packets, filtered with source IPv6 address, and destination
+ IPv6 address of Bruno and ACS.
+
+ tcpdump filter:
+ 1. source address: device address
+ destination address: ACS address
+ 2. source address: ACS address
+ destination address: device address
+
+ if packets on both directions are found, test succeeds.
+ Otherwise, test failed.
+ """
+ info = self.log.CreateProgressInfo(
+ '10%', 'Verifying current image version, expected: '
+ + self.params['expected_version'])
+ self.log.SendLine(self.test_info, info)
+ self.VerifyExpectedVersion()
+
+ # capture 10 packets on both directions.
+ # capture length: 100 bytes
+ packet_to_catch = '5'
+ bytes_to_catch = '100'
+ interface_to_catch = 'br0'
+ tcpdump_command = ('tcpdump -i ' + interface_to_catch + ' -c '
+ + packet_to_catch + ' -s ' + bytes_to_catch
+ + ' \'(((src ' + self.params['addr_ipv6']
+ + ') and (dst ' + self.params['acs_ipv6']
+ + ')) or ((src ' + self.params['acs_ipv6']
+ + ') and (dst ' + self.params['addr_ipv6']
+ + ')))\'')
+ info = self.log.CreateProgressInfo(
+ '30%', 'Calling tcpdump to capture device traffic to ACS server: '
+ 'capturing on interface: ' + interface_to_catch
+ + ' Catch packets: ' + packet_to_catch)
+ self.log.SendLine(self.test_info, info)
+ self.p_ssh.SendCmd(tcpdump_command, 180)
+ info = self.log.CreateProgressInfo(
+ '60%', 'Packets captured. Analyzing...')
+ self.log.SendLine(self.test_info, info)
+ tcpdump_result = self.p_ssh.GetCmdOutput()
+ tcpdump_result.reverse()
+ if self.CheckACSIPv6Connection(tcpdump_result):
+ info = self.log.CreateProgressInfo(
+ '100%', 'TR069 traffic verified: Device connected to ACS via IPv6')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Pass', 'TR069 traffic verified: Device connected to ACS via IPv6')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateResultInfo(
+ 'Failed', 'TR069 traffic verified: No IPv6 connection detected.')
+ self.log.SendLine(self.test_info, info)
+
+ def ParseDumpMsgToPackets(self, tcpdump_result):
+ """Parse tcpdump dump messages information to packets.
+
+ tcpdump result (for example):
+ 14:24:36.887936 00:1a:11:30:64:e6 (oui Unknown) > a8:d0:e5:a1:e3:02 L1
+ (oui Unknown), ethertype IPv6 (0x86dd), length 86: L2
+ 0x0000: 6000 0000 0020 0640 2605 a601 fe00 fd18 `......@&....... L3
+ 0x0010: 021a 11ff fe30 64e6 2001 4860 8005 0000 .....0d...H`.... L4
+ 0x0020: 0000 0000 0000 008d e2e8 01bb 73e4 0dc8 ............s... L5
+ 0x0030: a1bf d109 8011 0429 276b 0000 0101 080a .......)'k...... L6
+ 0x0040: 0389 b875 d29d 2401 ...u..$. L7
+ 14:24:36.912863 a8:d0:e5:a1:e3:02 (oui Unknown) > 00:1a:11:30:64:e6 L8
+ (oui Unknown), ethertype IPv6 (0x86dd), length 86: L9
+ 0x0000: 6000 0000 0020 0634 2001 4860 8005 0000 `......4..H`.... L10
+ 0x0010: 0000 0000 0000 008d 2605 a601 fe00 fd18 ........&....... L11
+ 0x0020: 021a 11ff fe30 64e6 01bb e2e8 a1bf d109 .....0d......... L12
+ 0x0030: 73e4 0dc9 8011 05a8 6bdc 0000 0101 080a s.......k....... L13
+ 0x0040: d29d 763c 0389 b875 ..v<...u L14
+
+ This method will parse the dump message to two packets dump:
+ (new packet starts with time stamp: 14:24:36.912863)
+ packet_list[0]: line L1 ~ line L7
+ packet_list[1]: line L8 ~ line L14
+
+ Args:
+ tcpdump_result: raw dump message from tcpdump
+ Returns:
+ packet_list
+ """
+ packet_list = []
+ packet = []
+ for line in tcpdump_result:
+ m = re.search('^\s*\d{2}:\d{2}:\d{2}\.\d+\s*', line)
+ if m:
+ # new packet found:
+ packet = []
+ packet.append(line)
+ index = tcpdump_result.index(line)
+ while index+1 < len(tcpdump_result):
+ packet_line = tcpdump_result[index+1]
+ m = re.search('^\s*\d{2}:\d{2}:\d{2}\.\d+\s*', packet_line)
+ if not m:
+ # not start of next packet
+ packet.append(packet_line)
+ index +=1
+ else:
+ # next packet starts
+ packet_list.append(packet)
+ break
+ return packet_list
+
+ def FindSrcAddrFromTcpdump(self, packet):
+ """Parse source IPv6 address from tcpdump message.
+
+ Args:
+ packet: tcpdump dump message
+ Returns:
+ source address in packet dump message
+ """
+ src_addr = None
+ for line in packet:
+ m = re.search('^\s*0x0000:\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*.*', line)
+ if m:
+ # first line of packet data:
+ src_addr = (m.group(5) + ':' + m.group(6) + ':' + m.group(7)
+ + ':' + m.group(8)) + ':'
+ index = packet.index(line)
+ m = re.search('^\s*0x0010:\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*.*', packet[index+1])
+ src_addr += (m.group(1) + ':' + m.group(2) + ':' + m.group(3)
+ + ':' + m.group(4))
+ break
+ return src_addr
+
+ def FindDstAddrFromTcpdump(self, packet):
+ """Parse destination IPv6 address from tcpdump message.
+
+ Args:
+ packet: tcpdump dump message
+ Returns:
+ source address in packet dump message
+ """
+ dst_addr = None
+ for line in packet:
+ m = re.search('^\s*0x0010:\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*.*', line)
+ if m:
+ # first line of packet data:
+ dst_addr = (m.group(5) + ':' + m.group(6) + ':' + m.group(7)
+ + ':' + m.group(8)) + ':'
+ index = packet.index(line)
+ m = re.search('^\s*0x0020:\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*([\da-f]{4})'
+ '\s*([\da-f]{4})\s*([\da-f]{4})\s*.*', packet[index+1])
+ dst_addr += (m.group(1) + ':' + m.group(2) + ':' + m.group(3)
+ + ':' + m.group(4))
+ break
+ return dst_addr
+
+ def CheckACSIPv6Connection(self, tcpdump_result):
+ """Examine captured packets, find IPv6 communication between ACS and device.
+
+ Args:
+ tcpdump_result: tcpdump result: captured packets message dump
+ Returns:
+ True: if bi-directional communication found from packet list.
+ False: otherwise
+ """
+ packet_list = self.ParseDumpMsgToPackets(tcpdump_result)
+ dev_to_acs = False
+ acs_to_dev = False
+ for pacekt in packet_list:
+ src_addr = IP(self.FindSrcAddrFromTcpdump(pacekt))
+ dst_addr = IP(self.FindDstAddrFromTcpdump(pacekt))
+ if (src_addr == IP(self.params['addr_ipv6'])
+ and dst_addr == IP(self.params['acs_ipv6'])):
+ dev_to_acs = True
+ if (dst_addr == IP(self.params['addr_ipv6'])
+ and src_addr == IP(self.params['acs_ipv6'])):
+ acs_to_dev = True
+
+ if dev_to_acs and acs_to_dev:
+ return True
+ else:
+ return False
+
+ def VerifyFactoryReset(self):
+ """Test case: 11721375, Verify the factory reset function of device.
+
+ Verify factory reset according to the following conditions:
+ - version
+ - address
+ - cat /etc/ntpd.conf
+ - cat /etc/resolv.conf
+ - cat /tmp/gvsbhost
+ - ls /tmp/gvsb*
+ - cat /tmp/cwmp/acs_url
+ - ls -l /var/media
+ - ls -l /rw/sage
+
+ This test will need to manually reset the device using reset button.
+ """
+ test_failed = False
+ info = self.log.CreateProgressInfo(
+ '10%', 'Verifying current image version, expected: '
+ + self.params['expected_version'])
+ self.log.SendLine(self.test_info, info)
+ self.VerifyExpectedVersion()
+
+ # get file time information:
+ wiz_bin_date = None
+ self.p_ssh.SendCmd('ls -l /rw/sage')
+ cmd_list = self.p_ssh.GetCmdOutput()
+ for line in cmd_list:
+ if 'Wiz.bin' in line:
+ m = re.search('.*(\d{4}-\d{2}-\d{2}\s*\d+:\d+).*', line)
+ if m:
+ wiz_bin_date = m.group(1)
+
+ info = self.log.CreateProgressInfo(
+ '20%', 'Please reset the device. '
+ 'Hold down reset button for 5 seconds...')
+ self.log.SendLine(self.test_info, info)
+
+ info = self.log.CreateProgressInfo(
+ '30%', 'Wait ' + str(self.__delay_5_min)
+ + ' for device to reset and reboot...')
+ self.log.SendLine(self.test_info, info)
+ time.sleep(self.__delay_5_min)
+
+ info = self.log.CreateProgressInfo(
+ '40%', 'Connecting to device...')
+ self.log.SendLine(self.test_info, info)
+ self.SshTunnel()
+
+ addr = self.bruno.GetIPv4Address()
+ if not addr:
+ info = self.log.CreateErrorInfo('critical',
+ 'Error! No IPv4 address found!')
+ self.log.SendLine(self.test_info, info)
+ test_failed = True
+ else:
+ info = self.log.CreateProgressInfo(
+ '50%', 'Checking device IPv4 address succeed: IPv4: '
+ + addr)
+ self.log.SendLine(self.test_info, info)
+
+ addr_v6 = self.bruno.GetIPv6Address()
+ if not addr_v6:
+ info = self.log.CreateErrorInfo('warning', 'No IPv6 address found!')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '55%', 'Checking device IPv6 address succeed: IPv6: '
+ + addr_v6[0])
+ self.log.SendLine(self.test_info, info)
+
+ if self.bruno.GetProductClass() == 'GFMS100':
+ # fat bruno, check Wiz.bin file:
+ found_file = False
+ self.p_ssh.SendCmd('ls -l /rw/sage')
+ cmd_list = self.p_ssh.GetCmdOutput()
+ for line in cmd_list:
+ if 'Wiz.bin' in line:
+ found_file = True
+ m = re.search('.*(\d{4}-\d{2}-\d{2}\s*\d+:\d+).*', line)
+ new_date = m.group(1)
+ if new_date == wiz_bin_date:
+ # if file is not updated
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'warning', 'Wiz.bin not updated: '
+ 'file date before reset: ' + wiz_bin_date
+ + ' new file date: ' + new_date)
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '60%', 'Checking Wiz.bin file succeed: new file created at: '
+ + new_date)
+ self.log.SendLine(self.test_info, info)
+ break
+ if not found_file:
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'No Wiz.bin file found after reset!')
+ self.log.SendLine(self.test_info, info)
+
+ # verify ntpd.conf
+ ts = self.bruno.GetTimeServer()
+ if not ts:
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'No time server found in file /etc/ntpd.conf!')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '65%', 'Checking time server (ntpd.conf) succeed: Timeserver: '
+ + ts)
+ self.log.SendLine(self.test_info, info)
+
+ # verify resolv.conf
+ dns = self.bruno.GetDNSServer()
+ if not dns:
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'No DNS server found in file /etc/resolv.conf!')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '70%', 'Checking DNS server (resolv.conf) succeed: DNS: '
+ + dns)
+ self.log.SendLine(self.test_info, info)
+
+ # check gvsb:
+ gvsb = self.bruno.GetGVSBHost().strip()
+ if not gvsb or (gvsb != self.params['gvsb_host']):
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'GVSB host not found or not matching expected host!')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '75%', 'Checking GVSB server (gvsbhost) succeed: URL: '
+ + gvsb)
+ self.log.SendLine(self.test_info, info)
+
+ # check gvsb files:
+ self.bruno.dev_ssh.SendCmd('ls /tmp/gvsb*')
+ cmd_list = self.bruno.dev_ssh.GetCmdOutput()
+ gvsb_channel = False
+ gvsb_kick = False
+ gvsb_host = False
+ for line in cmd_list:
+ if 'gvsbhost' in line:
+ gvsb_host = True
+ elif 'gvsbchannel' in line:
+ gvsb_channel = True
+ elif 'gvsbkick' in line:
+ gvsb_kick = True
+ if not gvsb_channel or not gvsb_kick or not gvsb_host:
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'GVSB files missing! Check /tmp/for gvsb files')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '80%', 'Checking GVSB files (/tmp/gvsb*) succeed. Files found: '
+ 'gvsbhost, gvsbchannel, gvsbkick.')
+ self.log.SendLine(self.test_info, info)
+
+ # verify acs url:
+ if self.bruno.GetACSUrl() != self.params['acs_url']:
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'ACS URL is not expected! Expected URL: '
+ + self.params['acs_url'])
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '85%', 'Checking ACS URL (/tmp/cwmp/acs_url) succeed. URL: '
+ + self.params['acs_url'])
+ self.log.SendLine(self.test_info, info)
+
+ # verify sage properties
+ self.bruno.dev_ssh.SendCmd('ls -l /rw/sage')
+ cmd_list = self.bruno.dev_ssh.GetCmdOutput()
+ sage_prop = False
+ sage_prop_backup = False
+ for line in cmd_list:
+ if 'Sage.properties.autobackup' in line:
+ sage_prop_backup = True
+ elif 'Sage.properties' in line:
+ sage_prop = True
+ if not sage_prop or not sage_prop_backup:
+ test_failed = True
+ info = self.log.CreateErrorInfo(
+ 'critical', 'Missing file in /rw/sage folder.')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '90%', 'Checking sage properties files (/rw/sage/) succeed.')
+ self.log.SendLine(self.test_info, info)
+
+ if test_failed:
+ info = self.log.CreateResultInfo(
+ 'Failed', 'Device configuration not '
+ 'restored completely after factory reset!')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateResultInfo(
+ 'Pass', 'Device configuration '
+ 'successfully restored after factory reset.')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateProgressInfo(
+ '100%', 'Device configuration '
+ 'successfully restored after factory reset.')
+ self.log.SendLine(self.test_info, info)
+
+ def VerifyDefaultACSURL(self):
+ """Test case: 14319146. This method verifies default ACS URL on device."""
+ # this test needs to disable the Ethernet port, therefore can not
+ # be automated via ssh tunnel
+ print 'pass this test case'
+
+ def Run(self):
+ """Run the test case."""
+ ####### Add your code here -- BEGIN #######
+ print 'Test Started...'
+
+ self.Init()
+ time.sleep(2) # time delay between commands issued to Device
+ self.SshTunnel()
+ info = self.log.CreateProgressInfo(
+ '5%', 'SSH session to device successfully established!')
+ self.log.SendLine(self.test_info, info)
+
+ if self.test_info['testID'] == '11865129':
+ self.VerifyACSWithIPv6()
+ elif self.test_info['testID'] == '11721375':
+ self.VerifyFactoryReset()
+ elif self.test_info['testID'] == '14319146':
+ self.VerifyDefaultACSURL()
+ elif self.test_info['testID'] == '11721371':
+ print 'run test'
+ elif self.test_info['testID'] == '14312009':
+ print 'run test'
+ elif self.test_info['testID'] == '14186028':
+ print 'run test'
+ elif self.test_info['testID'] == '14312010':
+ print 'run test'
+ elif self.test_info['testID'] == '14247012':
+ print 'run test'
+ elif self.test_info['testID'] == '14186029':
+ print 'run test'
+ elif self.test_info['testID'] == '14312011':
+ print 'run test'
+ elif self.test_info['testID'] == '14293012':
+ print 'run test'
+ elif self.test_info['testID'] == '14165037':
+ print 'run test'
+ elif self.test_info['testID'] == '14315010':
+ print 'run test'
+ elif self.test_info['testID'] == '14247013':
+ print 'run test'
+ elif self.test_info['testID'] == '14312012':
+ print 'run test'
+ elif self.test_info['testID'] == '14293013':
+ print 'run test'
+ elif self.test_info['testID'] == '14311008':
+ print 'run test'
+ elif self.test_info['testID'] == '14293014':
+ print 'run test'
+ elif self.test_info['testID'] == '14311009':
+ print 'run test'
+ elif self.test_info['testID'] == '14186030':
+ print 'run test'
+ elif self.test_info['testID'] == '14319148':
+ print 'run test'
+ elif self.test_info['testID'] == '14266497':
+ print 'run test'
+ elif self.test_info['testID'] == '14312013':
+ print 'run test'
+ elif self.test_info['testID'] == '11874098':
+ print 'run test'
+ elif self.test_info['testID'] == '14315007':
+ print 'run test'
+ elif self.test_info['testID'] == '14247014':
+ print 'run test'
+ elif self.test_info['testID'] == '14293015':
+ print 'run test'
+ elif self.test_info['testID'] == '14293016':
+ print 'run test'
+ elif self.test_info['testID'] == '11865130':
+ print 'run test'
+ elif self.test_info['testID'] == '11722370':
+ print 'run test'
+ elif self.test_info['testID'] == '11874103':
+ print 'run test'
+ elif self.test_info['testID'] == '11843139':
+ print 'run test'
+ else:
+ print 'do nothing...'
+
+ print 'Test Completed...'
+ ####### Add your code here -- END #######
+
+ def Destructor(self):
+ """This is the destructor function to call for the user extened class."""
+ self.p_ssh.close()
+
+if __name__ == '__main__':
+ # Test cases defined in this file:
+ test_defined = ['11865129', '11721375', '14319146', '11721371',
+ '14312009', '14186028', '14312010', '14247012', '14186029',
+ '14312011', '14293012', '14165037', '14315010', '14247013',
+ '14312012', '14293013', '14311008', '14293014', '14311009',
+ '14186030', '14319148', '14266497', '14312013', '11874098',
+ '14315007', '14247014', '14293015', '14293016', '11865130',
+ '11722370', '11874103', '11843139']
+ # To execute test cases that defined in config file:
+ test_to_execute = testCase.TestCase.FindTestCaseToExecute('config.cfg')
+ for test_id in test_to_execute:
+ if test_id in test_defined:
+ test = BasicTR069Test(testID=test_id)
diff --git a/basic_network_testcases.py b/basic_network_testcases.py
new file mode 100644
index 0000000..2979227
--- /dev/null
+++ b/basic_network_testcases.py
@@ -0,0 +1,230 @@
+#!/usr/bin/python
+# Copyright 2012 Google Inc. All Rights Reserved.
+
+"""This class implements some basic networking test cases.
+
+ This class extends the TestCase Class.
+ This file implements some test cases to verify Bruno basic networking
+ features. In the following test cases, only those that are defined in
+ 'config.cfg' file will be executed.
+
+ Feature test cases include the following:
+ testID: 11874111 7.2.12 IPv6 Duplicate Address Detection
+ testID: 11724363 7.2.16 Ethernet/Moca Traffic Overloading
+ testID: 11843145 7.2.17 VLAN Pass Through
+ testID: 11723379 7.2.18 VLAN Tagging over Home Owner WiFi
+ testID: 11722378 7.2.19 VLAN Tagging over Guest WiFi
+ testID: 11721378 7.2.2 Verify BRUNO (connected to BRUNO-IS) can obtain
+ IPv4 address via RG's DHCP service
+ testID: 11792173 7.2.20 VLAN Tag Removal
+ testID: 11721382 7.2.21 IPv6 Verify subnet with /56, /64 length
+ testID: 11843141 7.2.5 Verify client can get correct IPv4 address if
+ the RG changes DHCP address pool
+ testID: 11721379 7.2.7 Verify time offset information obtained via
+ DHCP option2
+"""
+
+__author__ = 'Lehan Meng (lmeng@google.com)'
+
+
+import re
+import time
+
+import device
+import ip
+import ssh
+import testCase
+
+
+class BasicNetworkingTest(testCase.TestCase):
+ """Class for test cases of Bruno networking features.
+
+ Configuration Parameters:
+ testID: 11874111/11724363/11843145/11723379
+ /11722378/11721378/11792173/11721382/
+ /11843141/11721379
+ addr = Bruno's IP address
+ user = root
+ pwd = google
+ bruno_prompt = gfibertv#
+ file = cmdOutput.txt
+ title = Basic_Networking
+ """
+
+ def Init(self):
+ """Initialize the networking feature tests."""
+ # Get all device information.
+ info = self.log.CreateResultInfo(
+ '---', 'Networking test verification started...')
+ self.log.SendLine(self.test_info, info)
+ self.bruno = device.Bruno(user=self.params['user'],
+ addr=self.params['addr'],
+ bruno_prompt=self.params['bruno_prompt'],
+ addr_ipv6=self.params['addr_ipv6'])
+ self.bruno.SetLogging(self.log)
+ if not self.p_ssh:
+ self.p_ssh = ssh.SSH(user=self.params['user'],
+ addr=self.params['addr'],
+ bruno_prompt=self.params['bruno_prompt'],
+ addr_ipv6=self.params['addr_ipv6'])
+ self.bruno.dev_ssh = self.p_ssh
+ self.p_ssh.SetLogging(self.log)
+ return
+
+ def SshTunnel(self):
+ """Create ssh tunnel for the test."""
+ self.p_ssh.SshRetry(5, 15)
+ return
+
+ def VerifyDuplicatedAddress(self, ip_addr):
+ """Verify device can detect duplicated ipv6 address existing in network.
+
+ Set a duplicated ip address on the device.
+ Verify that the device can detect duplicated ip address
+
+ Args:
+ ip_addr: the duplicated address
+ Returns:
+ True: if duplicated detected
+ False: if duplicated address is not detected
+ """
+ time_stamp = self.bruno.GetTimeStamp()
+ self.bruno.dev_ssh.SendCmd('ip -6 addr add ' + ip_addr + '/64 dev br0')
+ time.sleep(1)
+ self.bruno.dev_ssh.SendCmd('dmesg | grep detected')
+ log_list = self.bruno.dev_ssh.GetCmdOutput()
+
+ pattern_line = ('\[\s*\d*\.\d{3}\]\s*\w*\d:\s* IPv6 duplicate address\s*('
+ + ip_addr + ')\s*[/\d+]*\s*detected!')
+ self.bruno.dev_ssh.SendCmd('ip -6 addr del ' + ip_addr + '/64 dev br0')
+
+ if self.bruno.FindLineInLog(log_list, time_stamp, pattern_line):
+ info = self.log.CreateResultInfo('Pass', 'Duplicated IPv6 address '
+ 'detected: ' + ip_addr)
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateResultInfo('Failed',
+ 'Duplicated IPv6 address not detected: '
+ + ip_addr + '. Verification Failed!')
+ self.log.SendLine(self.test_info, info)
+ #[132344.324] br0: IPv6 duplicate address fe80:... detected!
+
+ def GetIPv4Address(self):
+ """Test Bruno IP address. testID: 11874104."""
+ # Use ifconfig to check interface IP address
+ self.p_ssh.SendCmd(r'ifconfig br0')
+ line = self.p_ssh.GetCmdOutput(3)[1]
+
+ # Get Bruno IP address
+ address = ip.IPADDR(line).IsLikeIpv4Address()
+ if address is not None:
+ ip.IPADDR(address).IsValidIpv4Address()
+ info = self.log.CreateProgressInfo('---',
+ 'Verify IP: Bruno has IP address '
+ + address)
+ self.log.SendLine(self.test_info, info)
+ return address
+ else:
+ info = self.log.CreateErrorInfo('critical',
+ 'Error! Bruno does not have IP address!'
+ ' Exit!')
+ self.log.SendLine(self.test_info, info)
+ return False
+
+ def GetNetMask(self):
+ """Get Netmask from device."""
+ self.p_ssh.SendCmd(r'ifconfig br0')
+ line = self.p_ssh.GetCmdOutput(3)[1]
+
+ netmask = re.search(r'mask [0-9]+(?:\.[0-9]+){3}', line).group()
+ address = ip.IPADDR(netmask).IsLikeIpv4Address()
+ if address is not None:
+ ip.IPADDR(address).IsValidIpv4Address()
+ info = self.log.CreateProgressInfo('---',
+ 'Verify IP: Bruno has netmask: '
+ + address)
+ self.log.SendLine(self.test_info, info)
+ return address
+ else:
+ info = self.log.CreateErrorInfo('critical',
+ 'Error! Bruno does not have netmask!'
+ ' Exit!')
+ self.log.SendLine(self.test_info, info)
+ return False
+
+ def VerifyIPv6(self):
+ """Test Bruno IPv6 address. testID: 11722376."""
+ # Use ip addr to check interface IPv6 address
+ self.p_ssh.SendCmd(r'ip addr show dev br0')
+
+ for line in reversed(self.p_ssh.p_ssh.before.splitlines()[-10:]):
+ ipv6_addr = re.search('inet6.*dynamic', line)
+ # Get Bruno IPv6 address
+ if ipv6_addr is not None:
+ info = self.log.CreateProgressInfo('---',
+ 'Verify IPv6: '
+ 'Bruno has IPv6 address: '
+ + ipv6_addr.group())
+ self.log.SendLine(self.test_info, info)
+ break
+ else:
+ info = self.log.CreateErrorInfo('intermediate',
+ 'Wanring! '
+ 'Bruno did not have IPv6 address')
+ self.log.SendLine(self.test_info, info)
+ return
+
+ def VerifyDHCPOverBrunoIS(self):
+ """Verify Bruno (connected to Bruno-IS) can get IPv4 address from RG.
+
+ test ID: 11721378
+ This test requires thinBruno is connected to FatBruno, then to RG.
+ """
+ ip_addr = self.GetIPv4Address()
+ if ip_addr:
+ info = self.log.CreateResultInfo('Pass', 'Bruno can get IP address from '
+ 'RG while connecting to Bruno-IS. '
+ 'IP address: '+ ip_addr)
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateResultInfo('Failed',
+ 'Bruno cannot get IP address from '
+ 'RG. Verification Failed!')
+ self.log.SendLine(self.test_info, info)
+
+ def Run(self):
+ """Run the test case."""
+ ####### Add your code here -- BEGIN #######
+ print 'Test Started...'
+
+ self.Init()
+ time.sleep(2) # time delay between commands issued to Device
+ self.SshTunnel()
+
+ self.VerifyIPv6()
+
+ if self.test_info['testID'] == '11874111':
+ ip_addr = self.params['addr_ipv6_dup']
+ self.VerifyDuplicatedAddress(ip_addr)
+ elif self.test_info['testID'] == '11721378':
+ self.VerifyDHCPOverBrunoIS()
+
+ # TODO(lmeng): Test cases: '11724363', '11843145', '11723379', '11722378',
+ # '11792173', '11721382', '11843141', '11721379'
+
+ print 'Test Completed...'
+ ####### Add your code here -- END #######
+
+ def Destructor(self):
+ """This is the destructor function to call for the user extened class."""
+ self.p_ssh.close()
+
+if __name__ == '__main__':
+ # Test cases defined in this file:
+ test_defined = ['11874111', '11724363', '11843145', '11723379', '11722378',
+ '11721378', '11792173', '11721382', '11843141', '11721379']
+ # To execute test cases that defined in config file:
+ test_to_execute = testCase.TestCase.FindTestCaseToExecute('config.cfg')
+ for test_id in test_to_execute:
+ if test_id in test_defined:
+ test = BasicNetworkingTest(testID=test_id)
diff --git a/checkall b/checkall
new file mode 100755
index 0000000..bd5aa40
--- /dev/null
+++ b/checkall
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Display a one-line overview of whether each device is up.
+#
+mydir=$(dirname "$0")
+cd "$mydir"
+hosts=$(./config)
+
+out=
+./run $hosts -- 'ping -c1 -w1 -n $ip >/dev/null && echo yes || echo no' |
+sort | (
+ while read name ok; do
+ case "$ok" in
+ yes) out="$out${name%:} " ;;
+ no) out="$out^${name%:} " ;;
+ *) ( echo; echo "Unexpected: '$name' '$ok'" ) >&2 ;;
+ esac
+ done
+ echo "$out"
+)
diff --git a/config.cfg b/config.cfg
index 6659a21..ea3fef7 100644
--- a/config.cfg
+++ b/config.cfg
@@ -1,39 +1,126 @@
-##############################################
-# This is the config file for the test cases #
-# #
-# All test cases share the same config file. #
-# Each test case configuration starts with #
-# the testID of that test case. #
-# #
-# comment line starts with '#' and will not #
-# be parsed as configuration information #
-##############################################
-testID: 11843140
-user = root
-addr = 192.168.1.4
-addr_ipv6 = 2605:a601:15:9001:21a:11ff:fe30:64d5
-pwd = google
-bruno_prompt = gfibertv#
-title =Image_Download
-expected_version_id=481109
-expected_version=bruno-jaguar-3
-downgrade_version=bruno-koala-2
-downgrade_version_id=662005
-ACS_URL = https://gfiber-acs-staging.appspot.com/cwmp
-# if authentication server is required to access the device
-jump_server = jmp.googlefiber.net
-# if athena server is required to access the device:
-athena_user = lmeng
-athena_pwd = changeme
+{
+ "Image_Download":{
+ "comment":["this is a comment line",
+ "this configuration profile is for ImageDownload test case",
+ "the acs_path is the google3 repository where upgrade_test.py locates. This test depends on it to modify ACS datastore."],
+ "testID": ["11843140", "14165036"],
+ "title" : {"11843140":"Image_Download", "14165036":"DownloadInterrupt"},
+ "user" : "root",
+ "addr" : "192.168.1.4",
+ "addr_ipv6" : "2605:a601:fe00:fd18:21a:11ff:fe30:64e6",
+ "pwd" : "google",
+ "bruno_prompt" : "gfibertv#",
+ "expected_version_id": "987010",
+ "expected_version": "bruno-monkey-4",
+ "downgrade_version": "bruno-monkey-3",
+ "downgrade_version_id": "1038003",
+ "acs_url" : "https://gfiber-acs-staging.appspot.com/cwmp",
+ "acs_path": "/home/lmeng/git_project/google3/isp/gfiber/testing/automated_tests/upgrade_test/",
+ "acs_app_id": "gfiber-acs-staging",
+ "acs_host_name": "gfiber-acs-staging.appspot.com",
+ "jump_server" : "jmp.googlefiber.net",
+ "athena_user" : "your_uname",
+ "athena_pwd" : "your_pwd",
+ "profile_id" : "557001"
+ },
+ "basic_network_testcases":{
+ "comment":["addr_ipv6_dup: the duplicated address to set on device, in",
+ "or der to detect the address duplication error."],
+ "testID": ["11874111", "11721378"],
+ "title" : {"11874111":"DuplicateIPv6Addr", "11721378":"DHCPv4OverBruno-IS"},
+ "user" : "root",
+ "addr" : "192.168.1.4",
+ "addr_ipv6" : "2605:a601:fe00:fd18:21a:11ff:fe30:64e6",
+ "bruno_prompt" : "gfibertv#",
+ "expected_version_id": "987010",
+ "expected_version": "bruno-monkey-4",
+ "addr_ipv6_dup":"2605:a601:fe00:fd18:21a:11ff:fe30:6383"
+ },
+
+ "dataModel":{
+ "testID": ["14187024", "14165033", "14315008", "14165035", "14314007",
+ "11865133", "14335001", "14266498", "14319149", "14314008",
+ "11722375", "14315009", "14314009", "14165038", "15643845"],
+ "title" : {"14187024":"TR140_DataModel",
+ "14165033":"GVSB_DataModel",
+ "14315008":"TR098_DataModel",
+ "14165035":"MoCA_DataModel",
+ "14314007":"TR181_DataModel",
+ "11865133":"TR135_DataModel",
+ "14335001":"X_CATAWAMPUS-ORG_DataModel",
+ "14266498":"FlashDevice_DataModel",
+ "14319149":"X_GOOGLE-COM_DataModel",
+ "14314008":"TR157_DataModel",
+ "11722375":"SetParameterValuesRPC",
+ "14315009":"ManagementServerURL",
+ "14314009":"MonitorTemperature",
+ "14165038":"AutoChannelEnable",
+ "15643845":"NTPTimeZoneConfig"},
+ "user" : "root",
+ "addr" : "192.168.1.4",
+ "addr_ipv6" : "2605:a601:fe00:fd18:21a:11ff:fe30:64e6",
+ "req_file" : "data/DataModel_requirement.cfg",
+ "bruno_prompt" : "gfibertv#",
+ "expected_version_id": "959004",
+ "expected_version": "bruno-monkey-2",
+ "acs_url" : "https://gfiber-acs-staging.appspot.com/cwmp",
+ "nbi_client_path" : "/home/lmeng/git_project/google3/blaze-bin/java/com/google/fiber/provisioning/acs/client/nbi/nbi_client",
+ "para_tree_file" : "ParameterTree.xml",
+ "from_which_param_to_set": "None",
+ "param_path" : "."
+ },
+
+ "dataModel_WiFi":{
+ "comment":["wifi_params type: e.g., string_value, is the data type accepted by nbi_client RPC call."],
+ "testID": ["14165034"],
+ "title" : {"14165034":"WiFi_configuration"},
+ "user" : "root",
+ "addr" : "192.168.1.4",
+ "addr_ipv6" : "2605:a601:fe00:fd18:21a:11ff:fe30:64e6",
+ "bruno_prompt" : "gfibertv#",
+ "expected_version_id": "987010",
+ "expected_version": "bruno-monkey-4",
+ "acs_url" : "https://gfiber-acs-staging.appspot.com/cwmp",
+ "nbi_client_path" : "/home/lmeng/git_project/google3/blaze-bin/java/com/google/fiber/provisioning/acs/client/nbi/nbi_client",
+ "account_id":"5438577436",
+ "device_label":"0101209088BC",
+ "wifi_params":{
+ "Google.Wifi.SSID":{"string_value":"Home"},
+ "Google.Wifi.Home.SSID_2GHz":{"string_value":"Home"},
+ "Google.Wifi.Enable":{"boolean_value":"true"},
+ "Google.Wifi.BeaconType":{"string_value": "Basic"},
+ "Google.Wifi.BasicEncryptionModes":{"string_value":"WEPEncryption"}}
+ },
+ "basic_tr069_test_case":{
+ "comment":["Factory_reset test case needs to manually reset the device"],
+ "testID": ["11865129", "11721375"],
+ "title" : {"11865129":"IPv6_Connection",
+ "11721375":"Factory_Reset"
+ },
+ "user" : "root",
+ "addr" : {"value":"192.168.1.4", "comment":"device ipv4 address"},
+ "expected_version": "bruno-monkey-4",
+ "addr_ipv6" : {"value":"2605:a601:fe00:fd18:21a:11ff:fe30:6383",
+ "comment":"device ipv6 address"},
+ "acs_ipv6" : "2001:4860:8005::8d",
+ "gvsb_host": "https://gvsb.e2e.gfsvc.com/rpc",
+ "acs_url" : "https://acs.e2e.gfsvc.com/cwmp",
+ "bruno_prompt" : "gfibertv#"
+ },
+
+ "blocked_test_case":{
+ "testID": ["14314009", "14165038"],
+ "title" : {"14314009":"MonitorTemperature",
+ "14165038":"AutoChannelEnable"},
+ "user" : "root",
+ "addr" : "192.168.1.4",
+ "addr_ipv6" : "2605:a601:fe00:fd18:21a:11ff:fe30:64e6",
+ "bruno_prompt" : "gfibertv#"
+ }
+}
-#testID: 14187024
-#title =DataModel
-#rq_file = 14187024-requirement.req
-#device_mURL = http://[2607:f8b0:400b:800::1011]/
-#device_IP = 2607:f8b0:400b:800::1011
-#serial_NO =
diff --git a/configs/core.py b/configs/core.py
index 1f751a1..f85e2fa 100644
--- a/configs/core.py
+++ b/configs/core.py
@@ -35,7 +35,7 @@
if ival == False: ival = None
if value == False: value = None
if value == True and ival:
- return True # any nonempty value is true, so match it
+ continue # any nonempty value is true, so match it
if ival != value:
return False
return True
diff --git a/configs/core_test.py b/configs/core_test.py
index db97834..21b1f5f 100644
--- a/configs/core_test.py
+++ b/configs/core_test.py
@@ -14,20 +14,33 @@
def testConfigCore():
hosts = core.Hosts()
WVPASSEQ(len(hosts), 0)
- h = hosts.FindOrAdd(ether='11:22:33:44:55:66')
- h.Set(name='testname', ip='1.2.3.4')
- h.platform = 'bob'
+ h1 = hosts.FindOrAdd(ether='11:22:33:44:55:66')
+ h1.Set(name='testname', ip='1.2.3.4')
+ h1.platform = 'bob'
WVPASSEQ(len(hosts), 1)
- h2 = hosts.FindOrAdd(ip='1.2.3.4')
+ h1b = hosts.FindOrAdd(ip='1.2.3.4')
WVPASSEQ(len(hosts), 1)
- WVPASSEQ(h2.name, 'testname')
- WVPASSEQ(h2.ether, '11:22:33:44:55:66')
- WVPASSEQ(h, h2)
+ WVPASSEQ(h1b.name, 'testname')
+ WVPASSEQ(h1b.ether, '11:22:33:44:55:66')
+ WVPASSEQ(h1, h1b)
# test Host.Query()
- WVPASSEQ(hosts.Query(ip=True), [h])
+ WVPASSEQ(hosts.Query(ip=True), [h1])
WVPASSEQ(hosts.Query(ip=False), [])
- WVPASSEQ(hosts.Query(ip='1.2.3.4'), [h])
+ WVPASSEQ(hosts.Query(ip='1.2.3.4'), [h1])
WVPASSEQ(hosts.Query(ip='1.2.3.5'), [])
WVPASSEQ(hosts.Query(ip='1.2.3.4', maxcount=0), [])
WVEXCEPT(core.QueryError, hosts.Query, ip='1.2.3.5', mincount=1)
+
+ # add a second host
+ h2 = hosts.FindOrAdd(ether='22:33:44:22:33:44')
+ h2.Set(name='test2')
+
+ WVPASSEQ(len(hosts), 2)
+ WVPASSEQ(hosts.Query(ip='1.2.3.4'), [h1])
+ WVPASSEQ(hosts.Query(ip=None), [h2])
+ WVPASSEQ(hosts.Query(name='test2'), [h2])
+ WVPASSEQ(hosts.Query(ip=True), [h1])
+ WVPASSEQ(sorted(hosts.Query(name=True)), sorted([h1, h2]))
+ WVPASSEQ(hosts.Query(ether=True, name='test2'), [h2])
+ WVPASSEQ(hosts.Query(ether=True, name='testname'), [h1])
diff --git a/data/DataModel_requirement.cfg b/data/DataModel_requirement.cfg
new file mode 100644
index 0000000..35f5a43
--- /dev/null
+++ b/data/DataModel_requirement.cfg
@@ -0,0 +1,296 @@
+Device.DeviceInfo.AdditionalHardwareVersion
+Device.DeviceInfo.AdditionalSoftwareVersion
+Device.DeviceInfo.Description
+Device.DeviceInfo.HardwareVersion
+Device.DeviceInfo.Manufacturer
+Device.DeviceInfo.ManufacturerOUI
+Device.DeviceInfo.MemoryStatus.Free
+Device.DeviceInfo.MemoryStatus.Total
+Device.DeviceInfo.ModelName
+Device.DeviceInfo.ProcessStatus.CPUUsage
+Device.DeviceInfo.ProcessStatus.Process.{i}.CPUTime
+Device.DeviceInfo.ProcessStatus.Process.{i}.Command
+Device.DeviceInfo.ProcessStatus.Process.{i}.PID
+Device.DeviceInfo.ProcessStatus.Process.{i}.Priority
+Device.DeviceInfo.ProcessStatus.Process.{i}.Size
+Device.DeviceInfo.ProcessStatus.Process.{i}.State
+Device.DeviceInfo.ProcessStatus.ProcessNumberOfEntries
+Device.DeviceInfo.ProcessorNumberOfEntries
+Device.DeviceInfo.ProductClass
+Device.DeviceInfo.SerialNumber
+Device.DeviceInfo.SoftwareVersion
+Device.DeviceInfo.SupportedDataModelNumberOfEntries
+Device.DeviceInfo.TemperatureStatus.TemperatureSensorNumberOfEntries
+Device.DeviceInfo.UpTime
+Device.DeviceInfo.VendorConfigFileNumberOfEntries
+Device.DeviceInfo.VendorLogFileNumberOfEntries
+Device.Ethernet.Interface.{i}.DuplexMode
+Device.Ethernet.Interface.{i}.Enable
+Device.Ethernet.Interface.{i}.LastChange
+Device.Ethernet.Interface.{i}.LowerLayers
+Device.Ethernet.Interface.{i}.MACAddress
+Device.Ethernet.Interface.{i}.MaxBitRate
+Device.Ethernet.Interface.{i}.Name
+Device.Ethernet.Interface.{i}.Stats.BroadcastPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.BroadcastPacketsSent
+Device.Ethernet.Interface.{i}.Stats.BytesReceived
+Device.Ethernet.Interface.{i}.Stats.BytesSent
+Device.Ethernet.Interface.{i}.Stats.DiscardPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.DiscardPacketsSent
+Device.Ethernet.Interface.{i}.Stats.ErrorsReceived
+Device.Ethernet.Interface.{i}.Stats.ErrorsSent
+Device.Ethernet.Interface.{i}.Stats.MulticastPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.MulticastPacketsSent
+Device.Ethernet.Interface.{i}.Stats.PacketsReceived
+Device.Ethernet.Interface.{i}.Stats.PacketsSent
+Device.Ethernet.Interface.{i}.Stats.UnicastPacketsReceived
+Device.Ethernet.Interface.{i}.Stats.UnicastPacketsSent
+Device.Ethernet.Interface.{i}.Stats.UnknownProtoPacketsReceived
+Device.Ethernet.Interface.{i}.Status
+Device.Ethernet.Interface.{i}.Upstream
+Device.Ethernet.Interface.{i}.X_CATAWAMPUS-ORG_ActualBitRate
+Device.Ethernet.Interface.{i}.X_CATAWAMPUS-ORG_ActualDuplexMode
+Device.Ethernet.InterfaceNumberOfEntries
+Device.Ethernet.LinkNumberOfEntries
+Device.Ethernet.VLANTerminationNumberOfEntries
+Device.InterfaceStackNumberOfEntries
+Device.ManagementServer.CWMPRetryIntervalMultiplier
+Device.ManagementServer.CWMPRetryMinimumWaitInterval
+Device.ManagementServer.ConnectionRequestPassword
+Device.ManagementServer.ConnectionRequestURL
+Device.ManagementServer.ConnectionRequestUsername
+Device.ManagementServer.DefaultActiveNotificationThrottle
+Device.ManagementServer.EnableCWMP
+Device.ManagementServer.ManageableDeviceNumberOfEntries
+Device.ManagementServer.ParameterKey
+Device.ManagementServer.Password
+Device.ManagementServer.PeriodicInformEnable
+Device.ManagementServer.PeriodicInformInterval
+Device.ManagementServer.PeriodicInformTime
+Device.ManagementServer.STUNEnable
+Device.ManagementServer.URL
+Device.ManagementServer.UpgradesManaged
+Device.ManagementServer.Username
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.MACAddress
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.NodeID
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PHYRxRate
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PHYTxRate
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PacketAggregationCapability
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.PreferredNC
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.QAM256Capable
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxBcastPowerLevel
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxErroredAndMissedPackets
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxPackets
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxPowerLevel
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.RxSNR
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.TxBcastRate
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.TxPackets
+Device.MoCA.Interface.{i}.AssociatedDevice.{i}.TxPowerControlReduction
+Device.MoCA.Interface.{i}.AssociatedDeviceNumberOfEntries
+Device.MoCA.Interface.{i}.BackupNC
+Device.MoCA.Interface.{i}.CurrentOperFreq
+Device.MoCA.Interface.{i}.CurrentVersion
+Device.MoCA.Interface.{i}.Enable
+Device.MoCA.Interface.{i}.FirmwareVersion
+Device.MoCA.Interface.{i}.HighestVersion
+Device.MoCA.Interface.{i}.LastChange
+Device.MoCA.Interface.{i}.LastOperFreq
+Device.MoCA.Interface.{i}.LowerLayers
+Device.MoCA.Interface.{i}.MACAddress
+Device.MoCA.Interface.{i}.Name
+Device.MoCA.Interface.{i}.NetworkCoordinator
+Device.MoCA.Interface.{i}.NodeID
+Device.MoCA.Interface.{i}.PacketAggregationCapability
+Device.MoCA.Interface.{i}.PrivacyEnabled
+Device.MoCA.Interface.{i}.QAM256Capable
+Device.MoCA.Interface.{i}.Stats.BroadcastPacketsReceived
+Device.MoCA.Interface.{i}.Stats.BroadcastPacketsSent
+Device.MoCA.Interface.{i}.Stats.BytesReceived
+Device.MoCA.Interface.{i}.Stats.BytesSent
+Device.MoCA.Interface.{i}.Stats.DiscardPacketsReceived
+Device.MoCA.Interface.{i}.Stats.DiscardPacketsSent
+Device.MoCA.Interface.{i}.Stats.ErrorsReceived
+Device.MoCA.Interface.{i}.Stats.ErrorsSent
+Device.MoCA.Interface.{i}.Stats.MulticastPacketsReceived
+Device.MoCA.Interface.{i}.Stats.MulticastPacketsSent
+Device.MoCA.Interface.{i}.Stats.PacketsReceived
+Device.MoCA.Interface.{i}.Stats.PacketsSent
+Device.MoCA.Interface.{i}.Stats.UnicastPacketsReceived
+Device.MoCA.Interface.{i}.Stats.UnicastPacketsSent
+Device.MoCA.Interface.{i}.Stats.UnknownProtoPacketsReceived
+Device.MoCA.Interface.{i}.Status
+Device.MoCA.Interface.{i}.Upstream
+Device.MoCA.InterfaceNumberOfEntries
+Device.PeriodicStatistics.MaxReportSamples
+Device.PeriodicStatistics.MinSampleInterval
+Device.PeriodicStatistics.SampleSet.{i}.Enable
+Device.PeriodicStatistics.SampleSet.{i}.FetchSamples
+Device.PeriodicStatistics.SampleSet.{i}.Name
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.CalculationMode
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Enable
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Failures
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.HighThreshold
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.LowThreshold
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Reference
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleMode
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleSeconds
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SuspectData
+Device.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Values
+Device.PeriodicStatistics.SampleSet.{i}.ParameterNumberOfEntries
+Device.PeriodicStatistics.SampleSet.{i}.ReportEndTime
+Device.PeriodicStatistics.SampleSet.{i}.ReportSamples
+Device.PeriodicStatistics.SampleSet.{i}.ReportStartTime
+Device.PeriodicStatistics.SampleSet.{i}.SampleInterval
+Device.PeriodicStatistics.SampleSet.{i}.SampleSeconds
+Device.PeriodicStatistics.SampleSet.{i}.Status
+Device.PeriodicStatistics.SampleSet.{i}.TimeReference
+Device.PeriodicStatistics.SampleSetNumberOfEntries
+Device.Services.STBService.{i}.Components.FrontEnd.{i}.IP.IGMP.ClientGroup.{i}.GroupAddress
+Device.Services.STBService.{i}.Components.FrontEnd.{i}.IP.IGMP.ClientGroupNumberOfEntries
+Device.Services.STBService.{i}.Components.FrontEndNumberOfEntries
+Device.Services.STBServiceNumberOfEntries
+Device.Services.StorageServices.Capabilities.FTPCapable
+Device.Services.StorageServices.Capabilities.HTTPCapable
+Device.Services.StorageServices.Capabilities.HTTPSCapable
+Device.Services.StorageServices.Capabilities.HTTPWritable
+Device.Services.StorageServices.Capabilities.SFTPCapable
+Device.Services.StorageServices.Capabilities.SupportedFileSystemTypes
+Device.Services.StorageServices.Capabilities.SupportedNetworkProtocols
+Device.Services.StorageServices.Capabilities.SupportedRaidTypes
+Device.Services.StorageServices.Capabilities.VolumeEncryptionCapable
+Device.Services.StorageServices.Enable
+Device.Services.StorageServices.LogicalVolume.{i}.Capacity
+Device.Services.StorageServices.LogicalVolume.{i}.Enable
+Device.Services.StorageServices.LogicalVolume.{i}.FileSystem
+Device.Services.StorageServices.LogicalVolume.{i}.FolderNumberOfEntries
+Device.Services.StorageServices.LogicalVolume.{i}.Name
+Device.Services.StorageServices.LogicalVolume.{i}.Status
+Device.Services.StorageServices.LogicalVolume.{i}.ThresholdLimit
+Device.Services.StorageServices.LogicalVolume.{i}.UsedSpace
+Device.Services.StorageServices.LogicalVolume.{i}.X_CATAWAMPUS-ORG_ReadOnly
+Device.Services.StorageServices.LogicalVolumeNumberOfEntries
+Device.Services.StorageServices.PhysicalMedium.{i}.Capacity
+Device.Services.StorageServices.PhysicalMedium.{i}.ConnectionType
+Device.Services.StorageServices.PhysicalMedium.{i}.FirmwareVersion
+Device.Services.StorageServices.PhysicalMedium.{i}.Health
+Device.Services.StorageServices.PhysicalMedium.{i}.HotSwappable
+Device.Services.StorageServices.PhysicalMedium.{i}.Model
+Device.Services.StorageServices.PhysicalMedium.{i}.Name
+Device.Services.StorageServices.PhysicalMedium.{i}.Removable
+Device.Services.StorageServices.PhysicalMedium.{i}.SMARTCapable
+Device.Services.StorageServices.PhysicalMedium.{i}.SerialNumber
+Device.Services.StorageServices.PhysicalMedium.{i}.Vendor
+Device.Services.StorageServices.PhysicalMediumNumberOfEntries
+Device.Services.StorageServices.StorageArrayNumberOfEntries
+Device.Services.StorageServices.UserAccountNumberOfEntries
+Device.Services.StorageServices.UserGroupNumberOfEntries
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.BadEraseBlocks
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.CorrectedErrors
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.EraseBlockSize
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.IOSize
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.MaxEraseCount
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.Name
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.ReservedEraseBlocks
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolume.{i}.DataBytes
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolume.{i}.Name
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolume.{i}.Status
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.SubVolumeNumberOfEntries
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.TotalEraseBlocks
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMedia.{i}.UncorrectedErrors
+Device.Services.StorageServices.X_CATAWAMPUS-ORG_FlashMediaNumberOfEntries
+InternetGatewayDevice.DeviceInfo.AdditionalHardwareVersion
+InternetGatewayDevice.DeviceInfo.AdditionalSoftwareVersion
+InternetGatewayDevice.DeviceInfo.Description
+InternetGatewayDevice.DeviceInfo.HardwareVersion
+InternetGatewayDevice.DeviceInfo.Manufacturer
+InternetGatewayDevice.DeviceInfo.ManufacturerOUI
+InternetGatewayDevice.DeviceInfo.ModelName
+InternetGatewayDevice.DeviceInfo.ModemFirmwareVersion
+InternetGatewayDevice.DeviceInfo.ProductClass
+InternetGatewayDevice.DeviceInfo.SerialNumber
+InternetGatewayDevice.DeviceInfo.SoftwareVersion
+InternetGatewayDevice.DeviceInfo.SpecVersion
+InternetGatewayDevice.DeviceInfo.UpTime
+InternetGatewayDevice.DeviceInfo.VendorConfigFileNumberOfEntries
+InternetGatewayDevice.LANDevice.{i}.LANEthernetInterfaceNumberOfEntries
+InternetGatewayDevice.LANDevice.{i}.LANUSBInterfaceNumberOfEntries
+InternetGatewayDevice.LANDevice.{i}.LANWLANConfigurationNumberOfEntries
+InternetGatewayDevice.LANDeviceNumberOfEntries
+InternetGatewayDevice.ManagementServer.ConnectionRequestPassword
+InternetGatewayDevice.ManagementServer.ConnectionRequestURL
+InternetGatewayDevice.ManagementServer.ConnectionRequestUsername
+InternetGatewayDevice.ManagementServer.DefaultActiveNotificationThrottle
+InternetGatewayDevice.ManagementServer.EnableCWMP
+InternetGatewayDevice.ManagementServer.ParameterKey
+InternetGatewayDevice.ManagementServer.Password
+InternetGatewayDevice.ManagementServer.PeriodicInformEnable
+InternetGatewayDevice.ManagementServer.PeriodicInformInterval
+InternetGatewayDevice.ManagementServer.PeriodicInformTime
+InternetGatewayDevice.ManagementServer.URL
+InternetGatewayDevice.ManagementServer.UpgradesManaged
+InternetGatewayDevice.ManagementServer.Username
+InternetGatewayDevice.PeriodicStatistics.MaxReportSamples
+InternetGatewayDevice.PeriodicStatistics.MinSampleInterval
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Enable
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.FetchSamples
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Name
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.CalculationMode
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Enable
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Failures
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.HighThreshold
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.LowThreshold
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Reference
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleMode
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SampleSeconds
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.SuspectData
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Parameter.{i}.Values
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ParameterNumberOfEntries
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ReportEndTime
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ReportSamples
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.ReportStartTime
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.SampleInterval
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.SampleSeconds
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.Status
+InternetGatewayDevice.PeriodicStatistics.SampleSet.{i}.TimeReference
+InternetGatewayDevice.PeriodicStatistics.SampleSetNumberOfEntries
+InternetGatewayDevice.Time.CurrentLocalTime
+InternetGatewayDevice.Time.Enable
+InternetGatewayDevice.Time.LocalTimeZoneName
+InternetGatewayDevice.WANDeviceNumberOfEntries
+TraceRoute.DSCP
+TraceRoute.DataBlockSize
+TraceRoute.DiagnosticsState
+TraceRoute.Host
+TraceRoute.Interface
+TraceRoute.MaxHopCount
+TraceRoute.NumberOfTries
+TraceRoute.ResponseTime
+TraceRoute.RouteHopsNumberOfEntries
+TraceRoute.Timeout
+X_CATAWAMPUS-ORG_CATAWAMPUS.RuntimeEnvInfo
+X_GOOGLE-COM_GFIBERTV.Mailbox.Name
+X_GOOGLE-COM_GFIBERTV.Mailbox.Node
+X_GOOGLE-COM_GFIBERTV.Mailbox.NodeList
+X_GOOGLE-COM_GFIBERTV.Mailbox.Value
+X_GOOGLE-COM_GVSB.EpgPrimary
+X_GOOGLE-COM_GVSB.EpgSecondary
+X_GOOGLE-COM_GVSB.GvsbChannelLineup
+X_GOOGLE-COM_GVSB.GvsbKick
+X_GOOGLE-COM_GVSB.GvsbServer
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.MPEG2TSStats.TSPacketsReceived
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.MPEG2TSStats.PacketDiscontinuityCounter
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.DejitteringStats.EmptyBufferTime
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.DejitteringStats.Overruns
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.DejitteringStats.Underruns
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.TCPStats.PacketsReceived
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.TCPStats.PacketsRetransmitted
+STBService.{i}.ServiceMonitoring.MainStream.{i}.Total.TCPStats.BytesReceived
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.Status
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.SupportedResolutions
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.PreferredResolution
+STBService.{i}.Components.HDMI.{i}.ResolutionValue
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.EEDID
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.AutoLipSyncSupport
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.VideoLatency
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.HDMI3DPresent
+STBService.{i}.Components.HDMI.{i}.DisplayDevice.CECSupport
diff --git a/dataModel.py b/dataModel.py
new file mode 100755
index 0000000..af45a0b
--- /dev/null
+++ b/dataModel.py
@@ -0,0 +1,1430 @@
+#!/usr/bin/python
+# Copyright 2012 Google Inc. All Rights Reserved.
+
+"""This test performs TR069 related DataModel verification.
+
+This class extends the TestCase class, it does the following:
+ 1. Configure/Verify Bruno with correct TR69 parameters:
+ - ACS URL
+ - Image Version
+ 2. Verify Bruno has connection to ACS (IPv6/IPv4)
+ 3. Set/Get TR69 data model parameters:
+ - getParameterNames()
+ - getParameterValues()
+ 4. Data mode:
+ - Data Models for GVSB
+ - Data Models for TR098
+ - Data Models for MoCA
+ - Data Models for TR140
+ - Data Models for TR181
+ 5. This file includes the following test cases:
+ (all data models test case are pending on bug: http://b/issue?id=6813492
+ SetParameterValues does not work currently)
+ - testID: 14165033 7.6.13 Support of Data Models for GVSB (TBD)
+ - testID: 14315008 7.6.14 Support of Data Models for TR098
+ - testID: 14165035 7.6.15 Support of Data Models for MoCA
+ - testID: 14187024 7.6.16 Support of Data Models for TR140
+ - testID: 14314007 7.6.17 Support of Data Models for TR181
+ - testID: 11865133 7.6.9.1 Support of Data Models: TR135
+ - testID: 14335001 7.6.31 Implement X_CATAWAMPUS-ORG parameters
+ - testID: 14319149 7.6.29 Implement X_GOOGLE-COM data model for Sage
+ information
+ - testID: 14314008 7.6.30 Implement tr-157 PeriodicStatistics
+
+ - testID: 14315009 7.6.20 Support of modification of
+ InternetGatewayDevice.ManagementServer.URL
+ - testID: 14314009 7.6.35 Monitor temperature
+ - testID: 14266498 7.6.36 X_CATAWAMPUS-ORG_FlashDevice Storage data
+ model (for tracking flash wear)
+ - testID: 14165038 7.6.37 Implement AutoChannelEnable in catawampus
+ - testID: 11722375 7.6.6 TR069 - SetParameter RPC Request
+ - testID: 15643845 7.6.39 NTP-Time zone configuration
+ - testID: 14165034 7.6.14.1 WiFi Test: ACS configuration
+"""
+
+__author__ = 'Lehan Meng (lmeng@google.com)'
+
+
+import os
+import re
+import string
+import time
+import xml.etree.ElementTree as ET
+
+import acs
+import device
+import ssh
+import testCase
+
+
+class DataModelTest(testCase.TestCase):
+ """Class for TR69 DataModel and parameter verification test.
+
+ Configuration Parameters:
+ TBD
+ testID: 14187024
+ title =DataModel
+ req_file = 14187024-requirement.req
+ """
+
+ def Init(self):
+ """Initialte the test case."""
+ info = self.log.CreateResultInfo(
+ '---', 'Data model verification test started...')
+ self.log.SendLine(self.test_info, info)
+ self.bruno = device.Bruno(user=self.params['user'],
+ addr=self.params['addr'],
+ pwd=self.params['pwd'],
+ cmd_prompt=self.params['bruno_prompt'])
+ if not self.p_ssh:
+ self.p_ssh = ssh.SSH(user=self.params['user'],
+ addr=self.params['addr'],
+ pwd=self.params['pwd'],
+ bruno_prompt=self.params['bruno_prompt'],
+ addr_ipv6=self.params['addr_ipv6'])
+ self.p_ssh.SetLogging(self.log)
+ self.bruno.SetLogging(self.log)
+ self.bruno.dev_ssh = self.p_ssh
+
+ self.__short_delay = 30 #short delay: 5 seconds
+ self.__delay = 80 #medium delay: 180 seconds
+ self.__long_delay = 120 #long delay: 600 seconds
+
+ self.req_file = open(self.params['req_file'], 'r')
+ self.req_tr069_params = []
+ self.acs_instance = acs.ACS()
+ self.acs_instance.SetLogging(self.log)
+
+ def SetACSURL(self, url=None):
+ """Set the ACS URL on device."""
+ if not url:
+ url = self.params['acs_url']
+ self.p_ssh.SendCmd('echo '+ url + ' > /tmp/cwmp/acs_url')
+ self.p_ssh.GetCmdOutput()[1].rstrip()
+
+ def GetACSURL(self):
+ """Get ACS URL from Device."""
+ self.p_ssh.SendCmd(r'cat /tmp/cwmp/acs_url')
+ line = self.p_ssh.GetCmdOutput()[1].strip()
+ m = re.match('http[s]?://([\w-]+\.)+\.com/cwmp', line)
+ if m is None:
+ info = self.log.CreateErrorInfo('Warning',
+ 'Can not get ACS url')
+ self.log.SendLine(None, info)
+ else:
+ info = self.log.CreateProgressInfo('---',
+ 'Get ACS URL from device file: '
+ + line)
+ self.log.SendLine(None, info)
+ return line
+
+ def GetCurrentVersion(self):
+ """Get current software version."""
+ self.p_ssh.SendCmd('more /etc/version')
+ return self.p_ssh.GetCmdOutput()[1].strip()
+
+ def TestEnvCheck(self):
+ """Verify device configuration meets the test requirement."""
+ acs_url = self.GetACSURL()
+ if acs_url != self.params['acs_url']:
+ info = self.log.CreateErrorInfo(
+ 'Warning', 'ACS URL incorrect!. Expected: '
+ + self.params['acs_url'] + ', Current: '
+ + acs_url)
+ self.log.SendLine(self.test_info, info)
+ #os.sys.exit(1)
+ info = self.log.CreateProgressInfo(
+ '---', 'Reset ACS URL to Expected: ' + self.params['acs_url'])
+ self.log.SendLine(self.test_info, info)
+ self.SetACSURL()
+
+ current_version = self.GetCurrentVersion()
+ if (not current_version
+ or current_version != self.params['expected_version']):
+ info = self.log.CreateErrorInfo(
+ 'critical', 'Not expected image version. Expected: '
+ + self.params['expected_version'] + ', Current: '
+ + current_version)
+ self.log.SendLine(self.test_info, info)
+ else:
+ self.AddConfigParams('current_version', current_version)
+
+ def CheckRequirement(self, chk_list, req_list):
+ """Check parameter names with parameters in requirement file.
+
+ This file will compare the parameters from Device and from requirement
+ File. The differences between the two will be written to the result log
+ file
+ Args:
+ chk_list: the list of parameters results returned by query
+ req_list: the list of parameters from requirement
+ Returns:
+ missing_list: the list of parameters that is missing from requirement
+ net_list: the list of parameters that are newly added but not in the
+ requirement
+ """
+ missing_list = []
+ new_list = []
+ #check missing parameters from Requirement
+ for req_line in req_list:
+ verify = False
+ for chk_line in chk_list:
+ if req_line == chk_line:
+ verify = True
+ elif string.find(chk_line, req_line) == 0:
+ verify = True
+ if not verify:
+ missing_list.append(req_line)
+
+ #check newly added parameters to Requirement
+ for line in chk_list:
+ if req_list.count(line) < 1 and new_list.count(line) < 1:
+ new_list.append(line)
+ return (missing_list, new_list)
+
+ def GetACSAppFromURL(self):
+ """Parse ACS appID from ACS URL string."""
+ return re.sub(r'https?://(.*).appspot.com/cwmp', r'\1',
+ self.params['acs_url'])
+
+ def GetSerialNumber(self):
+ """This function returns Bruno device serial number."""
+ self.p_ssh.SendCmd('hnvram -r 1st_serial_number')
+ line = self.p_ssh.GetCmdOutput()[1].strip()
+ if line is None:
+ info = self.log.CreateErrorInfo('Warning',
+ 'Can not get serial number')
+ self.log.SendLine(None, info)
+ return False
+ return re.sub(r'1st_serial_number=([0-9A-Z]{12})', r'\1', line)
+
+ def GetProductClass(self):
+ """Get product class from device system."""
+ self.p_ssh.SendCmd('cat /etc/platform')
+ return self.p_ssh.GetCmdOutput()[1].strip()
+
+ def BatchSetParametersValues(self, tree, param):
+ """Set multiple parameter values in a single nbi_client RPC call.
+
+ Set multiple parameter values
+ Args:
+ tree: the parameter names are in TreeElement structure. It has
+ parameter 'writable' states and test_value to set.
+ Parameter names/paths are sorted in alphabetical order in tree.
+ param: the parameters name list that needs to be set with new value
+ """
+ nbi_path = self.params['nbi_client_path']
+ app_id = self.GetACSAppFromURL()
+ product_class = self.GetProductClass()
+ serial_no = self.GetSerialNumber()
+
+ container = tree.findall('parameter_value_struct')
+ batch_size = 1
+ counter = 0
+ parameter_struct = ''
+
+ for node in container:
+ name = node.findtext('name')
+
+ if name in param:
+ nbi_type = node.findtext('nbi_type')
+
+ if not nbi_type:
+ info = self.log.CreateErrorInfo('Warning',
+ 'Can not determin parameter type!')
+ self.log.SendLine(None, info)
+
+ test_value = node.findtext('test_value')
+ if nbi_type == 'string_value':
+ test_value = '"' + test_value + '"'
+ if (node.findtext('writable') == '1'
+ or node.findtext('writable') == 'true'):
+ parameter_struct += ('parameter_value_struct: <name: "'
+ + name + '" ' + nbi_type + ': '
+ + test_value + '> ')
+ counter += 1
+ else:
+ continue
+
+ if counter >= batch_size:
+ counter = 0
+ cmd = (nbi_path
+ + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+ '*/' + app_id + '/default/* --service CpeParametersService '
+ '--method SetParameterValues --request \''
+ 'cpe_id: <oui: "F88FCA" product_class: "' + product_class
+ + '" serial_no: "' + serial_no
+ + '"> request: <parameter_list < '
+ + parameter_struct + ' > parameter_key: "myParamKey">\'')
+ print cmd
+ parameter_struct = ''
+ time_stamp = self.GetTimeStamp()
+ call_result = self.acs_instance.StubbyClientCall(cmd)
+ print call_result
+ if not self.acs_instance.CheckCmdCall(call_result):
+ # command not succeed according to nbi_client reply
+ # However, there is possibility that the request is successful,
+ # nbi_client reports a failure only because the timeout 60s.
+ # check device log to see if this request actually fails
+ self.p_ssh.SendCmd('dmesg | grep cwmpd:')
+ # read the last 500 lines from log
+ log_list = self.p_ssh.GetCmdOutput(500)
+ log_list.reverse()
+ reply_msg = self.ParseDeviceLogForError(
+ time_stamp, log_list, name, 'SetParameterValues')
+ if '<soap:Fault>' in reply_msg[0] or '<soap:fault>' in reply_msg[0]:
+ # failed to set parameter values (on nbi_client and device):
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Batch SetParameterValues() failed on Parameter: '
+ + name + '. Parameter type: ' + nbi_type
+ + '. Set test value: ' + test_value + '. Error message: \n'
+ + ''.join(reply_msg))
+ self.log.SendLines(self.test_info, info)
+ elif '<cwmp:SetParameterValuesResponse>' in reply_msg[0]:
+ # nbi_reports failure, but success on device
+ info = self.log.CreateResultInfo(
+ '---', 'Batch SetParameterValues() Succeeded on Parameter: '
+ + name + '. Parameter type: ' + nbi_type
+ + '. Set test value: ' + test_value)
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Batch SetParameterValues() failed on Parameter: '
+ + name + '. Parameter type: ' + nbi_type
+ + '. Set test value: ' + test_value + '. Error message: \n'
+ + ''.join(reply_msg))
+ self.log.SendLines(self.test_info, info)
+ else:
+ # Succeed, logging to result file:
+ self.log.SendTestData(call_result)
+ info = self.log.CreateResultInfo(
+ '---', 'Batch SetParameterValues() Succeeded on Parameter: '
+ + name + '. Parameter type: ' + nbi_type
+ + '. Set test value: ' + test_value)
+ self.log.SendLine(self.test_info, info)
+
+ def SetParameterValues(self, param='', param_type='', value=''):
+ """Set parameter values on Device.
+
+ Set parameter values
+ Args:
+ param: the parameter name that needs to be set with new value
+ param_type: type of value: string, boolean, long_int, etc
+ value: parameter value
+ Returns:
+ return setParameterValues if succeeded.
+ return error information if failed
+ """
+ nbi_path = self.params['nbi_client_path']
+ app_id = self.GetACSAppFromURL()
+ product_class = self.GetProductClass()
+ serial_no = self.GetSerialNumber()
+ # parse type:
+ if 'string' in param_type:
+ param_type = 'string_value'
+ value = '"' + value + '"'
+ elif 'bool' in param_type:
+ param_type = 'boolean_value'
+ elif 'unsignedInt' in param_type:
+ param_type = 'long_value'
+ else:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Unknown parameter type: ' + param_type)
+ self.log.SendLines(self.test_info, info)
+ os.sys.exit(1)
+
+ cmd = (nbi_path + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+ '*/' + app_id + '/default/* --service CpeParametersService '
+ '--method SetParameterValues --request \''
+ 'cpe_id: <oui: "F88FCA" product_class: "' + product_class
+ + '" serial_no: "' + serial_no + '"> request: <parameter_list < '
+ 'parameter_value_struct: <name: "' + param +
+ '" ' + param_type + ': ' + value
+ + '> > parameter_key: "myParamKey">\'')
+ print cmd
+ call_result = self.acs_instance.StubbyClientCall(cmd)
+ if 'FAILED' in call_result[0]:
+ info = self.log.CreateErrorInfo(
+ 'Warning', 'ACS failed on SetParameterValues query: ' + cmd)
+ self.log.SendLines(self.test_info, info)
+ return False
+ else:
+ print call_result
+ return call_result
+
+ def GetParameterNames(self, param='.', next_level=True):
+ """Get next level parameter names of the current queried parameter.
+
+ The next level parameter names will be output to result file
+ Due to the large number of parameter instances, parameters in the same
+ parameter family will be grouped together into a single nbi_client
+ RPC call (getParameterNames() call, this call will feedback the access
+ state information). In order to minimize the total processing time, as
+ well as the number of RPC calls to ACS appEngine. This will greatly
+ reduce the execution time.
+ Each getParameterNames call will be created as an individual thread.
+ And multiple calls are made in parallel.
+
+ Args:
+ param:
+ This is the parameter that is queried for its next level names
+ Note that currently GetParameterNames does not support multiple
+ parameter names in a query command. If parameter name is a dot,
+ and the next level is 'False', then device will return all
+ parameters under the queried parameter path
+ next_level:
+ if this parameter is True:
+ only the next level parameter names are returned
+ if this parameter is False:
+ all parameter in the requested parameter path will be returned
+ Returns:
+ return the nbi_client RPC call response. Response can be used to
+ parse for parameters' name, access attribute, etc.
+ """
+ nbi_path = self.params['nbi_client_path']
+ app_id = self.GetACSAppFromURL()
+ product_class = self.GetProductClass()
+ serial_no = self.GetSerialNumber()
+ if next_level:
+ next_level = 'true'
+ else:
+ next_level = 'false'
+ cmd = (nbi_path + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+ '*/' + app_id + '/default/* --service CpeParametersService '
+ '--method GetParameterNames --request \''
+ 'cpe_id: <oui: "F88FCA" product_class: "' + product_class
+ + '" serial_no: "' + serial_no + '"> request: <parameter_path: "'
+ + param + '" next_level: ' + next_level + '>\'')
+ print cmd
+ call_result = self.acs_instance.StubbyClientCall(cmd)
+ if 'FAILED' in call_result[0]:
+ self.nbi_result = []
+ info = self.log.CreateErrorInfo(
+ 'Warning', 'ACS failed on GetParameterNames query: ' + cmd)
+ self.log.SendLines(self.test_info, info)
+ return False
+ else:
+ self.nbi_result = call_result
+ print call_result
+ return call_result
+
+ def GetParameterValues(self, param=''):
+ """Make a Get parameter value(s) query at ACS to request the device.
+
+ This method get value(s) of the designated parameter(s)
+ Args:
+ param: this is the parameter (path) that need to get value(s) from.
+ 1. If this path ends with a dot '.', all parameters
+ under this path will be queried for values.
+ 2. If this path does not end with a '.', the parameter
+ must be a leaf parameter which has a value.
+ Returns:
+ This method returns a list of parameter values and record them to file.
+ """
+ nbi_path = self.params['nbi_client_path']
+ if param == '.':
+ param = ''
+ app_id = self.GetACSAppFromURL()
+ product_class = self.GetProductClass()
+ serial_no = self.GetSerialNumber()
+ cmd = (nbi_path + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+ '*/' + app_id + '/default/* --service CpeParametersService '
+ '--method GetParameterValues --request \''
+ 'cpe_id: <oui: "F88FCA" product_class: "' + product_class
+ + '" serial_no: "' + serial_no + '"> request: <parameter_names: "'
+ +param + '">\'')
+
+ print cmd
+ call_result = self.acs_instance.StubbyClientCall(cmd)
+ if 'FAILED' in call_result[0]:
+ self.nbi_result = []
+ info = self.log.CreateErrorInfo(
+ 'Warning', 'ACS failed on GetParameterValues query: ' + cmd)
+ self.log.SendLines(self.test_info, info)
+ return False
+ else:
+ self.nbi_result = call_result
+ print call_result
+ return call_result
+
+ def FindEndofRequest(self, log_list, index, pattern):
+ """Find the end of a request string from device log.
+
+ Some times the close tag, for example: </cwmp:SetParameterValues> is
+ separated to two consecutive log lines, due to the line length limitation.
+ This method try to search the request closing tag on separate lines
+
+ Args:
+ log_list: the device log list.
+ index: the index of the line to search for tag
+ pattern: the closing tag that need to be searched in log file
+ Returns:
+ True: if pattern find in log
+ False: if pattern not find in log
+ """
+ if index >= len(log_list)-1:
+ if pattern in log_list[index]:
+ return True
+ else:
+ return False
+
+ s = re.sub(r'\[\s*\d*\.\d*\]\s*cwmpd:\s*(.*)', r'\1', log_list[index + 1])
+ s = log_list[index].strip() + s.strip()
+
+ if pattern in s:
+ return True
+ return False
+
+ def MatchParameterName(self, line, param_name, rpc):
+ """Match rpc request logs to a parameter name."""
+ if rpc == 'GetParameterNames':
+ m = re.search(r'.*<ParameterPath>(.*)</ParameterPath>.*', line)
+ if m is not None and param_name in m.group(1):
+ return True
+ else:
+ return False
+ elif rpc == 'GetParameterValues':
+ m = re.search(r'.*<cwmp:GetParameterValues>.*<ParameterNames.*>(.*)'
+ '</ParameterNames></cwmp:GetParameterValues>.*', line)
+ if m is not None and param_name in m.group(1):
+ return True
+ else:
+ return False
+ elif rpc == 'SetParameterValues':
+ m = re.search(r'.*<Name>(.*)</Name><Value.*', line)
+ if m is not None and param_name in m.group(1):
+ return True
+ else:
+ return False
+ else:
+ return False
+
+ def ParseDeviceLogForError(self, time_stamp, log_list, param, rpc):
+ """Parse the log information from device, Looking for cwmp errors.
+
+ Args:
+ time_stamp: looking for log events that happened only after this time
+ log_list: the cwmp event information obtained from device log
+ param: the parameter name to get value from
+ rpc: the rpc function name to search in log
+ Returns:
+ return cwmp error message found in device log.
+ """
+
+ reply_msg = []
+ start_of_rpc_reply = False
+ end_of_rpc_reply = False
+ start_of_rpc_request = False
+ end_of_rpc_request = False
+ param_matching = False
+ current_index = 0
+
+ for line in log_list:
+ # check only the getParameter query starts after the specified time:
+ if float(self.GetTimeStamp(line)) > float(time_stamp):
+ if 'CPE RECEIVED (at' in line and not start_of_rpc_request:
+ index = log_list.index(line)
+ # macthing the request query in log messages
+ for i in range(index+1, len(log_list)-1):
+ if i < len(log_list):
+ # match rpc request string
+ if '<cwmp:' + rpc + '>' in log_list[i]:
+ start_of_rpc_request = True
+ # match rpc request string end
+ if (self.FindEndofRequest(log_list, i, '</cwmp:' + rpc + '>')
+ and start_of_rpc_request):
+ if (start_of_rpc_request and end_of_rpc_request
+ and not param in log_list[i]):
+ end_of_rpc_request = True
+ # rpc request scan completed, requested parameter not matched
+ # this request is not request of interest
+ start_of_rpc_request = False
+ end_of_rpc_request = False
+ break
+ # match queried parameter in rpc request
+ if start_of_rpc_request and not end_of_rpc_request:
+ if self.MatchParameterName(log_list[i], param, rpc):
+ param_matching = True
+ current_index = i
+ break
+
+ # search for end of rpc request command
+ if param_matching and not end_of_rpc_request:
+ for i in range(current_index, len(log_list)-1):
+ if i < len(log_list):
+ # match rpc request string end
+ if self.FindEndofRequest(log_list, i, '</cwmp:' + rpc + '>'):
+ end_of_rpc_request = True
+ current_index = i
+ break
+
+ # search for rpc request reply message
+ if param_matching and end_of_rpc_request:
+ for i in range(current_index, len(log_list)-1):
+ if i < len(log_list):
+ if ('<soap:Fault>' in log_list[i] or '<soap:fault>' in log_list[i]
+ or '<cwmp:' + rpc + 'Response>' in log_list[i]):
+ start_of_rpc_reply = True
+ if (' </soap:Fault>' in log_list[i]
+ or ' </soap:fault>' in log_list[i]
+ or '</cwmp:' + rpc + 'Response>' in log_list[i]):
+ end_of_rpc_reply = True
+ reply_msg.append(log_list[i].strip()+'\n')
+ if start_of_rpc_reply and not end_of_rpc_reply:
+ reply_msg.append(log_list[i].strip()+'\n')
+ if start_of_rpc_reply and end_of_rpc_reply:
+ return reply_msg
+ return ['No rpc message is available in device log']
+
+ def GetParameterListFromDeviceLog(self, time_stamp, param=''):
+ """Parse TR069 data model parameters from Device log information.
+
+ Args:
+ time_stamp: looking for log events that happened only after this time
+ param: the cwmp parameter to search in device log
+ Returns:
+ return parameter list if succeeded
+ return false otherwise
+ """
+ if param == '.':
+ param = ''
+ param_list = []
+ param_list_start = False
+ param_list_end = False
+ self.device_log_result = []
+
+ self.p_ssh.SendCmd('dmesg | grep cwmpd:')
+ log_list = self.p_ssh.GetCmdOutput()
+ log_list.reverse()
+
+ for line in log_list:
+ # check only the getParameter query starts after the last known event:
+ if float(self.GetTimeStamp(line)) > float(time_stamp):
+ if '<cwmp:GetParameterValues>' in line:
+ # check the parameter query from nbi_client has been answered:
+ m = re.search(r'.*<cwmp:GetParameterValues>.*<ParameterNames.*>(.*)'
+ '</ParameterNames></cwmp:GetParameterValues>.*', line)
+ if m is not None and param in m.group(1):
+ index = log_list.index(line)
+ for i in range(index+1, index+50):
+ if '<cwmp:GetParameterValuesResponse>' in log_list[i]:
+ param_list_start = True
+ break
+ continue
+
+ if '</cwmp:GetParameterValuesResponse>' in line:
+ if param_list_start:
+ param_list_end = True
+ break
+
+ if param_list_start:
+ self.device_log_result.append(line.strip())
+ if '<Name>' in line:
+ output = re.sub(r'.*(<Name>)(.*)(</Name>)', r'\2', line)
+ output = output.strip()
+ output = re.sub(r'(\.)[\d]+(\.)', r'\1{i}\2', output)
+ if param_list.count(output) < 1:
+ param_list.append(output)
+
+ if (not param_list_start) or (not param_list_end):
+ error_msg = self.ParseDeviceLogForError(
+ time_stamp, log_list, param, 'GetParameterValues')
+ if 'fault' in error_msg[0] or 'Fault' in error_msg[0]:
+ # error occurred in rpc request
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Exit! Error while get values: ' + ''.join(error_msg))
+ self.log.SendLines(self.test_info, info)
+ os.sys.exit(1)
+ else:
+ # no error in request, request still in progress
+ return False
+ return param_list
+
+ def CreateElement(self, param_name, param_type_device,
+ param_type_nbi, param_value):
+ """Create a new parameter node that contains parameter name and value.
+
+ Currently try to set the same value as being read from the device.
+ Parameter type may have different representations on different platforms.
+ E.g.: type 'long_value' on nbi_client is 'unsignedInt' on bruno.
+ type 'string_value' on nbi_client is 'string' on bruno
+ Args:
+ param_name: the name of the parameter (Path)
+ param_type_device: the type of the parameter represented by device format
+ param_type_nbi: the type of parameter, represented by nbi_client format
+ param_value: the value of the parameter
+ Returns:
+ return the created element node
+ """
+ e = ET.Element('parameter_value_struct')
+ name = ET.Element('name')
+ name.text = param_name
+ e.append(name)
+ device_type = ET.Element('device_type')
+ device_type.text = param_type_device
+ e.append(device_type)
+ nbi_type = ET.Element('nbi_type')
+ if not param_type_nbi:
+ if param_type_device == 'unsignedInt':
+ param_type_nbi = 'long_value'
+ elif param_type_device == 'boolean':
+ param_type_nbi = 'boolean_value'
+ elif param_type_device == 'string':
+ param_type_nbi = 'string_value'
+ else:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Exit! Unknown (new) Parameter types found from device:'
+ ': ' + param_type_device + '. Define as string_value')
+ self.log.SendLine(self.test_info, info)
+ param_type_nbi = 'string_value'
+ nbi_type.text = param_type_nbi
+ e.append(nbi_type)
+ value = ET.Element('value')
+ value.text = param_value
+ e.append(value)
+ test_value = ET.Element('test_value')
+ test_value.text = param_value
+ e.append(test_value)
+ access_state = ET.Element('writable')
+ access_state.text = 'true'
+ e.append(access_state)
+ return e
+
+ def SortElementTree(self, tree):
+ """Sort the parameters in element tree, in alphabetical order of names.
+
+ Args:
+ tree: the ElementTree data structure that holds the parameter list
+ Returns:
+ return the sorted tree data structure
+ """
+ data = []
+ container = tree.findall('parameter_value_struct')
+ for node in container:
+ key = node.findtext('name')
+ data.append((key, node))
+
+ root = ET.Element(tree.getroot().tag, tree.getroot().attrib)
+ root.text = tree.getroot().text
+ data.sort()
+ for node in data:
+ root.append(node[1])
+ return ET.ElementTree(root)
+
+ def ParseToElementTree(self):
+ """Parse the parameter list to Element Tree structure."""
+ root = ET.Element('Bruno_Automation_Test', name='Set_Parameter_Value_Test',
+ testCases=self.test_info['testID'])
+ root.text = 'This structure stores all parameters and values for this test'
+
+ if self.nbi_result:
+ for line in self.nbi_result:
+ if 'name:' in line:
+ param_name = re.sub('.*\"(.*)\".*', r'\1', line).strip()
+ index = self.nbi_result.index(line)
+ param_type = self.nbi_result[index+1].split(':')[0].strip()
+ param_value = self.nbi_result[index+1].split(':')[1].strip()
+ root.append(self.CreateElement(param_name, None,
+ param_type, param_value))
+ elif self.device_log_result:
+ #self.device_log_result.reverse()
+ for line in self.device_log_result:
+ if '<Name>' in line:
+ param_name = re.sub(r'.*(<Name>)(.*)(</Name>)', r'\2', line).strip()
+
+ index = self.device_log_result.index(line) + 1
+ while not 'Value' in self.device_log_result[index]:
+ index += 1
+ if index >= len(self.device_log_result) or '<Name>' in line:
+ info = self.log.createErrorInfo(
+ 'Critical', 'Mis-matching name-value pair in log file!')
+ self.log.sendLine(self.test_info, info)
+ return False
+
+ line = self.device_log_result[index]
+ # In case value string spans multiple lines:
+ while '</Value>' not in line:
+ index +=1
+ if index >= len(self.device_log_result):
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Exit! Cannot find matching </Value> tag in log!')
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+ new_line = self.device_log_result[index]
+ # if <Name> or EOF reached before a matching </Value> is found
+ if '<Name>' in new_line:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Exit! Cannot find matching </Value> tag in log!')
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+ new_line = re.sub('\[\s*\d*\.\d{3}\]\s*cwmpd:\s*(.*)',
+ r'\1', new_line).strip()
+ line += new_line.strip()
+
+ param_type = re.search('.*type\s*=\s*\"[a-zA-Z]*:([a-zA-Z]*)\">.*',
+ line, re.DOTALL).group(1)
+ param_value = re.search('.*<Value\s*[a-zA-Z]*:type="[a-zA-Z]*:'
+ '[a-zA-Z]*">(.*)</Value>',
+ line, re.DOTALL).group(1)
+ root.append(self.CreateElement(param_name, param_type,
+ None, param_value))
+ else:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Failed to get parameter list from device!')
+ self.log.SendLine(self.test_info, info)
+ return False
+
+ tree = ET.ElementTree(root)
+ tree = self.SortElementTree(tree)
+ return tree
+
+ def ParametersFromXMLFile(self, file_path=None):
+ """Read parameter data from an XML format file.
+
+ Args:
+ file_path: file path to the xml file
+ Returns:
+ return the parameter list stored in the file, in the format of ElementTree
+ """
+ f = None
+ try:
+ if not file_path:
+ f = open(self.params['para_tree_file'], 'r')
+ else:
+ f = open(file_path, 'r')
+ tree = ET.parse(f)
+ except IOError, inst:
+ err_string = ('Exit! Unexpected error opening '
+ + str(f.name) + '. ' + str(inst))
+ info = self.log.CreateErrorInfo('Critical', err_string)
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+ return tree
+
+ def ParametersToXMLFile(self, tree=None, file_path=None):
+ """Output the available parameter instance tree to XML format file.
+
+ Parameter instance results will be written to XML formated file,
+ with test value that has been set for the parameter(s)
+ For those parameters that failed to set value, they are recorded in
+ the result file.
+
+ Args:
+ tree: the parameters structure to parse to file
+ file_path: path to the output file
+ """
+ if not tree:
+ tree = self.para_tree
+ if not file_path:
+ fout = open(self.params['para_tree_file'], 'w')
+ else:
+ fout = open(file_path, 'w')
+
+ try:
+ tree.write(fout)
+ except IOError, inst:
+ err_string = ('Exit! Unexpected error writing to file '
+ + fout.name() + str(inst))
+ info = self.log.CreateErrorInfo('Critical', err_string)
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+ fout.close()
+
+ def GetRequirementFromFile(self):
+ """Get TR69 data model parameters from requirement file.
+
+ The format of the requirement file is as follows:
+ 1. Each line represents a data model parameter
+ 2. If a parameter has indexed nodes, index numbers are replaced by {i}
+ ###### Begin of File ######
+ Device.Bridging.
+ Device.Bridging.Bridge.{i}.
+ Device.Bridging.Bridge.{i}.Port.{i}.
+ Device.Bridging.Bridge.{i}.Port.{i}.Stats.
+ ... ... ...
+ Device.DeviceInfo.Manufacturer
+ Device.DeviceInfo.ManufacturerOUI
+ X_GOOGLE-COM_GVSB.EpgSecondary
+ X_GOOGLE-COM_GVSB.GvsbChannelLineup
+ X_GOOGLE-COM_GVSB.GvsbKick
+ X_GOOGLE-COM_GVSB.GvsbServer
+ ###### End of File ######
+ Returns:
+ return the requirement parameters in a list
+ """
+ req_list = []
+ for line in self.req_file:
+ line = line.strip()
+ if req_list.count(line) < 1:
+ req_list.append(line)
+ return req_list
+
+ def GetTimeStamp(self, line=None):
+ """Extract the time information of most current event from Device log.
+
+ i.e., the time that the last event happens since reboot (in seconds)
+ Args:
+ line: a line of device log which contains event time information.
+ If it has value of 'None', then retrieve the last a few lines from
+ device log to extract time information.
+ Returns:
+ return the time stamp string if succeed, return -1 otherwise.
+ """
+ if line is None:
+ # get time stamp from Device log file
+ self.p_ssh.SendCmd('dmesg | grep cwmpd:')
+ # search the last 30 lines in Device log for time information
+ latest_log_entry = 30
+ log_list = self.p_ssh.GetCmdOutput(latest_log_entry)
+
+ for line in log_list:
+ m = re.match('\\[[\s]*(\d*\\.\d{3})\\]', line)
+ if m is not None:
+ # Match, return the time stamp
+ return m.group(1)
+ else:
+ # get time stamp from a Device log line
+ m = re.match('\\[[\s]*(\d*\\.\d{3})\\]', line)
+ if m is not None:
+ # Match, return the time stamp
+ return m.group(1)
+ # No time stamp found
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Cannot get time information from Device log!')
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+
+ def VerifyAllReqParameters(self):
+ """Check available parameters and compare to requirement list.
+
+ This method checks all available parameters on current image, and
+ compare the list with requirement list. It will find out:
+ 1. the parameters that are missing from the requirement list.
+ 2. the newly added parameters that are not in the requirement list.
+ Both lists are written to the result file.
+
+ This method also output all parameter instances on current device to
+ a xml formated file, together with their types and values
+ """
+ # get parameter name lists, compare with requirement list
+ info = self.log.CreateProgressInfo(
+ '30%', 'Get parameters from nbi_client ...')
+ self.log.SendLine(self.test_info, info)
+ time_stamp = self.GetTimeStamp()
+ chk_list = self.acs_instance.ParseParameterNames(self.GetParameterValues(),
+ True)
+
+ if not chk_list:
+ info = self.log.CreateProgressInfo(
+ '35%', 'ACS failed to reply, get parameters from device log ...')
+ self.log.SendLine(self.test_info, info)
+ # nbi client may fail because of timeout
+ # if that is the case, then get parameter list from device log
+ count = 0
+ while not chk_list:
+ time.sleep(self.__delay)
+ chk_list = self.GetParameterListFromDeviceLog(time_stamp)
+ count += 1
+ if count*self.__delay >= self.__long_delay:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Can not retrieve complete log info from device')
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+
+ if not chk_list:
+ info = self.log.CreateProgressInfo(
+ '40%', 'GetParameterValue query in progress, retry after '
+ + str(self.__delay) + ' seconds.')
+ self.log.SendLine(self.test_info, info)
+ chk_list.sort()
+
+ info = self.log.CreateProgressInfo(
+ '55%', 'GetParameterValue query completed, verify parameter list '
+ 'with parameter requirement document.')
+ self.log.SendLine(self.test_info, info)
+ req_list = self.GetRequirementFromFile()
+ (mis_list, new_list) = self.CheckRequirement(chk_list, req_list)
+ mis_list.sort()
+ new_list.sort()
+ self.log.SendTestData(['############# Missing Parameters #############'])
+ self.log.SendTestData(mis_list)
+
+ self.log.SendTestData(['############ Newly Added Parameters ############'])
+ self.log.SendTestData(new_list)
+
+ info = self.log.CreateProgressInfo(
+ '75%', 'Verify current parameter list with requirement completed. '
+ 'Check result file:' + self.log.params['f_error'] + ' for details.')
+ self.log.SendLine(self.test_info, info)
+
+ # write parameter instance to file
+ self.para_tree = self.ParseToElementTree()
+ self.ParametersToXMLFile()
+ info = self.log.CreateProgressInfo(
+ '100%', 'Update parameter file: ' + self.params['para_tree_file']
+ + ' completed.')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Pass', 'Verification completed. '
+ 'Check result file: ' + self.log.params['f_result'] + ' for missing '
+ 'parameters and new parameters.')
+ self.log.SendLine(self.test_info, info)
+
+ def GetParameterAccessStateFromDeviceLog(self, time_stamp=None, param='.'):
+ """Get parameter access state from device logs.
+
+ Args:
+ time_stamp: search events only after this time in log file
+ param: the parameter to search in device log
+ Returns:
+ return dictionary: parameter names as key, and writable status as value
+ """
+ self.p_ssh.SendCmd('dmesg | grep cwmpd:')
+ log_list = self.p_ssh.GetCmdOutput()
+ log_list.reverse()
+
+ param_state_dict = {}
+ param_list_start = False
+ param_list_end = False
+
+ for line in log_list:
+ # check only the getParameter query starts after the last known event:
+ if float(self.GetTimeStamp(line)) > float(time_stamp):
+ if '<cwmp:GetParameterNames>' in line:
+ # check the parameter query from nbi_client has been answered:
+ m = re.search(
+ r'.*<cwmp:GetParameterNames>.*<ParameterPath>(.*)'
+ '</ParameterPath>.*<NextLevel>false</NextLevel>.*'
+ '</cwmp:GetParameterNames>.*', line)
+ if m and param in line:
+ index = log_list.index(line)
+ for i in range(index+1, index+30):
+ if i < len(log_list):
+ if '<cwmp:GetParameterNamesResponse>' in log_list[i]:
+ param_list_start = True
+ break
+ continue
+
+ if '</cwmp:GetParameterNamesResponse>' in line:
+ if param_list_start:
+ param_list_end = True
+ break
+
+ if param_list_start:
+ if '<Name>' in line:
+ name = re.search(r'.*<Name>(.*)</Name>', line).group(1).strip()
+ index = log_list.index(line)+1
+ if '<Writable>' in log_list[index]:
+ access = re.sub(r'.*<Writable>(.*)</Writable>',
+ r'\1', log_list[index]).strip()
+ if access == '1':
+ access = 'true'
+ else:
+ access = 'false'
+ param_state_dict.update({name: access})
+
+ if (not param_list_start) or (not param_list_end):
+ error_msg = self.ParseDeviceLogForError(
+ time_stamp, log_list, param, 'GetParameterNames')
+ if 'fault' in error_msg[0] or 'Fault' in error_msg[0]:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Exit! Error while get values: ' + ''.join(error_msg))
+ self.log.SendLines(self.test_info, info)
+ os.sys.exit(1)
+ else:
+ return False
+ return param_state_dict
+
+ def UpdateParameterAccessState(self, tree, param_name='.'):
+ """Update parameters' access state (read-only, read-write status).
+
+ TR69 parameters could be either in a read-only state, or a writable
+ state. The setParameterValues() test only verifies those parameters
+ in the 'writable' state. Try to set them with new values.
+ This method is called to determine the state of each supported parameter
+
+ Args:
+ tree:
+ the ElementTree data structure that holds the parameter attribute
+ list in XML format. This tree needs to be sorted before passed to
+ this function, according to the alphabetical order of parameter
+ names.
+ param_name:
+ the parameter name to update the access state
+ Returns:
+ returns this tree with updated parameter access state information
+ """
+ time_stamp = self.GetTimeStamp()
+ cmd_list = self.GetParameterNames(param_name, False)
+ if not self.acs_instance.CheckCmdCall(cmd_list):
+ info = self.log.CreateProgressInfo(
+ '---', 'GetParameterNames() query failed on ACS. Analyzing device'
+ ' logs...')
+ self.log.SendLine(self.test_info, info)
+
+ param_dict = self.GetParameterAccessStateFromDeviceLog(time_stamp,
+ param_name)
+ while not param_dict:
+ info = self.log.CreateProgressInfo(
+ '---', 'GetParameterNames() analyze in progress, wait for '
+ + str(self.__short_delay) + ' seconds.')
+ self.log.SendLine(self.test_info, info)
+ time.sleep(self.__short_delay)
+ param_dict = self.GetParameterAccessStateFromDeviceLog(time_stamp,
+ param_name)
+ else:
+ param_dict = self.acs_instance.ParseParameterAccessStates(cmd_list)
+ for key in param_dict.keys():
+ if param_name.endswith('.') and param_name != '.':
+ param_dict[param_name + key] = param_dict.pop(key)
+ else:
+ param_dict[param_name + '.' + key] = param_dict.pop(key)
+
+ container = tree.findall('parameter_value_struct')
+
+ for node in container:
+ name = node.findtext('name')
+ if name in param_dict.keys():
+ e = node.find('writable')
+ if not e:
+ # Create this tag:
+ e = ET.Element('writable')
+ e.text = param_dict[name]
+ node.append(e)
+ else:
+ e.text = param_dict[name]
+ else:
+ continue
+ return tree
+
+ def MarkParametersToSet(self, param_list, param):
+ """In the sorted tree, mark all parameters before param to read-only."""
+ if not param:
+ return param_list
+
+ ls = list(param_list)
+ for name in param_list:
+ if name != param:
+ ls.remove(name)
+ else:
+ break
+ return ls
+
+ def VerifySetAllParameterValues(self):
+ """This verifies the setParameterValues() on all supported parameters.
+
+ This method tests the 'set' capability on all parameters. According
+ to the data type, the value will be set as 'string', 'long_int', 'boolean',
+ etc. Note the current test only verify the 'set' action is supported by
+ each parameter. Therefore, it will try to set the same default value on each
+ parameter (it does not change the default value of each parameter).
+
+ This test assumes that the parameter list has already been retrieved from
+ device by 'GetparameterValues', and has been written to the parameter XML
+ file.
+ """
+ tree = self.ParametersFromXMLFile()
+ #tree = self.UpdateParameterAccessState(tree)
+ tree = self.SortElementTree(tree)
+ param_list = []
+ # Currently Assume all parameters are read-write
+ container = tree.findall('parameter_value_struct')
+
+ for node in container:
+ #name = node.findtext('name')
+ #test_value = node.findtext('test_value')
+ dev_type = node.findtext('device_type')
+ nbi_type = None
+ if dev_type == 'unsignedInt':
+ nbi_type = 'long_value'
+ elif dev_type == 'boolean':
+ nbi_type = 'boolean_value'
+ elif dev_type == 'string':
+ nbi_type = 'string_value'
+ else:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Exit! Unknown (new) Parameter types found from device:'
+ ': ' + dev_type)
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+ node.find('nbi_type').text = nbi_type
+ param_list.append(node.findtext('name'))
+ e = node.find('writable')
+ if not e:
+ access_state = ET.Element('writable')
+ access_state.text = 'true'
+ node.append(access_state)
+ param_list.sort()
+ param_list = self.MarkParametersToSet(
+ param_list, self.params['from_which_param_to_set'])
+ self.BatchSetParametersValues(tree, param_list)
+
+ def VerifyNTPTimeZoneConfig(self):
+ """Verify the Time zone configuration is received and processed by Device.
+
+ Check the log of device, to find TR69 parameter:
+ InternetGatewayDevice.Time.
+ is pushed down to device. Also verify the device correctly updated local
+ time information.
+ """
+ ## Blocked by bug: http://b/issue?id=6804551
+ ## TODO(lmeng): add set values and verify set values succeeded
+ self.p_ssh.SendCmd('dmesg | grep cwmpd:')
+ log_list = self.p_ssh.GetCmdOutput()
+ time_stamp = self.GetTimeStamp()
+ log_list = self.ParseDeviceLogForError(time_stamp, log_list,
+ 'InternetGatewayDevice.Time',
+ 'SetParameterValues')
+ if 'No rpc message is available in device log' in log_list[0]:
+ info = self.log.CreateResultInfo(
+ '---', 'Failed. Can not find TR69 Time zone information in device log')
+ self.log.SendLine(self.test_info, info)
+ elif 'fault' in log_list[0]:
+ info = self.log.CreateResultInfo(
+ '---', 'Failed to apply Time zone configuration on device.')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateResultInfo(
+ '---', 'Succeed to setup Time zone on deivce.')
+ self.log.SendLine(self.test_info, info)
+
+ def VerifyManagementServerURL(self):
+ """Verify set and get Management Server URL from device."""
+ # This test is blocked by bug: http://b/issue?id=6813492
+ mngmt_url_param = 'InternetGatewayDevice.ManagementServer.URL'
+ url_from_dev_param = self.acs_instance.ParseManagementURL(
+ self.GetParameterValues(mngmt_url_param))
+ info = self.log.CreateProgressInfo(
+ '15%', 'Get management url from TR069 parameter: '
+ + mngmt_url_param + '. Value: ' + url_from_dev_param)
+ self.log.SendLine(self.test_info, info)
+ if not url_from_dev_param:
+ info = self.log.CreateProgressInfo(
+ '30%', 'Can not found Value for: '
+ 'InternetGatewayDevice.ManagementServer.URL'
+ '. Verify to set default URL: ' + self.params['default_url'])
+ self.log.SendLine(self.test_info, info)
+ url_from_dev_param = self.params['default_url']
+
+ info = self.log.CreateProgressInfo(
+ '50%', 'Set parameter value for: '
+ 'InternetGatewayDevice.ManagementServer.URL'
+ '. Value: ' + url_from_dev_param)
+ self.log.SendLine(self.test_info, info)
+ result = self.SetParameterValues(mngmt_url_param, 'string',
+ url_from_dev_param)
+ if not result:
+ info = self.log.CreateErrorInfo(
+ 'Critical', 'Failed to set parameter: '
+ 'InternetGatewayDevice.ManagementServer.URL')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Failed', 'Failed to set parameter: '
+ 'InternetGatewayDevice.ManagementServer.URL')
+ self.log.SendLine(self.test_info, info)
+ else:
+ info = self.log.CreateProgressInfo(
+ '100%', 'Succeeded to set parameter: '
+ 'InternetGatewayDevice.ManagementServer.URL')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Pass', 'Succeeded to set parameter: '
+ 'InternetGatewayDevice.ManagementServer.URL')
+ self.log.SendLine(self.test_info, info)
+
+ def VerifySetParameterValuesRPC(self):
+ """Test case: 11722375. To verify SetParameterValues RPC.
+
+ To verify SetParametersRPC, this methond also try to set all parameters
+ which have 'write-able' status, with test value.
+ """
+ # update parameter access state:
+ info = self.log.CreateProgressInfo(
+ '20%', 'Read Parameters read/write status from file.')
+ self.log.SendLine(self.test_info, info)
+ tree = self.ParametersFromXMLFile()
+ info = self.log.CreateProgressInfo(
+ '50%', 'Update parameters read/write status from Device.')
+ self.log.SendLine(self.test_info, info)
+ tree = self.UpdateParameterAccessState(tree, self.params['param_path'])
+ self.ParametersToXMLFile(tree)
+ # set parameter values:
+ self.VerifySetAllParameterValues()
+ info = self.log.CreateProgressInfo(
+ '100%', 'Verify: Set values for all parameters completed.')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Pass', 'Verification: set parameter '
+ 'values completed. '
+ 'Check result file: ' + self.log.params['f_result'] + ' for details.')
+ self.log.SendLine(self.test_info, info)
+
+ def VerifyMonitorTemperature(self):
+ """Test the temperature monitor on device.
+
+ This feature is not available for first launch.
+ """
+ info = self.log.CreateResultInfo(
+ 'Block', 'This feature is not availalbe '
+ 'on the first launch images.')
+ self.log.SendLine(self.test_info, info)
+
+ def VerifyAutoChannelEnable(self):
+ """Test AutoChannelEnable on device.
+
+ This feature is not available for first launch.
+ """
+ info = self.log.CreateResultInfo(
+ 'Block', 'This feature is not availalbe '
+ 'on the first launch images.')
+ self.log.SendLine(self.test_info, info)
+
+ def SetServiceConfigs(self, param_dict):
+ """Set service parameter profile on ACS.
+
+ This method sets the service configurations on ACS, and returns the
+ SetServiceConfigs result.
+ Args:
+ param_dict:
+ it is in this format in config file:
+ "wifi_params":{
+ "Google.Wifi.SSID":{"string_value":"Home"},
+ "Google.Wifi.Home.SSID_2GHz":{"string_value":"Home"},
+ "Google.Wifi.Enable":{"boolean_value":"true"},
+ "Google.Wifi.BeaconType":{"string_value": "Basic"},
+ "Google.Wifi.BasicEncryptionModes":{"string_value":"WEPEncryption"}
+ Returns:
+ True: if set succeeded
+ False: otherwise
+ """
+ app_id = self.GetACSAppFromURL()
+ nbi_path = self.params['nbi_client_path']
+ account_id = self.params['account_id']
+ device_label = self.params['device_label']
+ parameter_value_struct = ''
+
+ # parameter_value_struct: <name: "Google.Wifi.BasicEncryptionModes"
+ # string_value: "WEPEncryption">
+ for key in param_dict.iterkeys():
+ name = key
+ param_type = param_dict[key].keys()[0]
+ value = None
+ if 'string' in param_type:
+ value = '"' + param_dict[key].values()[0] + '"'
+ else:
+ value = param_dict[key].values()[0]
+ parameter_value_struct += (
+ ' parameter_value_struct: <name: "'
+ + name + '" ' + param_type +': ' + value + '> ')
+
+ parameter_value_struct = None
+ cmd = (nbi_path
+ + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+ '*/' + app_id + '/default/* --service AccountService '
+ '--method SetServiceConfigs --request \''
+ 'account_device_id: <account_id: "' + account_id
+ + '" device_label: "' + device_label + '"> parameter_list: < '
+ + parameter_value_struct + ' > async_apply_now: false\'')
+ print cmd
+ call_result = self.acs_instance.StubbyClientCall(cmd)
+ if 'FAILED' in call_result[0]:
+ info = self.log.CreateErrorInfo(
+ 'Warning', 'ACS failed on SetServiceConfigs query: ' + cmd)
+ self.log.SendLines(self.test_info, info)
+ return False
+ else:
+ print call_result
+ return call_result
+
+ def VerifyWiFiConfiguration(self):
+ """Test case: 14165034, WiFi configuration verification.
+
+ This test case will set WiFi parameters on ACS, and verify
+ that the service profile is correctly pushed to device.
+ Thin Bruno is configured with correct WiFi network.
+
+ The WiFi parameters and values in specified in config file
+ in JSON format, e.g.:
+ "Google.Wifi.SSID":{"string_value":"Home"},
+ "Google.Wifi.Home.SSID_2GHz":{"string_value":"Home"},
+ "Google.Wifi.Enable":{"boolean_value":"true"},
+ "Google.Wifi.BeaconType":{string_value: "Basic"},
+ "Google.Wifi.BasicEncryptionModes":{"string_value":"WEPEncryption"}
+
+ Returns:
+ return False if test failed
+ """
+ info = self.log.CreateProgressInfo(
+ '10%', 'Setup SetServiceConfigs on ACS...')
+ self.log.SendLine(self.test_info, info)
+ if not self.SetServiceConfigs(self.params['wifi_params']):
+ return False
+ else:
+ info = self.log.CreateProgressInfo(
+ '20%', 'Setup SetServiceConfigs on ACS succeed.')
+ self.log.SendLine(self.test_info, info)
+
+ # Verify if the service is pushed to device
+ # TODO(lmeng): what parameters to set and to verify
+
+ #################### for Data Model Test Case ####################
+
+ def Run(self):
+ """Run the test case."""
+ ####### Add your code here -- BEGIN #######
+ print 'Test Started...'
+ self.Init()
+ self.p_ssh.SshRetry(5, 15)
+ info = self.log.CreateProgressInfo(
+ '5%', 'SSH session to device successfully established!')
+ self.log.SendLine(self.test_info, info)
+
+ #check device profile/image is as erquired management url from ACS
+ self.TestEnvCheck()
+ info = self.log.CreateProgressInfo(
+ '10%', 'Check device test setup completed!')
+ self.log.SendLine(self.test_info, info)
+
+ if (self.test_info['testID'] == '14187024'
+ or self.test_info['testID'] == '14165033'
+ or self.test_info['testID'] == '14315008'
+ or self.test_info['testID'] == '14165035'
+ or self.test_info['testID'] == '14314007'
+ or self.test_info['testID'] == '11865133'
+ or self.test_info['testID'] == '14335001'
+ or self.test_info['testID'] == '14266498'
+ or self.test_info['testID'] == '14319149'
+ or self.test_info['testID'] == '14314008'):
+ self.VerifyAllReqParameters()
+ elif self.test_info['testID'] == '11722375':
+ self.VerifySetParameterValuesRPC()
+ elif self.test_info['testID'] == '14315009':
+ self.VerifyManagementServerURL()
+ elif self.test_info['testID'] == '14314009':
+ self.VerifyMonitorTemperature()
+ elif self.test_info['testID'] == '14165038':
+ self.VerifyAutoChannelEnable()
+ elif self.test_info['testID'] == '15643845':
+ self.VerifyNTPTimeZoneConfig()
+ elif self.test_info['testID'] == '14165034':
+ self.VerifyWiFiConfiguration()
+ else:
+ return
+ print 'Test Completed...'
+ ####### Add your code here -- END #######
+
+ def Destructor(self):
+ self.p_ssh.Close()
+
+if __name__ == '__main__':
+ # Test cases defined in this file:
+ test_defined = ['14187024', '14165033', '14315008', '14165035', '14314007',
+ '11865133', '14335001', '14266498', '14319149', '14314008',
+ '11722375', '14315009', '14314009', '14165038', '15643845',
+ '14165034']
+ # To execute test cases that defined in config file:
+ test = DataModelTest(testID='14165034')
+ test_to_execute = testCase.TestCase.FindTestCaseToExecute('config.cfg')
+ for test_id in test_to_execute:
+ if test_id in test_defined:
+ test = DataModelTest(testID=test_id)
diff --git a/device.py b/device.py
index c02fa33..f3eb65c 100644
--- a/device.py
+++ b/device.py
@@ -1,17 +1,24 @@
#!/usr/bin/python
# Copyright 2012 Google Inc. All Rights Reserved.
-"""This class defines devices."""
+"""This class defines devices.
-__author__ = 'Lin Xue (linxue@google.com)'
-
-"""This file defines the base class of all devices,
+ This file defines the base class of all devices,
and specific device classes, such as Bruno, Spirent, etc.
"""
+__author__ = 'Lin Xue (linxue@google.com)'
+__author__ = 'Lehan Meng (lmeng@google.com)'
+
+
+import re
+
+import ip
+
class Device(object):
"""Base class of all devices.
+
information of device class:
addr: address of device
user: user name of device
@@ -23,24 +30,14 @@
other: other information of device
"""
- dev_info = {'addr': 'None',
- 'user': 'None',
- 'pwd': 'None',
- 'dev_name': 'None',
- 'cmd_prompt': 'None',
- 'other': 'None'}
-
def GetDevInfo(self):
"""This function returns device information."""
-
return (self.dev_info['dev_name'], self.dev_info['other'])
def SetLogging(self, logging):
- """This function set logging for device"""
-
+ """This function set logging for device."""
self.log = logging
return
-
# End of Device class
@@ -48,13 +45,20 @@
"""Bruno device class."""
def __init__(self, **kwargs):
- """ Constructor for Bruno device
- device name: Bruno, and other device informations
+ """Constructor for Bruno device.
+
+ device name: Bruno, and other device informations
"""
+ self.dev_info = {'addr': 'None',
+ 'user': 'None',
+ 'pwd': 'None',
+ 'dev_name': 'None',
+ 'cmd_prompt': 'None',
+ 'other': 'None'}
+
self.dev_info['dev_name'] = 'Bruno'
for s in kwargs:
- self.dev_info[s] = kwargs[s]
-
+ self.dev_info[s] = kwargs[s]
self.dev_ssh = None
# Bruno Device attributes
@@ -76,26 +80,26 @@
self.dev_epgpri = None
# EPG secondary
self.dev_epgsec = None
-
return
def GetDevCmd(self, cmd='', dev_attr='', dev_log=''):
"""This function sends command to Bruno.
+
Then it gets back the results
If success, save the results and write log
If fail, show Warning message
- Args:
- cmd:
- the command sent to Bruno system
- to query the device information
- dev_attr:
- the device attribute
- which can be get from this command
- dev_log:
- the logging message to be shown in log
-
+ Args:
+ cmd:
+ the command sent to Bruno system
+ to query the device information
+ dev_attr:
+ the device attribute
+ which can be get from this command
+ dev_log:
+ the logging message to be shown in log
+ Returns:
+ return the command results
"""
-
# Send command to query Bruno device information
self.dev_ssh.SendCmd(cmd)
@@ -111,8 +115,7 @@
'Get ' + dev_log + ': ' + line)
self.log.SendLine(None, info)
dev_attr = line
-
- return line
+ return line.strip()
def GetSerialNumber(self):
"""This function returns Bruno device serial number."""
@@ -130,20 +133,107 @@
return result
- def GetIPAddress(self):
- """This function returns Bruno device IP address."""
+ def VerifyCurrentVersion(self, version):
+ """Check if the current software version matches the given version.
+ Args:
+ version: the expected version string to check with device software
+ Returns:
+ True: if the current version matches the given version
+ False: if otherwise
+ """
+ if version == self.GetCurrentVersion():
+ return True
+ else:
+ return False
+
+ def GetIPaddress(self):
+ """This function returns Bruno device IP address."""
+ # Obsoleted
result = self.GetDevCmd(r'ifconfig br0', self.dev_ip,
'IP address')
return result
+ def GetDNSServer(self):
+ """This function returns device DNS server name."""
+ self.dev_ssh.SendCmd('cat /etc/resolv.conf')
+ cmd_list = self.dev_ssh.GetCmdOutput()
+ dns = None
+ for line in cmd_list:
+ m = re.search('nameserver (.*)', line)
+ if m:
+ dns = m.group(1)
+ break
+ return dns
+
+ def GetTimeServer(self):
+ """This function returns device DNS server name."""
+ self.dev_ssh.SendCmd('cat /etc/ntpd.conf')
+ cmd_list = self.dev_ssh.GetCmdOutput()
+ ts = None
+ for line in cmd_list:
+ m = re.search('server (.*)', line)
+ if m:
+ ts = m.group(1)
+ break
+ return ts
+
+ def GetGVSBHost(self):
+ """This function returns device GVSB host server name."""
+ self.dev_ssh.SendCmd('cat /tmp/gvsbhost')
+ cmd_line = self.dev_ssh.GetCmdOutput(2)[0]
+ m = re.search('http[s]?://[\w\d\./-]*', cmd_line)
+ if m:
+ return m.group(0)
+ return None
+
+ def GetIPv4Address(self):
+ """This function returns Bruno device IPv4 address."""
+ self.dev_ssh.SendCmd('ip addr show dev br0')
+ cmd_list = self.dev_ssh.GetCmdOutput()
+ address = None
+ for line in cmd_list:
+ if 'inet ' in line and 'scope global' in line:
+ address = ip.IPADDR(line).IsLikeIpv4Address()
+
+ if not address:
+ info = self.log.CreateErrorInfo('critical',
+ 'Can not detect IPv4 address on device!')
+ self.log.SendLine(None, info)
+ return address
+
+ def GetIPv6Address(self):
+ """This function returns Bruno device IPv6 address.
+
+ This function will return a list of addresses. Sometimes device may
+ have multiple IPv6 addresses.
+
+ Returns:
+ return the obtained IPv6 address
+ """
+ self.dev_ssh.SendCmd('ip addr show dev br0')
+ cmd_list = self.dev_ssh.GetCmdOutput()
+ address = []
+ for line in cmd_list:
+ if 'inet6 ' in line and 'scope global' in line:
+ print line
+ m = re.match('inet6 ([\da-f:]+)/\d+ scope global \w*', line.strip())
+ if not m:
+ info = self.log.CreateErrorInfo(
+ 'critical', 'Can not detect valid IPv6 address '
+ 'on device! ' + line)
+ self.log.SendLine(None, info)
+ return address
+ addr = m.group(1)
+ address.append(addr)
+ return address
+
def GetDevTime(self):
"""This function returns Bruno device current date."""
result = self.GetDevCmd(r'date', self.dev_time,
'current date')
-
return result
def GetACSUrl(self):
@@ -151,9 +241,22 @@
result = self.GetDevCmd(r'cat /tmp/cwmp/acs_url', self.dev_acs,
'ACS URL')
-
return result
+ def VerifyCurrentACSURL(self, url):
+ """Check if the current ACS URL matches the given URL.
+
+ Args:
+ url: the expected url string to check with device ACS URL
+ Returns:
+ True: if the current url matches the given url
+ False: otherwise
+ """
+ if url == self.GetACSUrl():
+ return True
+ else:
+ return False
+
def GetDnldImage(self):
"""This function returns Bruno device current downloading image."""
@@ -163,7 +266,7 @@
return result
def GetProductClass(self):
- """This function returns Bruno device product class"""
+ """This function returns Bruno device product class."""
result = self.GetDevCmd(r'cat /etc/platform', self.dev_class,
'product class')
@@ -171,7 +274,7 @@
return result
def GetEpgPrimary(self):
- """This function returns Bruno device primary EPG"""
+ """This function returns Bruno device primary EPG."""
result = self.GetDevCmd(r'cat /tmp/epgprimary', self.dev_epgpri,
'EPG primary')
@@ -179,7 +282,7 @@
return result
def GetEpgSecond(self):
- """This function returns Bruno device secondary EPG"""
+ """This function returns Bruno device secondary EPG."""
result = self.GetDevCmd(r'cat /tmp/epgsecondary', self.dev_epgsec,
'EPG secondary')
@@ -197,7 +300,7 @@
# Other device informations
self.GetSerialNumber()
self.GetCurrentVersion()
- self.GetIPAddress()
+ self.GetIPv4Address()
self.GetDevTime()
self.GetACSUrl()
self.GetDnldImage()
@@ -207,6 +310,59 @@
return
+ def GetTimeStamp(self, line=None):
+ """Extract the time information of most current event from Device log.
+
+ i.e., the time that the last event happens since reboot (in seconds)
+ Args:
+ line: a line of device log which contains event time information.
+ If it has value of 'None', then retrieve the last a few lines from
+ device log to extract time information.
+ Returns:
+ return the time stamp string if succeed, return -1 otherwise.
+ """
+ if not line:
+ # get time stamp from Device log file
+ self.dev_ssh.SendCmd('dmesg | grep cwmpd:')
+ log_list = self.dev_ssh.GetCmdOutput()
+
+ for line in log_list:
+ m = re.match('\\[[\s]*(\d*\\.\d{3})\\]', line)
+ if m:
+ # Match, return the time stamp
+ return m.group(1)
+ else:
+ # get time stamp from a Device log line
+ m = re.match('\\[[\s]*(\d*\\.\d{3})\\]', line)
+ if m:
+ # Match, return the time stamp
+ return m.group(1)
+ # No time stamp found
+ return -1
+
+ def FindLineInLog(self, log_list, time_stamp, pattern_line):
+ """Match a particular string from device logs.
+
+ Find a particular string or line from device log
+ Args:
+ log_list: the device log to search from
+ time_stamp: to search events that are after this time
+ pattern_line: the string or line that need to be matched in device log
+ Returns:
+ True: if 'line' matched
+ False: 'line' not found in device log
+ """
+ m = re.compile(pattern_line)
+ for line in log_list:
+ if float(self.GetTimeStamp(line)) > float(time_stamp):
+ #Debug
+ if 'detected' in line:
+ print 'here'
+
+ matching = m.search(line)
+ if matching:
+ return True
+ return False
# End of Bruno device class
@@ -214,7 +370,8 @@
"""Spirent device class."""
def __init__(self, **kwargs):
- """ Constructor for Spirent device
+ """Constructor for Spirent device.
+
device name: Spirent, and other device informations
"""
diff --git a/download.py b/download.py
index 475b287..d39b6e4 100755
--- a/download.py
+++ b/download.py
@@ -4,18 +4,11 @@
"""This class defines the test case "image download" from ACS.
This class extends the TestCase Class.
-testID: 11843140
-This test does the following tasks:
- - check current image version
- - if the current version is the same as expected version,
- this test will first to try to downgrade it to the downgrade_version.
- - Then, it will Run image download test
- - Fianlly, it will downgrade to downgrade_version if this has not been
- done. This is to test the download handling capabilities of the
- expected_version
- - configure ACS instance with these software versions
- - verify image download and installation to be complete and successful, by
- observing a successfully reboot after installation
+This file includes the following test cases:
+ - testID: 11843140 7.6.11 Verify firmware upgrade/downgrade by ACS
+ - testID: 14165036 7.6.18 Negative test: Bruno Image download interruption
+ - testID: 14334001 7.6.25.3 File Transfer (File upload is optional, this is
+ then basically the same test case as download.)
"""
__author__ = 'Lehan Meng (lmeng@google.com)'
@@ -43,20 +36,17 @@
title =Image_Download
current_version=None
expected_version=bruno-iguana-2-dg
- ACS_URL = https://gfiber-acs-staging.appspot.com/cwmp
+ acs_url = https://gfiber-acs-staging.appspot.com/cwmp
"""
def Init(self):
"""Initialte the DownloadTest instance."""
- self.p_ssh = ssh.SSH(user=self.params['user'],
- addr=self.params['addr'],
- pwd=self.params['pwd'],
- bruno_prompt=self.params['bruno_prompt'],
- addr_ipv6=self.params['addr_ipv6'],
- athena_user=self.params['athena_user'],
- athena_pwd=self.params['athena_pwd'],
- jump_server=self.params['jump_server'])
- self.p_ssh.Setlogging(self.log)
+ if not self.p_ssh:
+ self.p_ssh = ssh.SSH(user=self.params['user'],
+ addr=self.params['addr'],
+ bruno_prompt=self.params['bruno_prompt'],
+ addr_ipv6=self.params['addr_ipv6'])
+ self.p_ssh.SetLogging(self.log)
self.__debug = False
self.__short_delay = 5 #short delay: 5 seconds
self.__delay = 180 #medium delay: 180 seconds
@@ -69,18 +59,18 @@
"""
self.p_ssh.SendCmd('cat /tmp/cwmp/acs_url')
line = self.p_ssh.GetCmdOutput()[1]
- m_1 = re.match(self.params['ACS_URL'], line)
+ m_1 = re.match(self.params['acs_url'], line)
if m_1 is None:
info = self.log.CreateErrorInfo(
'intermediate',
'ACS URL unknown! Update ACS URL manually')
self.log.SendLine(self.test_info, info)
- self.p_ssh.SendCmd('echo ' + self.params['ACS_URL']
+ self.p_ssh.SendCmd('echo ' + self.params['acs_url']
+ ' > /tmp/cwmp/acs_url')
- self.AddConfigParams('current_ACS_url', self.params['ACS_URL'])
+ self.AddConfigParams('current_ACS_url', self.params['acs_url'])
else:
- self.AddConfigParams('current_ACS_url', self.params['ACS_URL'])
+ self.AddConfigParams('current_ACS_url', self.params['acs_url'])
def SetACSUrl(self, url='http://acs-sandbox.appspot.com/cwmp'):
"""Set ACS URL on the device."""
@@ -115,8 +105,7 @@
# get time stamp from Device log file
self.p_ssh.SendCmd('dmesg | grep cwmpd:')
# search the last 30 lines in Device log for time information
- latest_log_entry = 30
- log_list = self.p_ssh.GetCmdOutput(latest_log_entry)
+ log_list = self.p_ssh.GetCmdOutput()
for line in log_list:
m = re.match('\\[[\s]*(\d*\\.\d{3})\\]', line)
@@ -131,30 +120,29 @@
return m.group(1)
# No time stamp found
- return -1
+ info = self.log.createErrorInfo(
+ 'Critical', 'Exit! Can not find time information from '
+ 'device log ...')
+ self.log.sendLine(self.test_info, info)
+ return os.sys.exit(1)
- def ConfigACS(self):
+ def ConfigACS(self, version_id):
"""Setup the ACS device profile with expected image.
Then restart the cwmpd process on device to
initiate the inform/download process
+ Args:
+ version_id: the expected version id that needs to be configured
Returns:
return the time stamp string if succeed, return -1 otherwise.
"""
#configure ACS:
- acs_instance = acs.ACS(self.params['expected_version_id'])
+ acs_instance = acs.ACS(acs_path=self.params['acs_path'])
acs_instance.SetLogging(self.log)
- acs_instance.SaveConfiguration()
+ acs_instance.SaveConfiguration(
+ self.params['profile_id'], version_id, self.params['acs_app_id'],
+ self.params['acs_host_name'])
- #self.p_ssh.SendCmd('/etc/Init.d/S85catawampus restart')
- # To setup any device service parameter using nbi_client will trigger
- # ACS to make a remote connection request to the device.
- # However, currently ACS does not provide separate stubby call to
- # only request a remote connection.
- # As walk around:
- # 1. call remote management URL locally to initiate Inform immediately
- # 2. wait for the next periodic inform to initiate download
- # Here go for the 2nd option.
time_stamp = self.GetTimeStamp()
return time_stamp
@@ -220,30 +208,34 @@
return True if rebooted
return False otherwise
"""
+ time.sleep(5)
s = self.GetTimeStamp()
- if float(s) < float(time_stamp):
+ print 'previous time stamp: ' + time_stamp
+ print 'current time stamp: ' + s
+ if float(s) < float(time_stamp) and s is not -1:
return True
return False
- def CheckImageInstallation(self, time_stamp):
+ def CheckImageInstallation(self, time_stamp, version):
"""Check if the Image installation and Device reboot is successful.
An upgrade is considered complete only after the device is rebooted
after image installation
Args:
time_stamp: check if a reboot is done after this time
+ version: the image version that needs to be installed
Returns:
return True if installation succeeded
return False otherwise
"""
- self.p_ssh.ExitToAthena()
- time.sleep(3*self.__short_delay)
- self.p_ssh.SshRetryFromAthena()
+ self.p_ssh.Close()
+ time.sleep(6*self.__short_delay)
+ self.p_ssh.SshRetry()
count = 0
ts = time_stamp
while not self.IsRebooted(ts):
count +=1
- if count*self.__short_delay*3 > self.__long_delay:
+ if count*self.__short_delay*6 > self.__long_delay:
info = self.log.CreateErrorInfo(
'critical',
'Error! File download longer than ' + str(self.__long_delay)
@@ -252,61 +244,56 @@
os.sys.exit(1)
t = self.GetTimeStamp()
- if t > ts:
+ if float(t) > float(ts):
ts = t
- self.p_ssh.ExitToAthena()
+ self.p_ssh.Close()
info = self.log.CreateProgressInfo(
- '90%', 'Wait ' + str(3*self.__short_delay)
+ '---', 'Wait ' + str(6*self.__short_delay)
+ ' seconds for image installation and device reboot...')
self.log.SendLine(self.test_info, info)
- time.sleep(3*self.__short_delay)
- self.p_ssh.SshRetryFromAthena()
+ time.sleep(6*self.__short_delay)
+ self.p_ssh.SshRetry()
time.sleep(2*self.__short_delay)
self.p_ssh.SendCmd('more /etc/version')
line = self.p_ssh.GetCmdOutput()[1]
crt_version = line.strip()
- print crt_version + '########' + self.params['expected_version']
- if crt_version == self.params['expected_version']:
+ print crt_version + '########' + version
+ if crt_version == version:
return True
else:
return False
- def Run(self):
- """Run the test case."""
- ####### Add your code here -- BEGIN #######
- print 'Test Started...'
- self.Init()
- self.p_ssh.SshToAthena()
- info = self.log.CreateProgressInfo(
- '5%', 'SSH session to Athena server successfully established!')
- self.log.SendLine(self.test_info, info)
+ def VerifyImageDownload(self):
+ """Verify image download and upgrade.
- max_test_idx = 2
+ This test checks the following:
+ - check current image version
+ - if current_version is not expected_version as defined in config file:
+ - Upgrade to expected_version (verify expected version can boot up)
+ - Downgrade from expected_version to downgrade_version
+ (verify downgrade handling capability of expected_version image)
+ - Finally upgrade to expected_version, and keep device in this state.
+ - if the current version is already the expected_version,
+ - Downgrade from expected_version to downgrade_version
+ (verify downgrade handling capability of expected_version image)
+ - Finally upgrade to expected_version, and keep device in this state.
+
+ At the same time, it also verifies:
+ - configure ACS with these software versions
+ """
+ max_test_idx = 3
current_test_idx = 1
+ upgrade = True
- while current_test_idx < max_test_idx:
- self.p_ssh.SshRetryFromAthena()
+ while current_test_idx <= max_test_idx:
info = self.log.CreateResultInfo(
'---', 'Test Sequence Number: ' + str(current_test_idx))
self.log.SendLine(self.test_info, info)
- while self.CheckDownloadFileExists():
- # current in download
- info = self.log.CreateProgressInfo(
- '10%', 'Another downloading in progress, retry after '
- + str(self.__short_delay) + 'seconds...')
- self.log.SendLine(self.test_info, info)
- self.p_ssh.ExitToAthena()
- time.sleep(self.__short_delay)
- self.p_ssh.SshRetryFromAthena()
-
- info = self.log.CreateProgressInfo(
- '15%', 'SSH session to device successfully established!')
- self.log.SendLine(self.test_info, info)
self.CheckACSUrl()
info = self.log.CreateProgressInfo('20%', 'Current ACS url: '
- + self.params['ACS_URL'])
+ + self.params['acs_url'])
self.log.SendLine(self.test_info, info)
self.CheckVersion()
@@ -314,47 +301,199 @@
+ self.params['current_version'])
self.log.SendLine(self.test_info, info)
- # Need to do:
- # check if the currently version is already expected version,
- # if so, downgrade first, then Run this test
- #if self.params['current_version'] == self.params['expected_version']:
- # info = self.log.createErrorInfo(
- # 'low', 'Currently image is expected version, '
- # + 'please try other images.')
- # self.log.sendLine(self.test_info, info)
+ if self.params['current_version'] == self.params['expected_version']:
+ if current_test_idx == 1:
+ current_test_idx += 1
+ upgrade = False
+ continue
- self.time_stamp = self.ConfigACS()
- info = self.log.CreateProgressInfo('40%', 'Expected Version: '
- + self.params['expected_version'])
- self.log.SendLine(self.test_info, info)
+ version = None
+ if upgrade:
+ self.time_stamp = self.ConfigACS(self.params['expected_version_id'])
+ info = self.log.CreateProgressInfo('40%', 'Expected Version: '
+ + self.params['expected_version'])
+ self.log.SendLine(self.test_info, info)
+ version = self.params['expected_version']
+ else:
+ self.time_stamp = self.ConfigACS(self.params['downgrade_version_id'])
+ info = self.log.CreateProgressInfo('40%', 'Downgrade Version: '
+ + self.params['downgrade_version'])
+ self.log.SendLine(self.test_info, info)
+ version = self.params['downgrade_version']
+
info = self.log.CreateProgressInfo(
- '40%', 'Successfully configured ACS for expected image version')
+ '50%', 'Successfully configured ACS for image version: ' + version)
self.log.SendLine(self.test_info, info)
time.sleep(self.__short_delay) # time delay for log update
- if self.CheckImageInstallation(self.time_stamp):
+ self.time_stamp = self.GetTimeStamp()
+ if self.CheckImageInstallation(self.time_stamp, version):
info = self.log.CreateProgressInfo(
'100%', 'Image successfully upgraded to expected version: '
'from: ' + self.params['current_version']
- + ' to: ' + self.params['expected_version'])
+ + ' to: ' + version)
self.log.SendLine(self.test_info, info)
info = self.log.CreateResultInfo(
'Pass', 'Image successfully upgraded to expected version: '
'from: ' + self.params['current_version']
- + ' to: ' + self.params['expected_version'])
+ + ' to: ' + version)
self.log.SendLine(self.test_info, info)
else:
info = self.log.CreateErrorInfo(
'critical',
'Error! Image version does not match expected version!')
self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Failed', 'Image failed to upgraded to expected version: '
+ 'from: ' + self.params['current_version']
+ + ' to: ' + version)
+ self.log.SendLine(self.test_info, info)
current_test_idx += 1
- self.p_ssh.ExitToAthena()
- # Need to do:
- # downgrade from expected_version
- # To verify the capability of the download handling on expected version
- # image
+ if upgrade:
+ upgrade = False
+ else:
+ upgrade = True
+
+ def ImageUpgrade(self, expected_version, expected_version_id):
+ """This function upgrade device image to expected version.
+
+ Args:
+ expected_version: the version to be upgraded to
+ expected_version_id: the id of the expected version
+ Returns:
+ True if upgrade succeeds
+ False if upgrade fails
+ """
+ version = self.GetVersion()
+ if version == expected_version:
+ info = self.log.CreateProgressInfo(
+ '---', 'Image is already expected versioin: '
+ + expected_version)
+ self.log.SendLine(self.test_info, info)
+ return True
+ self.time_stamp = self.ConfigACS(expected_version_id)
+ info = self.log.CreateProgressInfo(
+ '---', 'Successfully configured ACS for image version: '
+ + expected_version)
+ self.log.SendLine(self.test_info, info)
+
+ time.sleep(self.__short_delay) # time delay for log update
+ self.time_stamp = self.GetTimeStamp()
+ if self.CheckImageInstallation(self.time_stamp, expected_version):
+ info = self.log.CreateProgressInfo(
+ '---', 'Image successfully upgraded to expected version: '
+ 'from: ' + version + ' to: ' + expected_version)
+ self.log.SendLine(self.test_info, info)
+ return True
+ else:
+ info = self.log.CreateErrorInfo(
+ 'critical',
+ 'Error! Image version does not match expected version!')
+ self.log.SendLine(self.test_info, info)
+ return False
+
+ def VerifyDownloadInterrupt(self):
+ """Negative test case: verify image download interruption.
+
+ This case tests device's capability of image upgrade in case of
+ power loss, power cycle, or multiple download requests from ACS.
+ The device should finally be able to complete the download and
+ image upgrade process from expected_version to downgrade_version.
+
+ Returns:
+ return: False if the verification fails
+ return: True otherwise
+ """
+ self.CheckVersion()
+ if self.params['current_version'] != self.params['expected_version']:
+ info = self.log.CreateProgressInfo(
+ '5%', 'Current version is not expected version to test, upgrade to: '
+ + self.params['expected_version'])
+ self.log.SendLine(self.test_info, info)
+ if not self.ImageUpgrade(self.params['expected_version'],
+ self.params['expected_version_id']):
+ info = self.log.CreateErrorInfo(
+ 'critical',
+ 'Failed to upgrade version to: '
+ + self.params['expected_version'])
+ self.log.SendLine(self.test_info, info)
+ return False
+ self.ConfigACS(self.params['downgrade_version_id'])
+ info = self.log.CreateProgressInfo(
+ '10%', 'Successfully configured ACS for image version: '
+ + self.params['downgrade_version'])
+ self.log.SendLine(self.test_info, info)
+
+ time.sleep(6*self.__short_delay)
+
+ info = self.log.CreateProgressInfo(
+ '40%', 'Download started, reboot the device.')
+ self.log.SendLine(self.test_info, info)
+ time_stamp = self.GetTimeStamp()
+ self.p_ssh.SendCmd('reboot')
+ self.p_ssh.GetCmdOutput()
+ while not self.IsRebooted(time_stamp):
+ self.p_ssh.Close()
+ time.sleep(self.__short_delay*3)
+ self.p_ssh.SshRetry()
+ info = self.log.CreateProgressInfo(
+ '60%', 'Downloading interrupted, waiting for device boot up.')
+ self.log.SendLine(self.test_info, info)
+
+ time_stamp = self.GetTimeStamp()
+ version = self.GetVersion()
+ if version != self.params['downgrade_version']:
+ count = 0
+ while not self.IsRebooted(time_stamp):
+ count += 1
+ if count*self.__short_delay > self.__long_delay:
+ info = self.log.CreateErrorInfo(
+ 'critical',
+ 'Error! Image downloading is not successful after interruption!')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Failed',
+ 'Image failed to upgraded to version: '
+ + self.params['downgrade_version'] + ' ('
+ + self.__long_delay + ' seconds) after interrupted.')
+ self.log.SendLine(self.test_info, info)
+ return False
+ time_stamp = self.GetTimeStamp()
+ info = self.log.CreateProgressInfo(
+ '80%', 'Waiting for downloading complete after interruption.')
+ self.log.SendLine(self.test_info, info)
+ time.sleep(self.__short_delay*5)
+
+ info = self.log.CreateProgressInfo(
+ '100%', 'Image successfully upgraded to version: '
+ + self.params['downgrade_version'] + ' after interrupted.')
+ self.log.SendLine(self.test_info, info)
+ info = self.log.CreateResultInfo(
+ 'Pass', 'Image successfully upgraded to version: '
+ + self.params['downgrade_version'] + ' after interrupted.')
+ self.log.SendLine(self.test_info, info)
+ return True
+
+ def Run(self):
+ """Run the test case."""
+ ####### Add your code here -- BEGIN #######
+ print 'Test Started...'
+ self.Init()
+ self.p_ssh.SshRetry()
+ info = self.log.CreateProgressInfo(
+ '5%', 'SSH session to device successfully established!')
+ self.log.SendLine(self.test_info, info)
+
+ if self.test_info['testID'] == '11843140':
+ self.VerifyImageDownload()
+ info = self.log.CreateResultInfo(
+ '----', 'For file transfer test, refer to image download test result:'
+ ' testID: 11843140.')
+ self.log.SendLine(self.test_info, info)
+ elif self.test_info['testID'] == '14165036':
+ self.VerifyDownloadInterrupt()
+
print 'Test Completed...'
####### Add your code here -- END #######
@@ -365,4 +504,10 @@
if __name__ == '__main__':
- DownloadTest(testID='11843140', key_word='Download')
\ No newline at end of file
+ # Test cases defined in this file:
+ test_defined = ['11843140', '14165036', '14334001']
+ # To execute test cases that defined in config file:
+ test_to_execute = testCase.TestCase.FindTestCaseToExecute('config.cfg')
+ for test_id in test_to_execute:
+ if test_id in test_defined:
+ test = DownloadTest(testID=test_id)
diff --git a/logging.py b/logging.py
old mode 100644
new mode 100755
index ade9f50..dd1654a
--- a/logging.py
+++ b/logging.py
@@ -16,17 +16,13 @@
import datetime
+import errno
+import os
class Logging(object):
"""This Class collects and writes test information to log files."""
- params = {'std_out': '1',
- 'f_result': 'result.log',
- 'f_error': 'error.log',
- 'f_progress': 'progress.log',
- 'delimiter': '\t'}
-
def __init__(self, **kwargs):
"""initiate the logging instance.
@@ -36,15 +32,129 @@
kwargs['f_progres']: file name for test progres
kwargs['std_out']: by default, send a copy of the log info to stdout
"""
- for s in ('f_result', 'f_error', 'f_progress', 'std_out'):
- if s in kwargs:
- self.params[s] = kwargs[s]
- self.rst_file = open(self.params['f_result'], 'a')
- self.err_file = open(self.params['f_error'], 'a')
- self.prg_file = open(self.params['f_progress'], 'a')
+ self.params = {'std_out': '1',
+ 'f_result': 'result.log',
+ 'f_error': 'error.log',
+ 'f_progress': 'progress.log',
+ 'delimiter': '\t',
+ 'out_folder': 'log',
+ 'in_folder': 'data'}
+ for s in kwargs:
+ self.params[s] = kwargs[s]
+
+ try:
+ os.makedirs(self.params['out_folder'])
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ now = datetime.datetime.now()
+ s_time = now.strftime('%Y-%m-%d_%H:%M:%S.%f')
+ for f_name in ['f_result', 'f_error', 'f_progress']:
+ name = self.ParseLogFileName(self.params[f_name], 'w', s_time)
+ if name:
+ self.params[f_name] = name
+ else:
+ print 'Error! Fail to create log File.'
+ self.rst_file = open(self.params['f_result'], 'w+')
+ self.err_file = open(self.params['f_error'], 'w+')
+ self.prg_file = open(self.params['f_progress'], 'w+')
self.NewTestStart()
+ def CreateFile(self, f_name, access='r'):
+ """Create a file for read/write.
+
+ Args:
+ f_name: the file name that needs to be created
+ access: 'r': read, input file
+ 'w': write, output file
+ 'rw': read and write
+ Returns:
+ f_obj: return the file object
+ if not succeed, return False
+ """
+ f_obj = None
+ if access == 'r':
+ # for input file
+ try:
+ f_obj = open(f_name, 'r')
+ except IOError:
+ print 'Cannot open the input file, return'
+ return False
+ elif access == 'w':
+ # for input file
+ try:
+ os.makedirs(self.params['out_folder'])
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ print 'Cannot create folder! Return'
+ return False
+ full_path = self.ParseLogFileName(f_name, access)
+ if full_path:
+ try:
+ f_obj = open(full_path, 'w+')
+ except IOError:
+ print 'Cannot open the given file, return'
+ return False
+ else:
+ # Can not parse file name
+ print 'Cannot parse given file name'
+ return False
+ else:
+ # read-write file:
+ try:
+ f_obj = open(f_name, 'r')
+ except IOError:
+ print 'Cannot open the input file, return'
+ return False
+ return f_obj
+
+ def ParseLogFileName(self, f_name, access='r', time_stamp=None):
+ """Generate an uniq log file name for each test case instance.
+
+ Args:
+ f_name: file name
+ access: 'r': read mode
+ 'w': write mode
+ 'rw': read-write
+ time_stamp: the time tag used for file name
+ if None, create a new tag using current time
+ Returns:
+ return the parsed file name
+ """
+ if not f_name:
+ f_name = 'logFile.log'
+ if not time_stamp:
+ now = datetime.datetime.now()
+ s_time = now.strftime('%Y-%m-%d_%H:%M:%S.%f')
+ else:
+ s_time = time_stamp
+
+ ls = f_name.split('.')
+ ext_name = ''
+ name = ''
+ if len(ls) == 1:
+ name = ls[0]
+ else:
+ ext_name = ls.pop(-1)
+ for s in ls:
+ name += (s+'.')
+ name = name.rstrip('.')
+
+ if not ext_name:
+ ext_name = 'log'
+ folder = None
+ if access == 'r':
+ folder = self.params['in_folder']
+ elif access == 'w':
+ folder = self.params['out_folder']
+ else:
+ folder = self.params['in_folder']
+ name = (folder + '/' + name + '_testID-' + self.params['testID']
+ + '_'+ s_time + '.' + ext_name)
+ return name
+
def SendLineToResult(self, test_info, result_info):
"""Send test result (one-line) information to the result file.
@@ -56,7 +166,7 @@
"""
now = datetime.datetime.now()
if test_info is not None:
- line = (now.strftime('%Y-%m-%d %H:%M') + self.params['delimiter']
+ line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter']
+ 'testID:' + test_info['testID']
+ self.params['delimiter'] + 'TestStartAt:'
+ test_info['start_time']
@@ -69,7 +179,8 @@
if self.params['std_out'] > 0:
print line
- return self.rst_file.write(line)
+ self.rst_file.write(line)
+ self.rst_file.flush()
def NewTestStart(self):
"""Mark the start of a new test in the file."""
@@ -91,7 +202,7 @@
"""
now = datetime.datetime.now()
if test_info is not None:
- line = (now.strftime('%Y-%m-%d %H:%M') + self.params['delimiter']
+ line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter']
+ 'testID:' + test_info['testID'] + self.params['delimiter']
+ 'TestStartAt:' + test_info['start_time']
+ self.params['delimiter'] + 'Keyword:'
@@ -99,13 +210,14 @@
+ error_info['severity'] + self.params['delimiter']
+ error_info['note'] + '\n')
else:
- line = (now.strftime('%Y-%m-%d %H:%M') + self.params['delimiter'] +
+ line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter'] +
error_info['severity'] + self.params['delimiter']
+ error_info['note'] + '\n')
if self.params['std_out'] > 0:
print line
- return self.err_file.write(line)
+ self.err_file.write(line)
+ self.err_file.flush()
def SendLineToProgress(self, test_info, progress_info):
"""Send test progress (one-line) information to the progress file.
@@ -119,20 +231,21 @@
"""
now = datetime.datetime.now()
if test_info is not None:
- line = (now.strftime('%Y-%m-%d %H:%M') + self.params['delimiter']
+ line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter']
+ 'testID:' + test_info['testID'] + self.params['delimiter']
+ 'TestStartAt:'+ test_info['start_time']
+ self.params['delimiter'] + 'Keyword:' + test_info['key_word']
+ self.params['delimiter'] + progress_info['percent']
+ self.params['delimiter'] + progress_info['note'] + '\n')
else:
- line = (now.strftime('%Y-%m-%d %H:%M') + self.params['delimiter']
+ line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter']
+ progress_info['percent'] + self.params['delimiter']
+ progress_info['note'] + '\n')
if self.params['std_out'] > 0:
print line
- return self.prg_file.write(line)
+ self.prg_file.write(line)
+ self.prg_file.flush()
def SendLine(self, test_info, info):
"""Send test (one-line) information to the corresponding log file.
@@ -151,12 +264,18 @@
elif info['type'] == 'progress':
return self.SendLineToProgress(test_info, info)
+ def SendTestData(self, data_list):
+ """Send test data to result file."""
+ for line in data_list:
+ self.rst_file.write(line + '\n')
+ self.err_file.flush()
+
def SendLines(self, test_info, info):
"""Send test output (multiple lines) to the corresponding log file.
Args:
test_info: test case related info: testID, start_time, key_word, etc
- info: test results/error/progress information (single line)
+ info: test results/error/progress information
test_info could be "None" for class other than TestCase
Returns:
return 1 when succeed
@@ -242,4 +361,4 @@
"""Close the log files."""
self.rst_file.close()
self.err_file.close()
- self.prg_file.close()
\ No newline at end of file
+ self.prg_file.close()
diff --git a/run.py b/run.py
index b0e5088..1130dc7 100755
--- a/run.py
+++ b/run.py
@@ -17,7 +17,7 @@
optspec = """
run [options...] <hosts> -- <command string...>
--
-r,raw Raw output (don't prefix with the host's name)
+r,raw Raw output (don't redirect stdin/stdout/stderr)
q,quiet Quiet: don't print to stderr for nonzero exit codes
"""
@@ -42,27 +42,32 @@
class Handler(object):
- def __init__(self, prefix, fileobj, raw):
+ """When data is received from the given file, print it line by line."""
+
+ def __init__(self, prefix, fileobj):
self.prefix = prefix
self.fileobj = fileobj
- self.raw = raw
+ self.buf = ''
+
+ def __del__(self):
+ if self.buf and not self.buf.endswith('\n'):
+ self.buf += '\n'
+ self.PrintBuf()
def fileno(self): #gpylint: disable-msg=C6409
return self.fileobj.fileno()
+ def PrintBuf(self):
+ while '\n' in self.buf:
+ line, self.buf = self.buf.split('\n', 1)
+ print '%s: %s' % (self.prefix, line)
+
def Run(self):
"""Read from this handler and write its results to stdout."""
- buf = os.read(self.fileobj.fileno(), 65536)
- if buf:
- if self.raw:
- sys.stdout.write(buf)
- else:
- if not buf.endswith('\n'):
- buf += '\n'
- lines = buf.split('\n')[:-1]
- for line in lines:
- print '%s: %s' % (self.prefix, line)
- return buf
+ got = os.read(self.fileobj.fileno(), 65536)
+ self.buf += got
+ self.PrintBuf()
+ return got
def main():
@@ -95,11 +100,14 @@
else:
argv = cmd[0]
shell = True
- p = subprocess.Popen(argv, env=env, shell=shell,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ if opt.raw:
+ p = subprocess.Popen(argv, env=env, shell=shell)
+ else:
+ p = subprocess.Popen(argv, env=env, shell=shell,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ handlers.append(Handler(host.name, p.stdout))
+ handlers.append(Handler(host.name, p.stderr))
procs.append((host, p))
- handlers.append(Handler(host.name, p.stdout, opt.raw))
- handlers.append(Handler(host.name, p.stderr, opt.raw))
while handlers:
r, _, _ = select.select(handlers, [], [])
diff --git a/ssh.py b/ssh.py
old mode 100644
new mode 100755
index 8b9f2e3..7232542
--- a/ssh.py
+++ b/ssh.py
@@ -54,9 +54,10 @@
'bruno_prompt': '\(none\)#',
'jump_server': 'jmp.googlefiber.net',
'jump_prompt': None,
- 'athena_user': None,
- 'athena_pwd': None,
- 'athena_prompt': None}
+ 'athena_user': 'lmeng',
+ 'athena_pwd': 'changeme',
+ 'athena_prompt': None,
+ 'ssh_use_athena': False}
self.tmp_file = tempfile.TemporaryFile(mode='w+t')
@@ -66,19 +67,13 @@
for s in kwargs:
self.params[s] = kwargs[s]
-
#populate prompt on jump and athena server:
- if self.params['athena_user'] is not 'None':
+ if self.params['athena_user']:
self.params['jump_prompt'] = self.params['athena_user'] + '@jmp101.nuq1:'
self.params['athena_prompt'] = ('[' + self.params['athena_user']
+ '@athenasrv3 ~]$')
- if dev is not None:
- # parameters used to open an ssh tunnel to this device
- for s in ('user', 'addr', 'pwd', 'bruno_prompt', 'addr_ipv6'):
- self.params[s] = kwargs[s]
-
- def Setlogging(self, logging):
+ def SetLogging(self, logging):
"""Setup the logging file(s)."""
self.log = logging
@@ -102,7 +97,7 @@
p_ssh = pexpect.spawn('ssh '+self.params['user']+'@'+addr)
i = p_ssh.expect(
[ssh_newkey, 'password:', self.params['bruno_prompt'], pexpect.EOF,
- '\(none\)#', 'gfibertv#', pexpect.TIMEOUT])
+ '\(none\)#', 'gfibertv#', pexpect.TIMEOUT], self.__long_delay)
while 1:
if i == 0:
@@ -123,25 +118,14 @@
if i == 5: self.params['bruno_prompt'] = 'gfibertv#'
return True
if i == 3 or i == 6:
- print 'Key or connection timeout'
+ print 'ssh did not respond, or connection timeout'
return False
if i < 0 or i > 6:
print 'Error, quit'
return False
- def SshFromAthena(self):
- """Initiate an ssh tunnel from the athenasrv3 to the device.
-
- Returns:
- return True when succeed, return False otherwise
- """
- # clear the temp file
- self.tmp_file.close()
- self.tmp_file = tempfile.TemporaryFile(mode='w+t')
- self.p_ssh.logfile = self.tmp_file
- #Const
- ssh_newkey = 'Are you sure you want to continue connecting'
-
+ def SetDeviceSSHKey(self):
+ """Setup ssh-agent on athena server."""
self.p_ssh.sendline('ssh-agent /bin/bash')
self.p_ssh.expect(self.params['athena_prompt'])
#p_ssh.expect(self.params['athena_prompt'])
@@ -153,6 +137,24 @@
self.p_ssh.expect(self.params['athena_prompt'])
#print self.p_ssh.before
+ def SshFromAthena(self, set_key=False):
+ """Initiate an ssh tunnel from the athenasrv3 to the device.
+
+ Args:
+ set_key: if set True, ssh-key will be reset before ssh
+ Returns:
+ return True when succeed, return False otherwise
+ """
+ # clear the temp file
+ self.tmp_file.close()
+ self.tmp_file = tempfile.TemporaryFile(mode='w+t')
+ self.p_ssh.logfile = self.tmp_file
+ #Const
+ ssh_newkey = 'Are you sure you want to continue connecting'
+
+ if set_key:
+ self.SetDeviceSSHKey()
+
if self.params['addr_ipv6']:
addr = self.params['addr_ipv6']
else:
@@ -189,8 +191,8 @@
def ExitToAthena(self):
"""Terminate the ssh tunnel to device, and exit to athenasrv3."""
- self.p_ssh.sendline('exit')
- self.p_ssh.expect(self.params['athena_prompt'])
+ #self.p_ssh.sendline('exit')
+ #self.p_ssh.expect(self.params['athena_prompt'])
self.p_ssh.sendline('exit')
self.p_ssh.expect(self.params['athena_prompt'])
@@ -203,12 +205,13 @@
Returns:
return True upon success
"""
+ self.params['ssh_use_athena'] = True
p_ssh = pexpect.spawn('ssh -a ' + self.params['jump_server'])
print 'ssh -a jmp.googlefiber.net ...'
#p_ssh.expect('lmeng@jmp101.nuq1:.*[\\$]')
i = p_ssh.expect(
[self.params['jump_prompt'], pexpect.TIMEOUT, pexpect.EOF],
- self.__short_delay)
+ self.__short_delay*3)
if i == 1 or i == 2:
info = self.log.CreateErrorInfo(
'critical', 'Cannot establish SSH tunnel to jump server.'
@@ -254,7 +257,7 @@
+ str(delay) + ' seconds')
self.log.SendLine(None, info)
time.sleep(delay)
- tunnel = self.p_ssh.Ssh()
+ tunnel = self.Ssh()
retry += 1
if retry >= max_retry:
@@ -269,14 +272,15 @@
'---', 'ssh session to Device successfully established!')
self.log.SendLine(None, info)
- def SshRetryFromAthena(self, max_retry=5, retry_delay=15):
+ def SshRetryFromAthena(self, max_retry=5, retry_delay=15, set_key=False):
"""Establish ssh connection to Device from athenasrv3, retry upon failure.
Args:
max_retry: the total number of retry
retry_delay: delay between each ssh try
+ set_key: if ssh-key needs to be set
"""
- tunnel = self.SshFromAthena()
+ tunnel = self.SshFromAthena(set_key)
if not tunnel:
retry = 0
while not tunnel and retry < max_retry:
@@ -301,11 +305,19 @@
'---', 'ssh session to Device successfully established!')
self.log.SendLine(None, info)
- def SendCmd(self, cmd):
- """Send a command to the Device over ssh tunnel."""
+ def SendCmd(self, cmd, delay=10):
+ """Send a command to the Device over ssh tunnel.
+
+ Args:
+ cmd: the shell command to execute
+ delay: time delay while waiting for shell prompt after command is
+ executed
+ Returns:
+ return True if send command succeeded
+ """
self.p_ssh.sendline(cmd)
i = self.p_ssh.expect(
- [self.params['bruno_prompt'], pexpect.EOF, pexpect.TIMEOUT], 10)
+ [self.params['bruno_prompt'], pexpect.EOF, pexpect.TIMEOUT], delay)
while 1:
if i == 0:
@@ -315,13 +327,16 @@
# prompt not returned
info = self.log.CreateErrorInfo(
'Warning', 'Device not responding to shell command or timeout.'
- ' Connect after 3 seconds ...')
+ ' Connect after 10 seconds ...')
self.log.SendLine(None, info)
- time.sleep(3)
- self.SshRetryFromAthena()
+ time.sleep(10)
+ if self.params['ssh_use_athena']:
+ self.SshRetryFromAthena()
+ else:
+ self.SshRetry()
self.p_ssh.sendline(cmd)
i = self.p_ssh.expect(
- [self.params['bruno_prompt'], pexpect.EOF, pexpect.TIMEOUT], 5)
+ [self.params['bruno_prompt'], pexpect.EOF, pexpect.TIMEOUT], delay)
def GetCmdOutput(self, buff_size=0):
"""Get lines from the command output of the device, in a bottom up manner.
@@ -367,18 +382,18 @@
def Close(self):
# exit the ssh tunnel
self.p_ssh.sendline('exit')
- i = self.expect([self.p_ssh.params['athena_prompt'],
- self.p_ssh.params['jump_prompt'],
- self.p_ssh.params['bruno_prompt'],
- pexpect.EOF, pexpect.TIMEOUT])
+ i = self.p_ssh.expect([self.params['athena_prompt'],
+ self.params['jump_prompt'],
+ self.params['bruno_prompt'],
+ pexpect.EOF, pexpect.TIMEOUT])
while 1:
if i == 0:
# now at athena server:
self.p_ssh.sendline('exit')
- i = self.expect([self.p_ssh.params['athena_prompt'],
- self.p_ssh.params['jump_prompt'],
- self.p_ssh.params['bruno_prompt'],
- pexpect.EOF, pexpect.TIMEOUT])
+ i = self.p_ssh.expect([self.params['athena_prompt'],
+ self.params['jump_prompt'],
+ self.params['bruno_prompt'],
+ pexpect.EOF, pexpect.TIMEOUT])
elif i == 1:
# now at jump server:
self.p_ssh.sendline('exit')
@@ -386,21 +401,20 @@
elif i == 2:
# now at bruno:
self.p_ssh.sendline('exit')
- i = self.expect([self.p_ssh.params['athena_prompt'],
- self.p_ssh.params['jump_prompt'],
- self.p_ssh.params['bruno_prompt'],
+ i = self.expect([self.params['athena_prompt'],
+ self.params['jump_prompt'],
+ self.params['bruno_prompt'],
pexpect.EOF, pexpect.TIMEOUT])
elif i == 3 or i == 4:
# time out:
- print 'Timeout when closing the ssh tunnel!'
+ print 'Exit from Device.'
break
else:
- print 'Error'
break
def __del__(self):
- self.tempFile.Close()
- self.p_ssh.Close()
+ self.tmp_file.close()
+ #self.Close()
# End of SSH class
diff --git a/sshto b/sshto
new file mode 100755
index 0000000..8c4f7e2
--- /dev/null
+++ b/sshto
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# ssh (or, if no network, portsh) into the given device.
+#
+mydir=$(dirname "$0")
+cd "$mydir"
+
+name=$1
+shift
+
+log()
+{
+ echo "$@" >&2
+}
+
+
+die()
+{
+ log "$@"
+ exit 99
+}
+
+
+if [ -z "$name" ]; then
+ die "Usage: $0 <configname> [command...]"
+fi
+
+cmd=$(args="$*" ./run --quiet --raw "$name" -- '
+ if [ -n "$ip6" ] && ping6 -n -c1 -w2 $ip6 >/dev/null 2>&1; then
+ echo "ssh root@$ip6"
+ elif [ -n "$ip" ] && ping -n -c1 -w2 $ip >/dev/null 2>&1; then
+ echo "ssh root@$ip"
+ elif [ -n "$serialport" ]; then
+ if [ -n "$args" ]; then
+ echo "./portsh/portsh -pgoogle $serialport"
+ else
+ echo "./portsh/port $serialport"
+ fi
+ else
+ exit 1
+ fi
+') || die "Fatal: host '$name' has no live IP or serial port"
+log ">>> $cmd --" "$@"
+exec $cmd -- "$@"
diff --git a/testCase.py b/testCase.py
old mode 100644
new mode 100755
index 81e289d..bd2932e
--- a/testCase.py
+++ b/testCase.py
@@ -11,9 +11,13 @@
__author__ = 'Lehan Meng (lmeng@google.com)'
import datetime
+import json
import logging
+import os
import re
+import device
+
class TestCase(object):
"""Base class for all test cases.
@@ -28,12 +32,13 @@
placed in here.
"""
- def __init__(self, **kwargs):
+ def __init__(self, ssh=None, **kwargs):
"""Initiate a test class instance.
Args:
+ ssh: ssh tunnel to device
kwargs['testID']: The assigned test ID on TCM
- kwargs['title']: Title (section number) of the test case
+ kwargs['title']: Title (keyword) of the test case
kwargs['start_time']: Start time of the test case (for statistic purpose)
kwargs['key_word']: the label of the test case
kwargs['description']: description if required
@@ -48,15 +53,165 @@
'configFile': 'config.cfg'}
self.params = {}
- for s in ('testID', 'title', 'start_time', 'key_word',
- 'description', 'configFile'):
- if s in kwargs:
- self.test_info[s] = kwargs[s]
+ for s in kwargs:
+ self.test_info[s] = kwargs[s]
- self.log = logging.Logging()
- self.ParseConfig()
+ self.device = device.Device()
+
+ self.log = logging.Logging(testID=self.test_info['testID'])
+ self.ParseJSONConfigFile()
+ #self.ParseConfig()
+ if ssh:
+ self.p_ssh = ssh
+ else:
+ self.p_ssh = None
self.Run()
+ @staticmethod
+ def FindTestCaseID(file_name):
+ """Parse the configuration file for test case.
+
+ Find test ids in the configuration file.
+ Args:
+ file_name: the configure file to parse
+ Returns:
+ return the test case ids that needs to run the test
+ """
+ test_id_list = []
+ cfg_file = open(file_name, 'r')
+ cfg_list = cfg_file.readlines()
+ for line in cfg_list:
+ s = line.strip().strip('\n')
+ m = re.match(r'testID\s*:\s*(\d*)\s*$', s)
+ if (m is not None) and not s.startswith('#'):
+ test_id_list.append(m.group(1))
+ cfg_file.close()
+ return test_id_list
+
+ @staticmethod
+ def FindTestCaseToExecute(file_name):
+ """Parse the configuration file for test case.
+
+ Find test ids in the configuration file.
+ Args:
+ file_name: the configure file to parse
+ Returns:
+ return the test case ids that needs to run the test
+ """
+ test_id_list = []
+ try:
+ cfg_file = open(file_name, 'r')
+ except IOError:
+ print 'Cannot open the input file, return'
+ return os.sys.exit(1)
+ data = json.load(cfg_file)
+ cfg_file.close()
+
+ for test_case in data.iterkeys():
+ for key in data[test_case].iterkeys():
+ if isinstance(data[test_case][key], list) and key == 'testID':
+ for i in range(0, len(data[test_case][key])):
+ test_id_list.append(data[test_case][key][i])
+ return test_id_list
+
+ def ConfigForThisTestCase(self, data):
+ """Match if the configuration data is for current test case.
+
+ Args:
+ data: configuration data
+ Returns:
+ True: if the data is for current test case
+ False: if otherwise
+ """
+ for key in data.iterkeys():
+ if key == 'testID':
+ if self.test_info['testID'] in data[key]:
+ return True
+ else:
+ return False
+ return False
+
+ def ParseJSONConfigFile(self):
+ """Parse the JSON format configuration file.
+
+ The sturcture of a JSON file is as follows:
+ File format: parameter_names: string_values (or lists of values)
+ comment is defined in the comment tag (JSON does not support
+ comment).
+ 'Test_Case_Description' : {
+ 'comment': ['comments'],
+ 'testID': ['11843140', '111111', '222222'],
+ 'user' : 'root',
+ 'addr' : '192.168.1.4',
+ 'addr_ipv6' : 'None',
+ 'pwd' : 'google',
+ 'bruno_prompt' : 'gfibertv#',
+ 'title' : 'Download',
+ 'expected_version_id': '716006',
+ 'expected_version': 'bruno-koala-5',
+ 'downgrade_version': 'bruno-koala-4',
+ 'downgrade_version_id': '728001',
+ 'acs_url' : 'https://gfiber-acs-staging.appspot.com/cwmp',
+ 'jump_server' : 'jmp.googlefiber.net',
+ 'athena_user' : 'your_uname',
+ 'athena_pwd' : 'your_pwd'
+ }
+
+ Returns:
+ return True if configure file parsed successfully
+ """
+ try:
+ self.cfg_file = open(self.test_info['configFile'], 'r')
+ except IOError:
+ print 'Cannot open the input file, return'
+ return os.sys.exit(1)
+ data = json.load(self.cfg_file)
+ self.cfg_file.close()
+
+ test_id_matched = False
+ for test_case in data.iterkeys():
+ if not self.ConfigForThisTestCase(data[test_case]):
+ continue
+ for key in data[test_case].iterkeys():
+ if isinstance(data[test_case][key], (str, unicode)):
+ value = data[test_case][key]
+ if not value or value == 'None':
+ self.params[key] = None
+ else:
+ self.params[key] = value
+ elif isinstance(data[test_case][key], dict):
+ if (key == 'title' and
+ self.test_info['testID'] in data[test_case][key]):
+ self.params[key] = data[test_case][key][self.test_info['testID']]
+ self.test_info['key_word'] = self.params[key]
+ else:
+ self.params[key] = data[test_case][key]
+ elif isinstance(data[test_case][key], list):
+ if key == 'testID':
+ for i in range(0, len(data[test_case][key])):
+ test_id = data[test_case][key][i]
+ if str(self.test_info['testID']) == test_id:
+ test_id_matched = True
+ elif key == 'comment':
+ # do not process comment lines
+ continue
+ else:
+ self.params[key] = data[test_case][key]
+ else:
+ info = self.log.CreateErrorInfo(
+ 'Low', 'Warning: Configuration Parameter type unknown '
+ 'in file ' + self.test_info['configFile'] + ': '
+ + test_case + ':' + key)
+ self.log.SendLine(self.test_info, info)
+
+ if not test_id_matched:
+ info = self.log.CreateErrorInfo(
+ 'Low', 'Warning: No configuration found for this test '
+ 'case in file: ' + self.test_info['configFile'])
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
+ return True
+
def ParseConfig(self):
"""Parse the configuration file for test case.
@@ -82,13 +237,16 @@
reuturn 1 if succeed
"""
# dictionary for params
- self.cfg_file = open(self.test_info['configFile'], 'r')
+ try:
+ self.cfg_file = open(self.test_info['configFile'], 'r')
+ except IOError:
+ print 'Cannot open the input file, return'
+ return os.sys.exit(1)
cfg_list = self.cfg_file.readlines()
test_id_matched = False
-
for line in cfg_list:
- s = line.lstrip().strip().strip('\n')
+ s = line.strip().strip('\n')
m = re.match('testID: ' + str(self.test_info['testID']), s)
if (m is not None) and not s.startswith('#'):
# test case matching succeed
@@ -105,7 +263,7 @@
self.log.sendLine(self.test_info, info)
s = cfg_list[index]
- s = s.lstrip().rstrip()
+ s = s.strip()
s = s.split('#', 2)[0]
m = re.match('testID: ', s)
@@ -114,8 +272,8 @@
# read and create current test case configuration data as dictionary
# format: parameter = value
if s is not '' and not s.startswith('#'):
- name = s.split('=')[0].strip().lstrip()
- value = s.split('=')[1].strip().lstrip()
+ name = s.split('=')[0].strip()
+ value = s.split('=')[1].strip()
if not value or value == 'None':
self.params[name] = None
else:
@@ -124,17 +282,18 @@
index += 1
if index == len(cfg_list): break
s = cfg_list[index]
- s = s.lstrip().rstrip()
+ s = s.strip()
m = re.match('testID: ', s)
# all configuration parsed
self.cfg_file.close()
if not test_id_matched:
# end of file reached
- info = self.log.createErrorInfo(
+ info = self.log.CreateErrorInfo(
'Low', 'Warning: No configuration found for this test case in file: '
+ self.test_info['configFile'])
- self.log.sendLine(self.test_info, info)
+ self.log.SendLine(self.test_info, info)
+ os.sys.exit(1)
return True
def AddConfigParams(self, par='par', value='value'):
@@ -144,7 +303,8 @@
par: parameter name
value: parameter value
"""
- self.params.setdefault(par.strip().lstrip(), value.strip().lstrip())
+ if par and value:
+ self.params[par.strip()] = value.strip()
def __del__(self):
pass
@@ -159,4 +319,4 @@
##### Add your code here -- END #######
def Destructor(self):
- pass
\ No newline at end of file
+ pass