Download/DataModel Test case code upload

Change-Id: I79f06f46d98da1deffed637ddaf9736d3b8b65f8
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..4025428
--- /dev/null
+++ b/acs.py
@@ -0,0 +1,93 @@
+#!/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,
+        '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 SaveConfiguration(self):
+    """Run the ACS configuration script from Google3 location."""
+    cur_dir = os.getcwd()
+    print '============' + cur_dir
+    os.chdir('/home/lmeng/git_project/google3/isp/'
+             'fiber/testing/automated_tests/bulkupgrade/')
+    print '============' + os.getcwd()
+    os.system(
+        'blaze test --notest_loasd --test_arg=--imageVersion='
+        + self.acs_info['imageVersion']
+        + ' --test_strategy=local :bulkupgrade_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 ParseParameterNames(self, cmd_list):
+    """Parse the command output from nbi_client, retrieve parameter names."""
+    if ('FAILED' in cmd_list[0]) or (not 'SUCCESS' in cmd_list[0]):
+      info = self.log.CreateErrorInfo(
+          'Critical', 'nbi_clienet call failed. Check log for details.')
+      self.log.SendLine(None, info)
+      return False
+
+    chk_list = []
+    for line in cmd_list:
+      if 'name:' in line:
+        output = re.sub('.*\"(.*)\".*', r'\1', line)
+        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/config.cfg b/config.cfg
index 6659a21..602b4d9 100644
--- a/config.cfg
+++ b/config.cfg
@@ -9,31 +9,41 @@
 # be parsed as configuration information     #
 ##############################################
 testID: 11843140
-user = root
+user = root  #obsoleted 
 addr = 192.168.1.4
-addr_ipv6 = 2605:a601:15:9001:21a:11ff:fe30:64d5
-pwd = google
+addr_ipv6 = None
+pwd = google  #obsoleted 
 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
+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
 # 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
+athena_user = your_uname
+athena_pwd = your_pwd
 
 
 
-#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 =
-
-
+testID: 14187024
+title =DataModel
+req_file = DataModel_requirement.cfg
+mis_file = missing_parameters.log
+user = root
+pwd = google
+addr = 192.168.1.4
+addr_ipv6 = 2605:a601:fe00:fd18:21a:11ff:fe30:6383
+serial_num = None
+bruno_prompt = gfibertv#
+expected_version_id=728001
+expected_version=bruno-koala-4
+acs_url = https://gfiber-acs-staging.appspot.com/cwmp
+jump_server = jmp.googlefiber.net
+athena_user = your_uname
+athena_pwd = your_pwd
+nbi_client_path = /home/$your_uname/git_project/google3/blaze-bin/java/com/google/fiber/provisioning/acs/client/nbi/nbi_client
+para_tree_file = tree.xml
 
diff --git a/dataModel.py b/dataModel.py
new file mode 100755
index 0000000..73adf5e
--- /dev/null
+++ b/dataModel.py
@@ -0,0 +1,450 @@
+#!/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
+"""
+
+__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'])
+    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)
+    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 = []
+
+  def TestEnvCheck(self):
+    """Verify device configuration meets the test requirement."""
+    acs_url = self.bruno.GetACSUrl()
+    self.AddConfigParams('acs_url', acs_url)
+
+    current_version = self.bruno.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 SetParameterValues(self, param='', value=''):
+    """Set parameter values on Device."""
+    nbi_path = self.params['nbi_client_path']
+    cmd = (nbi_path + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+           '*/gfiber-acs-staging/default/* --service CpeParametersService '
+           '--method SetParameterValues --request \''
+           'cpe_id: <oui: "F88FCA" product_class: "GFMS100" serial_no: '
+           '"110120906BDE"> request: <parameter_list < '
+           'parameter_value_struct: <name: "'
+           + param +
+           '" string_value: "' + value + '"> > parameter_key: "myParamKey">\'')
+    acs_instance = acs.ACS()
+    acs_instance.SetLogging(self.log)
+    call_result = acs_instance.StubbyClientCall(cmd)
+    if 'FAILED' in call_result[0]:
+      self.nbi_result = []
+    else:
+      self.nbi_result = call_result
+    print call_result
+
+  def GetParameterValues(self, param=''):
+    """Get parameter value(s) from the data model tree.
+
+    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']
+    cmd = (nbi_path + ' --server /gns/project/apphosting/stubby/prod-appengine/'
+           '*/gfiber-acs-staging/default/* --service CpeParametersService '
+           '--method GetParameterValues --request \''
+           'cpe_id: <oui: "F88FCA" product_class: "GFMS100" serial_no: '
+           '"110120906BDE"> request: <parameter_names: "' +param + '">\'')
+    acs_instance = acs.ACS()
+    acs_instance.SetLogging(self.log)
+    call_result = acs_instance.StubbyClientCall(cmd)
+    if 'FAILED' in call_result[0]:
+      self.nbi_result = []
+    else:
+      self.nbi_result = call_result
+    print call_result
+    return acs_instance.ParseParameterNames(call_result)
+
+  def GetParameterNamesFromDeviceLog(self, time_stamp, param=''):
+    """Get TR069 data model parameters that are supported by current image."""
+    param_list = []
+    param_list_start = False
+    param_list_end = False
+    self.device_log_result = []
+    num_of_params = 0
+
+    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:
+            output = re.sub(r'.*<cwmp:GetParameterValues>.*<ParameterNames.*/>'
+                            '(.*)</ParameterNames></cwmp:GetParameterValues>.*',
+                            r'\1', line)
+            if output.strip() == param:
+              param_list_start = True
+              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:
+          num_of_params += 1
+          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):
+      return False
+    return param_list
+
+  def CreateElement(self, param_name, param_type, 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
+    Args:
+      param_name: the name of the parameter (Path)
+      param_type: the type of the parameter
+      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)
+    value = ET.Element('value', type=param_type)
+    value.text = param_value
+    e.append(value)
+    test_value = ET.Element('test_value', type=param_type)
+    test_value.text = param_value
+    e.append(test_value)
+    return e
+
+  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, 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]
+          param_type = re.sub('.*type\w*=\w*\".*:(.*)\".*', r'\1', line).strip()
+          param_value = re.sub('.*<.*type.*>(.*)</Value>', r'\1', line).strip()
+          root.append(self.CreateElement(param_name, param_type, 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)
+    fout = open(self.params['para_tree_file'], 'w')
+    tree.write(fout)
+    fout.close()
+    return tree
+
+  def VerifyParameterValues(self, param=None):
+    """Verify the designated parameter can be set with expected value.
+
+    Test 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 specified by self.log instance.
+    Args:
+      param:
+        1. if parameter is given, this method will set the value of this
+           parameter
+        2. if the parameter is not given, this method will set all parameters
+           that the device currently supports
+    """
+    self.para_tree = self.ParseToElementTree()
+    self.SetParameterValues(param, 'none')
+
+  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
+    return -1
+
+  #################### 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)
+    #self.p_ssh.SshToAthena()
+    info = self.log.CreateProgressInfo(
+        '5%', 'SSH session to Athena server successfully established!')
+    self.log.SendLine(self.test_info, info)
+
+    #self.p_ssh.SshRetryFromAthena()
+    #info = self.log.CreateProgressInfo(
+    #    '15%', '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(
+        '25%', 'Check device test setup completed!')
+    self.log.SendLine(self.test_info, info)
+
+    # get parameter name lists, compare with requirement
+    info = self.log.CreateProgressInfo(
+        '35%', 'Get parameters from nbi_client ...')
+    self.log.SendLine(self.test_info, info)
+    time_stamp = self.GetTimeStamp()
+    if time_stamp == -1:
+      info = self.log.CreateErrorInfo(
+          'Critical', 'Cannot get time information from Device log!')
+      self.log.SendLine(self.test_info, info)
+      os.sys.exit(1)
+    chk_list = self.GetParameterValues()
+
+    if not chk_list:
+      info = self.log.CreateProgressInfo(
+        '40%', 'Get parameters from device log ...')
+      self.log.SendLine(self.test_info, info)
+      # nbi client may fail because of timeout
+      # then get parameter list from device log
+      while not chk_list:
+        time.sleep(self.__delay)
+        chk_list = self.GetParameterNamesFromDeviceLog(time_stamp)
+      chk_list.sort()
+
+      if not chk_list:
+        info = self.log.CreateErrorInfo(
+            'Critical', 'Cannot find parameter list requirement from log file!')
+        self.log.SendLine(self.test_info, info)
+        os.sys.exit(1)
+
+    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(
+        '50%', 'Get parameter names completed. Check '
+        + self.log.params['f_result'] + ' file for missing parameters.')
+    self.log.SendLine(self.test_info, info)
+
+    # TODO(lmeng):
+    #set parameter values:
+    #self.VerifyParameterValues('X_GOOGLE-COM_GVSB.GvsbKick')
+    #info = self.log.CreateProgressInfo(
+    #    '60%', 'Set parameter values completed. Check '
+    #    + self.log.params['f_result'] + ' file for missing parameters.')
+    #self.log.SendLine(self.test_info, info)
+
+    #self.SetParameterValues('X_GOOGLE-COM_GVSB.GvsbKick', 'arp')
+    #self.GetParameterValues('X_GOOGLE-COM_GVSB.GvsbKick')
+    #self.SetParameterValues('X_GOOGLE-COM_GVSB.GvsbKick', 'none')
+    #self.GetParameterValues('X_GOOGLE-COM_GVSB.GvsbKick')
+    #self.p_ssh.ExitToAthena()
+    print 'Test Completed...'
+    ####### Add your code here -- END   #######
+
+  def Destructor(self):
+    self.p_ssh.Close()
+
+if __name__ == '__main__':
+  test = DataModelTest(testID='14187024', key_word='DataModel')
diff --git a/download.py b/download.py
index 475b287..2bfdc7f 100755
--- a/download.py
+++ b/download.py
@@ -43,7 +43,7 @@
       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):
@@ -69,18 +69,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 +115,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)
@@ -133,16 +132,18 @@
     # No time stamp found
     return -1
 
-  def ConfigACS(self):
+  def ConfigACS(self, version_id=None):
     """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(version_id)
     acs_instance.SetLogging(self.log)
     acs_instance.SaveConfiguration()
 
@@ -220,8 +221,9 @@
       return True if rebooted
       return False otherwise
     """
+    time.sleep(5)
     s = self.GetTimeStamp()
-    if float(s) < float(time_stamp):
+    if float(s) < float(time_stamp) and s is not -1:
       return True
     return False
 
@@ -238,7 +240,7 @@
     """
     self.p_ssh.ExitToAthena()
     time.sleep(3*self.__short_delay)
-    self.p_ssh.SshRetryFromAthena()
+    self.p_ssh.SshRetryFromAthena(set_key=False)
     count = 0
     ts = time_stamp
     while not self.IsRebooted(ts):
@@ -252,7 +254,7 @@
         os.sys.exit(1)
 
       t = self.GetTimeStamp()
-      if t > ts:
+      if float(t) > float(ts):
         ts = t
       self.p_ssh.ExitToAthena()
       info = self.log.CreateProgressInfo(
@@ -260,7 +262,7 @@
           + ' seconds for image installation and device reboot...')
       self.log.SendLine(self.test_info, info)
       time.sleep(3*self.__short_delay)
-      self.p_ssh.SshRetryFromAthena()
+      self.p_ssh.SshRetryFromAthena(set_key=False)
 
     time.sleep(2*self.__short_delay)
     self.p_ssh.SendCmd('more /etc/version')
@@ -282,11 +284,12 @@
         '5%', 'SSH session to Athena server successfully established!')
     self.log.SendLine(self.test_info, info)
 
-    max_test_idx = 2
+    max_test_idx = 3
     current_test_idx = 1
+    upgrade = True
 
     while current_test_idx < max_test_idx:
-      self.p_ssh.SshRetryFromAthena()
+      self.p_ssh.SshRetryFromAthena(set_key=True)
       info = self.log.CreateResultInfo(
           '---', 'Test Sequence Number: ' + str(current_test_idx))
       self.log.SendLine(self.test_info, info)
@@ -298,7 +301,7 @@
         self.log.SendLine(self.test_info, info)
         self.p_ssh.ExitToAthena()
         time.sleep(self.__short_delay)
-        self.p_ssh.SshRetryFromAthena()
+        self.p_ssh.SshRetryFromAthena(set_key=False)
 
       info = self.log.CreateProgressInfo(
           '15%', 'SSH session to device successfully established!')
@@ -306,7 +309,7 @@
 
       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,24 +317,25 @@
                                          + 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 upgrade == True:
+        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)
+      else:
+        self.time_stamp = self.ConfigACS(self.params['downgrade_version_id'])
+        self.params['expected_version'] = self.params['downgrade_version']
+        self.params['expected_version_id'] = self.params['downgrade_version_id']
+        info = self.log.CreateProgressInfo('40%', 'Downgrade Version: '
+                                           + self.params['downgrade_version'])
+        self.log.SendLine(self.test_info, info)
 
-      self.time_stamp = self.ConfigACS()
-      info = self.log.CreateProgressInfo('40%', 'Expected Version: '
-                                         + self.params['expected_version'])
-      self.log.SendLine(self.test_info, info)
       info = self.log.CreateProgressInfo(
-          '40%', 'Successfully configured ACS for expected image version')
+          '40%', 'Successfully configured ACS for image 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):
         info = self.log.CreateProgressInfo(
             '100%', 'Image successfully upgraded to expected version: '
@@ -351,10 +355,7 @@
 
       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
+      upgrade = False
     print 'Test Completed...'
     ####### Add your code here -- END   #######
 
@@ -365,4 +366,4 @@
 
 
 if __name__ == '__main__':
-  DownloadTest(testID='11843140', key_word='Download')
\ No newline at end of file
+  DownloadTest(testID='11843140', key_word='Download')
diff --git a/logging.py b/logging.py
old mode 100644
new mode 100755
index ade9f50..da14edb
--- a/logging.py
+++ b/logging.py
@@ -22,10 +22,10 @@
   """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'}
+            'f_result': 'result.log',
+            'f_error': 'error.log',
+            'f_progress': 'progress.log',
+            'delimiter': '\t'}
 
   def __init__(self, **kwargs):
     """initiate the logging instance.
@@ -40,9 +40,9 @@
       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.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 SendLineToResult(self, test_info, result_info):
@@ -151,6 +151,12 @@
     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.
 
@@ -242,4 +248,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/ssh.py b/ssh.py
old mode 100644
new mode 100755
index 8b9f2e3..e721058
--- a/ssh.py
+++ b/ssh.py
@@ -102,7 +102,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], 20)
 
     while 1:
       if i == 0:
@@ -129,19 +129,8 @@
         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 +142,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 +196,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'])
 
@@ -208,7 +215,7 @@
     #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 +261,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 +276,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:
@@ -286,7 +294,7 @@
             + str(delay) + ' seconds')
         self.log.SendLine(None, info)
         time.sleep(delay)
-        tunnel = self.SshFromAthena()
+        tunnel = self.SshFromAthena(set_key)
         retry += 1
 
       if retry >= max_retry:
@@ -399,8 +407,8 @@
         break
 
   def __del__(self):
-    self.tempFile.Close()
-    self.p_ssh.Close()
+    self.tmp_file.close()
+    #self.Close()
 
 # End of SSH class
 
diff --git a/testCase.py b/testCase.py
old mode 100644
new mode 100755
index 81e289d..2fee061
--- a/testCase.py
+++ b/testCase.py
@@ -13,6 +13,7 @@
 import datetime
 import logging
 import re
+import device
 
 
 class TestCase(object):
@@ -53,6 +54,8 @@
       if s in kwargs:
         self.test_info[s] = kwargs[s]
 
+    self.device = device.Device()
+
     self.log = logging.Logging()
     self.ParseConfig()
     self.Run()
@@ -88,7 +91,7 @@
     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 +108,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 +117,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 +127,17 @@
           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)
     return True
 
   def AddConfigParams(self, par='par', value='value'):
@@ -144,7 +147,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 +163,4 @@
     ##### Add your code here -- END   #######
 
   def Destructor(self):
-    pass
\ No newline at end of file
+    pass