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