Home | History | Annotate | Download | only in power
      1 # Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import json, numpy, os, time, urllib, urllib2
      6 
      7 from autotest_lib.client.bin import utils
      8 from autotest_lib.client.common_lib import lsbrelease_utils
      9 from autotest_lib.client.cros.power import power_status
     10 from autotest_lib.client.cros.power import power_utils
     11 
     12 
     13 class BaseDashboard(object):
     14     """Base class that implements method for prepare and upload data to power
     15     dashboard.
     16     """
     17 
     18     def __init__(self, logger, testname, resultsdir=None, uploadurl=None):
     19         """Create BaseDashboard objects
     20 
     21         Args:
     22             logger: object that store the log. This will get convert to
     23                     dictionary by self._convert()
     24             testname: name of current test
     25             resultsdir: directory to save the power json
     26             uploadurl: url to upload power data
     27         """
     28         self._logger = logger
     29         self._testname = testname
     30         self._resultsdir = resultsdir
     31         self._uploadurl = uploadurl
     32 
     33 
     34     def _create_powerlog_dict(self, raw_measurement):
     35         """Create powerlog dictionary from raw measurement data
     36         Data format in go/power-dashboard-data
     37 
     38         Args:
     39             raw_measurement: dictionary contains raw measurement data.
     40 
     41         Returns:
     42             A dictionary of powerlog.
     43         """
     44         powerlog_dict = {
     45             'format_version': 2,
     46             'timestamp': time.time(),
     47             'test': self._testname,
     48             'dut': {
     49                 'board': utils.get_board(),
     50                 'version': {
     51                     'hw': utils.get_hardware_revision(),
     52                     'milestone':
     53                             lsbrelease_utils.get_chromeos_release_milestone(),
     54                     'os': lsbrelease_utils.get_chromeos_release_version(),
     55                     'channel': lsbrelease_utils.get_chromeos_channel(),
     56                     'firmware': utils.get_firmware_version(),
     57                     'ec': utils.get_ec_version(),
     58                     'kernel': utils.get_kernel_version(),
     59                 },
     60                 'sku' : {
     61                     'cpu': utils.get_cpu_name(),
     62                     'memory_size': utils.get_mem_total_gb(),
     63                     'storage_size':
     64                             utils.get_disk_size_gb(utils.get_root_device()),
     65                     'display_resolution': utils.get_screen_resolution(),
     66                 },
     67                 'ina': {
     68                     'version': 0,
     69                     'ina': raw_measurement['data'].keys()
     70                 },
     71                 'note': ''
     72             },
     73             'power': raw_measurement
     74         }
     75 
     76         if power_utils.has_battery():
     77             # Round the battery size to nearest tenth because it is fluctuated
     78             # for platform without battery norminal voltage data.
     79             powerlog_dict['dut']['sku']['battery_size'] = round(
     80                     power_status.get_status().battery[0].energy_full_design, 1)
     81             powerlog_dict['dut']['sku']['battery_shutdown_percent'] = \
     82                     power_utils.get_low_battery_shutdown_percent()
     83         return powerlog_dict
     84 
     85 
     86     def _save_json(self, powerlog_dict, resultsdir, filename='power_log.json'):
     87         """Convert powerlog dict to human readable formatted JSON and
     88         save to <resultsdir>/<filename>
     89 
     90         Args:
     91             powerlog_dict: dictionary of power data
     92             resultsdir: directory to save formatted JSON object
     93             filename: filename to save
     94         """
     95         filename = os.path.join(resultsdir, filename)
     96         with file(filename, 'w') as f:
     97             json.dump(powerlog_dict, f, indent=4, separators=(',', ': '))
     98 
     99 
    100     def _upload(self, powerlog_dict, uploadurl):
    101         """Convert powerlog dict to minimal size JSON and upload to dashboard.
    102 
    103         Args:
    104             powerlog_dict: dictionary of power data
    105         """
    106         data_obj = {'data': json.dumps(powerlog_dict)}
    107         encoded = urllib.urlencode(data_obj)
    108         req = urllib2.Request(uploadurl, encoded)
    109         urllib2.urlopen(req)
    110 
    111 
    112     def _convert(self):
    113         """Convert data from self._logger object to raw power measurement
    114         dictionary.
    115 
    116         MUST be implemented in subclass
    117 
    118         Return:
    119             raw measurement dictionary
    120         """
    121         raise NotImplementedError
    122 
    123     def upload(self):
    124         """Upload powerlog to dashboard and save data to results directory.
    125         """
    126         raw_measurement = self._convert()
    127         powerlog_dict = self._create_powerlog_dict(raw_measurement)
    128         if self._resultsdir is not None:
    129             self._save_json(powerlog_dict, self._resultsdir)
    130         if self._uploadurl is not None:
    131             self._upload(powerlog_dict, self._uploadurl)
    132 
    133 
    134 class MeasurementLoggerDashboard(BaseDashboard):
    135     """Dashboard class for power_status.MeasurementLogger
    136     """
    137 
    138     def _convert(self):
    139         """Convert data from power_status.MeasurementLogger object to raw
    140         power measurement dictionary.
    141 
    142         Return:
    143             raw measurement dictionary
    144         """
    145         power_dict = {
    146             'sample_count': len(self._logger.readings),
    147             'sample_duration': 0,
    148             'average': dict(),
    149             'data': dict()
    150         }
    151         if power_dict['sample_count'] > 1:
    152             total_duration = self._logger.times[-1] - self._logger.times[0]
    153             power_dict['sample_duration'] = \
    154                     1.0 * total_duration / (power_dict['sample_count'] - 1)
    155 
    156         for i, domain_readings in enumerate(zip(*self._logger.readings)):
    157             domain = self._logger.domains[i]
    158             power_dict['data'][domain] = domain_readings
    159             power_dict['average'][domain] = numpy.average(domain_readings)
    160         return power_dict
    161 
    162 
    163 class PowerLoggerDashboard(MeasurementLoggerDashboard):
    164     """Dashboard class for power_status.PowerLogger
    165     """
    166 
    167     def __init__(self, logger, testname, resultsdir=None, uploadurl=None):
    168         if uploadurl is None:
    169             uploadurl = 'http://chrome-power.appspot.com/rapl'
    170         super(PowerLoggerDashboard, self).__init__(logger, testname, resultsdir,
    171                                                    uploadurl)
    172