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