1# Copyright 2018, The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Metrics base class. 17""" 18 19from __future__ import print_function 20 21import logging 22import random 23import socket 24import subprocess 25import time 26import uuid 27 28import asuite_metrics 29import constants 30 31from proto import clientanalytics_pb2 32from proto import external_user_log_pb2 33from proto import internal_user_log_pb2 34 35from . import clearcut_client 36 37INTERNAL_USER = 0 38EXTERNAL_USER = 1 39 40ATEST_EVENTS = { 41 INTERNAL_USER: internal_user_log_pb2.AtestLogEventInternal, 42 EXTERNAL_USER: external_user_log_pb2.AtestLogEventExternal 43} 44# log source 45ATEST_LOG_SOURCE = { 46 INTERNAL_USER: 971, 47 EXTERNAL_USER: 934 48} 49 50 51def get_user_type(): 52 """Get user type. 53 54 Determine the internal user by passing at least one check: 55 - whose git mail domain is from google 56 - whose hostname is from google 57 Otherwise is external user. 58 59 Returns: 60 INTERNAL_USER if user is internal, EXTERNAL_USER otherwise. 61 """ 62 try: 63 output = subprocess.check_output( 64 ['git', 'config', '--get', 'user.email'], universal_newlines=True) 65 if output and output.strip().endswith(constants.INTERNAL_EMAIL): 66 return INTERNAL_USER 67 except OSError: 68 # OSError can be raised when running atest_unittests on a host 69 # without git being set up. 70 logging.debug('Unable to determine if this is an external run, git is ' 71 'not found.') 72 except subprocess.CalledProcessError: 73 logging.debug('Unable to determine if this is an external run, email ' 74 'is not found in git config.') 75 try: 76 hostname = socket.getfqdn() 77 if (hostname and 78 any([(x in hostname) for x in constants.INTERNAL_HOSTNAME])): 79 return INTERNAL_USER 80 except IOError: 81 logging.debug('Unable to determine if this is an external run, ' 82 'hostname is not found.') 83 return EXTERNAL_USER 84 85 86class MetricsBase: 87 """Class for separating allowed fields and sending metric.""" 88 89 _run_id = str(uuid.uuid4()) 90 try: 91 #pylint: disable=protected-access 92 _user_key = str(asuite_metrics._get_grouping_key()) 93 #pylint: disable=broad-except 94 except Exception: 95 _user_key = asuite_metrics.DUMMY_UUID 96 _user_type = get_user_type() 97 _log_source = ATEST_LOG_SOURCE[_user_type] 98 cc = clearcut_client.Clearcut(_log_source) 99 tool_name = None 100 101 def __new__(cls, **kwargs): 102 """Send metric event to clearcut. 103 104 Args: 105 cls: this class object. 106 **kwargs: A dict of named arguments. 107 108 Returns: 109 A Clearcut instance. 110 """ 111 # pylint: disable=no-member 112 if not cls.tool_name: 113 logging.debug('There is no tool_name, and metrics stops sending.') 114 return None 115 allowed = ({constants.EXTERNAL} if cls._user_type == EXTERNAL_USER 116 else {constants.EXTERNAL, constants.INTERNAL}) 117 fields = [k for k, v in vars(cls).items() 118 if not k.startswith('_') and v in allowed] 119 fields_and_values = {} 120 for field in fields: 121 if field in kwargs: 122 fields_and_values[field] = kwargs.pop(field) 123 params = {'user_key': cls._user_key, 124 'run_id': cls._run_id, 125 'user_type': cls._user_type, 126 'tool_name': cls.tool_name, 127 cls._EVENT_NAME: fields_and_values} 128 log_event = cls._build_full_event( 129 ATEST_EVENTS[cls._user_type](**params)) 130 cls.cc.log(log_event) 131 return cls.cc 132 133 @classmethod 134 def _build_full_event(cls, atest_event): 135 """This is all protobuf building you can ignore. 136 137 Args: 138 cls: this class object. 139 atest_event: A client_pb2.AtestLogEvent instance. 140 141 Returns: 142 A clientanalytics_pb2.LogEvent instance. 143 """ 144 log_event = clientanalytics_pb2.LogEvent() 145 log_event.event_time_ms = int( 146 (time.time() - random.randint(1, 600)) * 1000) 147 log_event.source_extension = atest_event.SerializeToString() 148 return log_event 149