#!/usr/bin/python3 ## Copyright (C) 2014 ABRT team ## Copyright (C) 2014 Red Hat, Inc. ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA """This module provides algorithms for generating Machine IDs. """ import os import sys from argparse import ArgumentParser from subprocess import check_output import logging import hashlib def generate_machine_id_dmidecode(): """Generate a machine_id based off dmidecode fields The function generates the same result as sosreport-uploader Returns a machine ID as string or throws RuntimeException """ if not os.path.isfile("/usr/sbin/dmidecode"): raise RuntimeError("Could not find dmidecode. It might not be available for this " \ "architecture.") # What to look for keys = ['system-manufacturer', 'system-product-name', 'system-serial-number', 'system-uuid'] # Create a sha256 of ^ for machine_id machine_id = hashlib.sha256() # Run dmidecode command for k in keys: data = check_output(["dmidecode", "-s", k]).strip() # Update the hash as we find the fields we are looking for machine_id.update(data) # Create sha256 digest return machine_id.hexdigest() def generate_machine_id_systemd(): """Generate a machine_id equals to a one generated by systemd This function returns contents of /etc/machine-id Returns a machine ID as string or throws RuntimeException. """ try: with open('/etc/machine-id', 'r') as midf: return "".join((l.strip() for l in midf)) except IOError as ex: raise RuntimeError("Could not use systemd's machine-id: {0}" .format(str(ex))) GENERATORS = { 'sosreport_uploader-dmidecode' : generate_machine_id_dmidecode, 'systemd' : generate_machine_id_systemd } def generate_machine_id(generators): """Generates all requested machine id with all required generators Keyword arguments: generators -- a list of generator names Returns a dictionary where keys are generators and associated values are products of those generators. """ ids = {} workers = GENERATORS for sd in generators: try: ids[sd] = workers[sd]() except RuntimeError as ex: logging.error("Machine-ID generator '{0}' failed: {1}" .format(sd, str(ex))) return ids def print_result(ids, outfile, prefixed): """Writes a dictionary of machine ids to a file Each dictionary entry is written on a single line. The function does not print trailing new-line if the dictionary contains only one item as it is common format of one-liners placed in a dump directory. Keyword arguments: ids -- a dictionary [generator name: machine ids] outfile -- output file prefixed -- use 'generator name=' prefix or not """ fmt = '{0}={1}' if prefixed else '{1}' if len(ids) > 1: fmt += '\n' for sd, mid in ids.items(): outfile.write(fmt.format(sd,mid)) def print_generators(outfile=None): """Prints requested generators Keyword arguments: outfile -- output file (default: sys.stdout) """ if outfile is None: outfile = sys.stdout for sd in GENERATORS.keys(): outfile.write("{0}\n".format(sd)) if __name__ == '__main__': CMDARGS = ArgumentParser(description = "Generate a machine_id") CMDARGS.add_argument('-o', '--output', type=str, help="Output file") CMDARGS.add_argument('-g', '--generators', nargs='+', type=str, help="Use given generators only") CMDARGS.add_argument('-l', '--list-generators', action='store_true', default=False, help="Print out a list of usable generators") CMDARGS.add_argument('-n', '--noprefix', action='store_true', default=False, help="Do not use generator name as prefix for IDs") OPTIONS = CMDARGS.parse_args() ARGS = vars(OPTIONS) logging.basicConfig(format='%(message)s') if ARGS['list_generators']: print_generators() sys.exit(0) requested_generators = None if ARGS['generators']: requested_generators = ARGS['generators'] else: requested_generators = list(GENERATORS.keys()) machineids = generate_machine_id(requested_generators) if ARGS['output']: try: with open(ARGS['output'], 'w') as fout: print_result(machineids, fout, not ARGS['noprefix']) except IOError as ex: logging.error("Could not open output file: {0}".format(str(ex))) sys.exit(1) else: print_result(machineids, sys.stdout, not ARGS['noprefix']) # print_results() omits new-line for one-liners if len(machineids) == 1: sys.stdout.write('\n') sys.exit(len(requested_generators) - len(machineids))