1*4882a593Smuzhiyun# Copyright (C) 2016 Intel Corporation 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# SPDX-License-Identifier: MIT 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# Functions to get metadata from the testing host used 6*4882a593Smuzhiyun# for analytics of test results. 7*4882a593Smuzhiyun 8*4882a593Smuzhiyunfrom collections import OrderedDict 9*4882a593Smuzhiyunfrom collections.abc import MutableMapping 10*4882a593Smuzhiyunfrom xml.dom.minidom import parseString 11*4882a593Smuzhiyunfrom xml.etree.ElementTree import Element, tostring 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunfrom oe.lsb import get_os_release 14*4882a593Smuzhiyunfrom oeqa.utils.commands import runCmd, get_bb_vars 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun 17*4882a593Smuzhiyundef metadata_from_bb(): 18*4882a593Smuzhiyun """ Returns test's metadata as OrderedDict. 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun Data will be gathered using bitbake -e thanks to get_bb_vars. 21*4882a593Smuzhiyun """ 22*4882a593Smuzhiyun metadata_config_vars = ('MACHINE', 'BB_NUMBER_THREADS', 'PARALLEL_MAKE') 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun info_dict = OrderedDict() 25*4882a593Smuzhiyun hostname = runCmd('hostname') 26*4882a593Smuzhiyun info_dict['hostname'] = hostname.output 27*4882a593Smuzhiyun data_dict = get_bb_vars() 28*4882a593Smuzhiyun 29*4882a593Smuzhiyun # Distro information 30*4882a593Smuzhiyun info_dict['distro'] = {'id': data_dict['DISTRO'], 31*4882a593Smuzhiyun 'version_id': data_dict['DISTRO_VERSION'], 32*4882a593Smuzhiyun 'pretty_name': '%s %s' % (data_dict['DISTRO'], data_dict['DISTRO_VERSION'])} 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun # Host distro information 35*4882a593Smuzhiyun os_release = get_os_release() 36*4882a593Smuzhiyun if os_release: 37*4882a593Smuzhiyun info_dict['host_distro'] = OrderedDict() 38*4882a593Smuzhiyun for key in ('ID', 'VERSION_ID', 'PRETTY_NAME'): 39*4882a593Smuzhiyun if key in os_release: 40*4882a593Smuzhiyun info_dict['host_distro'][key.lower()] = os_release[key] 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun info_dict['layers'] = get_layers(data_dict['BBLAYERS']) 43*4882a593Smuzhiyun info_dict['bitbake'] = git_rev_info(os.path.dirname(bb.__file__)) 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun info_dict['config'] = OrderedDict() 46*4882a593Smuzhiyun for var in sorted(metadata_config_vars): 47*4882a593Smuzhiyun info_dict['config'][var] = data_dict[var] 48*4882a593Smuzhiyun return info_dict 49*4882a593Smuzhiyun 50*4882a593Smuzhiyundef metadata_from_data_store(d): 51*4882a593Smuzhiyun """ Returns test's metadata as OrderedDict. 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun Data will be collected from the provided data store. 54*4882a593Smuzhiyun """ 55*4882a593Smuzhiyun # TODO: Getting metadata from the data store would 56*4882a593Smuzhiyun # be useful when running within bitbake. 57*4882a593Smuzhiyun pass 58*4882a593Smuzhiyun 59*4882a593Smuzhiyundef git_rev_info(path): 60*4882a593Smuzhiyun """Get git revision information as a dict""" 61*4882a593Smuzhiyun info = OrderedDict() 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun try: 64*4882a593Smuzhiyun from git import Repo, InvalidGitRepositoryError, NoSuchPathError 65*4882a593Smuzhiyun except ImportError: 66*4882a593Smuzhiyun import subprocess 67*4882a593Smuzhiyun try: 68*4882a593Smuzhiyun info['branch'] = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=path).decode('utf-8').strip() 69*4882a593Smuzhiyun except subprocess.CalledProcessError: 70*4882a593Smuzhiyun pass 71*4882a593Smuzhiyun try: 72*4882a593Smuzhiyun info['commit'] = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=path).decode('utf-8').strip() 73*4882a593Smuzhiyun except subprocess.CalledProcessError: 74*4882a593Smuzhiyun pass 75*4882a593Smuzhiyun try: 76*4882a593Smuzhiyun info['commit_count'] = int(subprocess.check_output(["git", "rev-list", "--count", "HEAD"], cwd=path).decode('utf-8').strip()) 77*4882a593Smuzhiyun except subprocess.CalledProcessError: 78*4882a593Smuzhiyun pass 79*4882a593Smuzhiyun return info 80*4882a593Smuzhiyun try: 81*4882a593Smuzhiyun repo = Repo(path, search_parent_directories=True) 82*4882a593Smuzhiyun except (InvalidGitRepositoryError, NoSuchPathError): 83*4882a593Smuzhiyun return info 84*4882a593Smuzhiyun info['commit'] = repo.head.commit.hexsha 85*4882a593Smuzhiyun info['commit_count'] = repo.head.commit.count() 86*4882a593Smuzhiyun try: 87*4882a593Smuzhiyun info['branch'] = repo.active_branch.name 88*4882a593Smuzhiyun except TypeError: 89*4882a593Smuzhiyun info['branch'] = '(nobranch)' 90*4882a593Smuzhiyun return info 91*4882a593Smuzhiyun 92*4882a593Smuzhiyundef get_layers(layers): 93*4882a593Smuzhiyun """Returns layer information in dict format""" 94*4882a593Smuzhiyun layer_dict = OrderedDict() 95*4882a593Smuzhiyun for layer in layers.split(): 96*4882a593Smuzhiyun layer_name = os.path.basename(layer) 97*4882a593Smuzhiyun layer_dict[layer_name] = git_rev_info(layer) 98*4882a593Smuzhiyun return layer_dict 99*4882a593Smuzhiyun 100*4882a593Smuzhiyundef write_metadata_file(file_path, metadata): 101*4882a593Smuzhiyun """ Writes metadata to a XML file in directory. """ 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun xml = dict_to_XML('metadata', metadata) 104*4882a593Smuzhiyun xml_doc = parseString(tostring(xml).decode('UTF-8')) 105*4882a593Smuzhiyun with open(file_path, 'w') as f: 106*4882a593Smuzhiyun f.write(xml_doc.toprettyxml()) 107*4882a593Smuzhiyun 108*4882a593Smuzhiyundef dict_to_XML(tag, dictionary, **kwargs): 109*4882a593Smuzhiyun """ Return XML element converting dicts recursively. """ 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun elem = Element(tag, **kwargs) 112*4882a593Smuzhiyun for key, val in dictionary.items(): 113*4882a593Smuzhiyun if tag == 'layers': 114*4882a593Smuzhiyun child = (dict_to_XML('layer', val, name=key)) 115*4882a593Smuzhiyun elif isinstance(val, MutableMapping): 116*4882a593Smuzhiyun child = (dict_to_XML(key, val)) 117*4882a593Smuzhiyun else: 118*4882a593Smuzhiyun if tag == 'config': 119*4882a593Smuzhiyun child = Element('variable', name=key) 120*4882a593Smuzhiyun else: 121*4882a593Smuzhiyun child = Element(key) 122*4882a593Smuzhiyun child.text = str(val) 123*4882a593Smuzhiyun elem.append(child) 124*4882a593Smuzhiyun return elem 125