1"""Metrics reporting module for Blueberry using protobuf. 2 3Internal reference 4""" 5 6from __future__ import absolute_import 7from __future__ import division 8from __future__ import print_function 9 10import base64 11import logging 12import time 13 14# Internal import 15 16 17class BluetoothMetricLogger(object): 18 """A class used for gathering metrics from tests and devices. 19 20 This class provides methods to allow test writers to easily export metrics 21 from their tests as protobuf messages. 22 23 Attributes: 24 _metrics: The Bluetooth test proto message to add metrics to. 25 """ 26 27 def __init__(self, bluetooth_test_proto_message): 28 self._metrics = bluetooth_test_proto_message 29 self._start_time = int(time.time()) 30 31 def add_primary_device_metrics(self, device): 32 """Adds primary device metrics to the test proto message. 33 34 Args: 35 device: The Bluetooth device object to gather device metrics from. 36 """ 37 device_message = self._metrics.configuration_data.primary_device 38 message_fields = device_message.DESCRIPTOR.fields_by_name.keys() 39 try: 40 device_metrics_dict = device.get_device_info() 41 except AttributeError: 42 logging.info( 43 'Must implement get_device_info method for this controller in order to upload device metrics.' 44 ) 45 return 46 47 for metric in device_metrics_dict: 48 if metric in message_fields: 49 setattr(device_message, metric, device_metrics_dict[metric]) 50 else: 51 logging.info('%s is not a valid metric field.', metric) 52 53 def add_connected_device_metrics(self, device): 54 """Adds connected device metrics to the test proto message. 55 56 Args: 57 device: The Bluetooth device object to gather device metrics from. 58 """ 59 device_message = self._metrics.configuration_data.connected_device 60 message_fields = device_message.DESCRIPTOR.fields_by_name.keys() 61 try: 62 device_metrics_dict = device.get_device_info() 63 except AttributeError: 64 logging.info( 65 'Must implement get_device_info method for this controller in order to upload device metrics.' 66 ) 67 return 68 69 for metric in device_metrics_dict: 70 if metric in message_fields: 71 setattr(device_message, metric, device_metrics_dict[metric]) 72 else: 73 logging.warning('%s is not a valid metric field.', metric) 74 75 def add_test_metrics(self, test_metrics_dict): 76 """Adds test metrics to the test proto message. 77 78 Args: 79 test_metrics_dict: A dictionary of metrics to add to the test proto 80 message. Metric will only be added if the key exists as a field in the 81 test proto message. 82 """ 83 if hasattr(self._metrics, 'configuration_data'): 84 self._metrics.configuration_data.test_date_time = self._start_time 85 message_fields = self._metrics.DESCRIPTOR.fields_by_name.keys() 86 for metric in test_metrics_dict: 87 if metric in message_fields: 88 metric_value = test_metrics_dict[metric] 89 if isinstance(metric_value, (list, tuple)): 90 getattr(self._metrics, metric).extend(metric_value) 91 else: 92 setattr(self._metrics, metric, metric_value) 93 else: 94 logging.warning('%s is not a valid metric field.', metric) 95 96 def proto_message_to_base64(self): 97 """Converts a proto message to a base64 string. 98 99 Returns: 100 string, Message formatted as a base64 string. 101 """ 102 return base64.b64encode(self._metrics.SerializeToString()).decode('utf-8') 103 104 def proto_message_to_ascii(self): 105 """Converts a proto message to an ASCII string. 106 107 Returns: 108 string, Message formatted as an ASCII string. Useful for debugging. 109 """ 110 return text_format.MessageToString(self._metrics) 111