#!/usr/bin/env python3 # Copyright 2014 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import json import logging import os import sys log = logging.getLogger() def os_family(distro): family = None if distro in ['fedora', 'rhel', 'rhel7', 'centos', 'centos7']: family = 'redhat' elif distro in ['debian', 'ubuntu']: family = 'debian' elif distro == 'opensuse': family = 'suse' elif distro == 'gentoo': family = 'gentoo' return family def os_major_release(release): if release in ['8-stream']: return '8' return release def main(): parser = argparse.ArgumentParser( description="Translate package name to distro specific name." " Exits with 1 if error is encountered, 2 if no pkg-map" " file is found. Otherwise exits with 0.") parser.add_argument('--element', default='', help='The element (namespace) to use for translation.') parser.add_argument('--pkg-map', default='', help='Path to specific pkg-map file. ' '(Useful for testing)') parser.add_argument('--distro', default=os.environ.get('DISTRO_NAME'), help='The distro name to use for translation.' ' Defaults to DISTRO_NAME') parser.add_argument('--release', default=os.environ.get('DIB_RELEASE'), help='A more specfic name for distribution release') parser.add_argument('--missing-ok', action="store_true", help='Do not consider missing mappings an error.' ' Causes packages where no mapping is set to be' ' printed.') # This tool has traditionally output status and debug messages on # stderr. The problem is if a caller has stderr > stdout then # actual output gets messed in with the logs. This allows callers # to disambiguate actual output by specifying a unique prefix. parser.add_argument('--prefix', default='', help='Output mapped packages with this prefix') parser.add_argument('--debug', dest='debug', action="store_true", help="Enable debugging output") args, extra = parser.parse_known_args() # Logs have traditionally gone to stderr with this tool. Maintain # compatibility level = logging.DEBUG if args.debug else logging.INFO logging.basicConfig(stream=sys.stderr, level=level) if not args.element and not args.pkg_map: log.error('Please specify an --element argument.') sys.exit(1) if args.element and args.pkg_map: log.error('Specify either --element or --pkg-map') sys.exit(1) if not args.distro: log.error('Please specify a --distro argument or set DISTRO_NAME.') sys.exit(1) if args.pkg_map: # specifying the pkg-map by hand is just for manual testing element = "<%s>" % args.pkg_map map_file = args.pkg_map else: element = args.element map_file = '/usr/share/pkg-map/%s' % element log.info("Mapping for %s : %s" % (element, ' '.join(extra))) if not os.path.exists(map_file): if args.missing_ok: log.info("No package map for %s, done" % element) for name in extra: print('%s%s' % (args.prefix, name)) sys.exit(0) else: log.error('Required pkg-map for %s element does not exist.' % args.element) sys.exit(2) with open(map_file) as fd: try: package_names = json.loads(fd.read()) # log.debug(pprint.pformat(package_names)) except ValueError: log.error('Unable to parse %s' % map_file) raise # Parse mapping data in this form using release/distro/family/default # Most specific takes priority; order is # - release # - distro # - family # - default # An empty package list can be provided. # # Example for Nova and Glance (using fictitious name for Fedora and SUSE # and package mapping for SUSE family) # { # "release": { # "fedora" : { # "23" : { # "nova_package": "openstack-compute-foo" # } # } # } # "distro": { # "fedora": { # "nova_package": "openstack-compute", # "glance_package": "openstack-image" # } # }, # "family": { # "redhat": { # "nova_package": "openstack-nova", # "glance_package": "openstack-glance" # }, # "suse": { # "nova_package": "" # } # }, # "default": { # "nova_package": "nova", # "glance_package": "glance" # } # } name_map = package_names.get('default', {}) if 'family' in package_names: family_map = package_names['family'].get(os_family(args.distro)) if family_map: name_map.update(family_map) if 'distro' in package_names: distro_map = package_names['distro'].get(args.distro) if distro_map: name_map.update(distro_map) if 'release' in package_names: try: # release is a sub-concept of distro release = os_major_release(args.release) release_map = package_names['release'][args.distro][release] name_map.update(release_map) except KeyError: pass # log.debug(pprint.pformat(name_map)) for name in extra: pkg_name = name_map.get(name) if pkg_name: log.debug("map %s -> %s" % (name, pkg_name)) print('%s%s' % (args.prefix, pkg_name)) elif name in name_map: log.debug("map %s -> " % (name)) continue else: if args.missing_ok: log.debug("pass -> %s" % (name)) print('%s%s' % (args.prefix, name)) else: log.error("%s has no valid mapping for package %s" % (element, name)) sys.exit(1) sys.exit(0) if __name__ == '__main__': main() # Tell emacs to use python-mode # Local variables: # mode: python # End: