pkgrepo.py revision 2569
1968N/A#!/usr/bin/python2.6
1968N/A#
1968N/A# CDDL HEADER START
1968N/A#
1968N/A# The contents of this file are subject to the terms of the
1968N/A# Common Development and Distribution License (the "License").
1968N/A# You may not use this file except in compliance with the License.
1968N/A#
1968N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1968N/A# or http://www.opensolaris.org/os/licensing.
1968N/A# See the License for the specific language governing permissions
1968N/A# and limitations under the License.
1968N/A#
1968N/A# When distributing Covered Code, include this CDDL HEADER in each
1968N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1968N/A# If applicable, add the following below this CDDL HEADER, with the
1968N/A# fields enclosed by brackets "[]" replaced with your own identifying
1968N/A# information: Portions Copyright [yyyy] [name of copyright owner]
1968N/A#
1968N/A# CDDL HEADER END
1968N/A#
1968N/A
1968N/A#
2310N/A# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
1968N/A#
1968N/A
1968N/APKG_CLIENT_NAME = "pkgrepo"
1968N/A
1968N/A# pkgrepo exit codes
1968N/AEXIT_OK = 0
1968N/AEXIT_OOPS = 1
1968N/AEXIT_BADOPT = 2
1968N/AEXIT_PARTIAL = 3
1968N/A
1968N/A# listing constants
2510N/ALISTING_FORMATS = ("json", "json-formatted", "tsv")
1968N/A
2028N/A# globals
2028N/Atmpdirs = []
2028N/A
2028N/Aimport atexit
2301N/Aimport collections
2028N/Aimport copy
1968N/Aimport errno
1968N/Aimport getopt
1968N/Aimport gettext
1968N/Aimport locale
1968N/Aimport logging
2028N/Aimport shlex
2028N/Aimport shutil
1968N/Aimport sys
2028N/Aimport tempfile
2028N/Aimport traceback
1968N/Aimport warnings
1968N/A
1968N/Afrom pkg.client import global_settings
1968N/Afrom pkg.misc import msg, PipeError
1968N/Aimport pkg
2028N/Aimport pkg.catalog
1968N/Aimport pkg.client.api_errors as apx
2301N/Aimport pkg.client.progress
1968N/Aimport pkg.client.publisher as publisher
2028N/Aimport pkg.client.transport.transport as transport
1968N/Aimport pkg.misc as misc
1968N/Aimport pkg.server.repository as sr
1968N/A
1968N/Alogger = global_settings.logger
2028N/A
2028N/A@atexit.register
2028N/Adef cleanup():
2028N/A """To be called at program finish."""
2028N/A for d in tmpdirs:
2028N/A shutil.rmtree(d, True)
1968N/A
2301N/A
1968N/Adef error(text, cmd=None):
1968N/A """Emit an error message prefixed by the command name """
1968N/A
1968N/A if cmd:
1968N/A text = "%s: %s" % (cmd, text)
1968N/A pkg_cmd = "pkgrepo "
1968N/A else:
1968N/A pkg_cmd = "pkgrepo: "
1968N/A
1968N/A # If we get passed something like an Exception, we can convert
1968N/A # it down to a string.
1968N/A text = str(text)
1968N/A
1968N/A # If the message starts with whitespace, assume that it should come
1968N/A # *before* the command-name prefix.
1968N/A text_nows = text.lstrip()
1968N/A ws = text[:len(text) - len(text_nows)]
1968N/A
1968N/A # This has to be a constant value as we can't reliably get our actual
1968N/A # program name on all platforms.
1968N/A logger.error(ws + pkg_cmd + text_nows)
1968N/A
1968N/A
2301N/Adef get_tracker(quiet=False):
2301N/A if quiet:
2301N/A progtrack = pkg.client.progress.QuietProgressTracker()
2301N/A else:
2301N/A try:
2301N/A progtrack = \
2301N/A pkg.client.progress.FancyUNIXProgressTracker()
2301N/A except pkg.client.progress.ProgressTrackerException:
2301N/A progtrack = \
2301N/A pkg.client.progress.CommandLineProgressTracker()
2301N/A return progtrack
2301N/A
2301N/A
1968N/Adef usage(usage_error=None, cmd=None, retcode=2, full=False):
1968N/A """Emit a usage message and optionally prefix it with a more
1968N/A specific error message. Causes program to exit.
1968N/A """
1968N/A
1968N/A if usage_error:
1968N/A error(usage_error, cmd=cmd)
1968N/A
1968N/A if not full:
1968N/A # The full usage message isn't desired.
1968N/A logger.error(_("Try `pkgrepo --help or -?' for more "
1968N/A "information."))
1968N/A sys.exit(retcode)
1968N/A
1968N/A msg(_("""\
1968N/AUsage:
1978N/A pkgrepo [options] command [cmd_options] [operands]
1968N/A
1968N/ASubcommands:
2510N/A pkgrepo create [--version ver] uri_or_path
2028N/A
2301N/A pkgrepo add-publisher -s repo_uri_or_path publisher ...
2142N/A
2301N/A pkgrepo get [-F format] [-p publisher ...] -s repo_uri_or_path
2028N/A [section/property ...]
2028N/A
2028N/A pkgrepo info [-F format] [-H] [-p publisher ...]
2301N/A -s repo_uri_or_path
1978N/A
2510N/A pkgrepo list [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
2510N/A [pkg_fmri_pattern ...]
2510N/A
2301N/A pkgrepo rebuild [-p publisher ...] -s repo_uri_or_path
2132N/A [--no-catalog] [--no-index]
2028N/A
2301N/A pkgrepo refresh [-p publisher ...] -s repo_uri_or_path
2132N/A [--no-catalog] [--no-index]
2028N/A
2301N/A pkgrepo remove [-n] [-p publisher ...] -s repo_uri_or_path
2301N/A pkg_fmri_pattern ...
2301N/A
2301N/A pkgrepo set [-p publisher ...] -s repo_uri_or_path
2028N/A section/property[+|-]=[value] ... or
2028N/A section/property[+|-]=([value]) ...
2028N/A
2028N/A pkgrepo help
2028N/A pkgrepo version
1968N/A
1968N/AOptions:
1978N/A --help or -?
1978N/A Displays a usage message."""))
1968N/A
1968N/A sys.exit(retcode)
1968N/A
1968N/Aclass OptionError(Exception):
1968N/A """Option exception. """
1968N/A
1968N/A def __init__(self, *args):
1968N/A Exception.__init__(self, *args)
1968N/A
1968N/A
1968N/Adef parse_uri(uri):
1968N/A """Parse the repository location provided and attempt to transform it
1968N/A into a valid repository URI.
1968N/A """
1968N/A
2028N/A return publisher.RepositoryURI(misc.parse_uri(uri))
1968N/A
1968N/A
2301N/Adef subcmd_remove(conf, args):
2301N/A subcommand = "remove"
2301N/A
2301N/A opts, pargs = getopt.getopt(args, "np:s:")
2301N/A
2301N/A dry_run = False
2301N/A pubs = set()
2301N/A for opt, arg in opts:
2301N/A if opt == "-n":
2301N/A dry_run = True
2301N/A elif opt == "-p":
2301N/A pubs.add(arg)
2301N/A elif opt == "-s":
2301N/A conf["repo_uri"] = parse_uri(arg)
2301N/A
2301N/A if not pargs:
2301N/A usage(_("At least one package pattern must be provided."),
2301N/A cmd=subcommand)
2301N/A
2301N/A # Get repository object.
2301N/A if not conf.get("repo_uri", None):
2301N/A usage(_("A package repository location must be provided "
2301N/A "using -s."), cmd=subcommand)
2301N/A repo = get_repo(conf, read_only=False, subcommand=subcommand)
2301N/A
2301N/A if "all" in pubs:
2301N/A pubs = set()
2301N/A
2301N/A # Find matching packages.
2301N/A try:
2301N/A matching, refs = repo.get_matching_fmris(pargs, pubs=pubs)
2301N/A except apx.PackageMatchErrors, e:
2301N/A error(str(e), cmd=subcommand)
2301N/A return EXIT_OOPS
2301N/A
2301N/A if dry_run:
2301N/A # Don't make any changes; display list of packages to be
2301N/A # removed and exit.
2301N/A packages = set(f for m in matching.values() for f in m)
2301N/A count = len(packages)
2301N/A plist = "\n".join("\t%s" % p for p in sorted(packages))
2301N/A logger.info(_("%(count)d package(s) will be removed:\n"
2301N/A "%(plist)s") % locals())
2301N/A return EXIT_OK
2301N/A
2301N/A progtrack = get_tracker()
2301N/A packages = collections.defaultdict(list)
2301N/A for m in matching.values():
2301N/A for f in m:
2301N/A packages[f.publisher].append(f)
2301N/A
2301N/A for pub in packages:
2301N/A logger.info(_("Removing packages for publisher %s ...") % pub)
2301N/A repo.remove_packages(packages[pub], progtrack=progtrack,
2301N/A pub=pub)
2301N/A if len(packages) > 1:
2301N/A # Add a newline between each publisher.
2301N/A logger.info("")
2301N/A
2301N/A return EXIT_OK
2301N/A
2301N/A
2028N/Adef get_repo(conf, read_only=True, subcommand=None):
2028N/A """Return the repository object for current program configuration."""
2028N/A
2028N/A repo_uri = conf["repo_uri"]
2028N/A if repo_uri.scheme != "file":
2028N/A usage(_("Network repositories are not currently supported "
2028N/A "for this operation."), cmd=subcommand)
2028N/A
2028N/A path = repo_uri.get_pathname()
2028N/A if not path:
2028N/A # Bad URI?
2028N/A raise sr.RepositoryInvalidError(str(repo_uri))
2028N/A return sr.Repository(read_only=read_only, root=path)
2028N/A
2028N/A
2073N/Adef setup_transport(conf, subcommand=None):
2028N/A repo_uri = conf.get("repo_uri", None)
2028N/A if not repo_uri:
2028N/A usage(_("No repository location specified."), cmd=subcommand)
2028N/A
2028N/A temp_root = misc.config_temp_root()
2028N/A
2028N/A tmp_dir = tempfile.mkdtemp(dir=temp_root)
2028N/A tmpdirs.append(tmp_dir)
2028N/A
2028N/A incoming_dir = tempfile.mkdtemp(dir=temp_root)
2028N/A tmpdirs.append(incoming_dir)
2028N/A
2028N/A cache_dir = tempfile.mkdtemp(dir=temp_root)
2028N/A tmpdirs.append(cache_dir)
2028N/A
2028N/A # Create transport and transport config.
2028N/A xport, xport_cfg = transport.setup_transport()
2073N/A xport_cfg.add_cache(cache_dir, readonly=False)
2073N/A xport_cfg.incoming_root = incoming_dir
2028N/A
2028N/A # Configure target publisher.
2028N/A src_pub = transport.setup_publisher(str(repo_uri), "target", xport,
2028N/A xport_cfg, remote_prefix=True)
2028N/A
2028N/A return xport, src_pub, tmp_dir
2028N/A
1968N/A
2142N/Adef subcmd_add_publisher(conf, args):
2142N/A """Add publisher(s) to the specified repository."""
2142N/A
2142N/A subcommand = "add-publisher"
2142N/A
2142N/A opts, pargs = getopt.getopt(args, "s:")
2142N/A for opt, arg in opts:
2142N/A if opt == "-s":
2142N/A conf["repo_uri"] = parse_uri(arg)
2142N/A
2142N/A repo_uri = conf.get("repo_uri", None)
2142N/A if not repo_uri:
2142N/A usage(_("No repository location specified."), cmd=subcommand)
2142N/A if repo_uri.scheme != "file":
2142N/A usage(_("Network repositories are not currently supported "
2142N/A "for this operation."), cmd=subcommand)
2142N/A
2142N/A if not pargs:
2142N/A usage(_("At least one publisher must be specified"),
2142N/A cmd=subcommand)
2142N/A
2142N/A abort = False
2142N/A for pfx in pargs:
2142N/A if not misc.valid_pub_prefix(pfx):
2142N/A error(_("Invalid publisher prefix '%s'") % pfx,
2142N/A cmd=subcommand)
2142N/A abort = True
2142N/A if abort:
2142N/A return EXIT_OOPS
2142N/A
2142N/A repo = get_repo(conf, read_only=False, subcommand=subcommand)
2142N/A make_default = not repo.publishers
2142N/A existing = repo.publishers & set(pargs)
2142N/A
2142N/A # Elide the publishers that already exist, but retain the order
2142N/A # publishers were specified in.
2142N/A new_pubs = [
2142N/A pfx for pfx in pargs
2142N/A if pfx not in repo.publishers
2142N/A ]
2142N/A
2142N/A # Tricky logic; _set_pub will happily add new publishers if necessary
2142N/A # and not set any properties if you didn't specify any.
2142N/A rval = _set_pub(conf, subcommand, {}, new_pubs, repo)
2142N/A
2142N/A if make_default:
2142N/A # No publisher existed previously, so set the default publisher
2142N/A # to be the first new one that was added.
2142N/A _set_repo(conf, subcommand, { "publisher": {
2142N/A "prefix": new_pubs[0] } }, repo)
2142N/A
2142N/A if rval == EXIT_OK and existing:
2142N/A # Some of the publishers that were requested for addition
2142N/A # were already known.
2142N/A error(_("specified publisher(s) already exist: %s") %
2142N/A ", ".join(existing), cmd=subcommand)
2142N/A if new_pubs:
2142N/A return EXIT_PARTIAL
2142N/A return EXIT_OOPS
2142N/A return rval
2142N/A
2142N/A
2028N/Adef subcmd_create(conf, args):
2028N/A """Create a package repository at the given location."""
2028N/A
2028N/A subcommand = "create"
2028N/A
2028N/A opts, pargs = getopt.getopt(args, "s:", ["version="])
2028N/A
2028N/A version = None
2028N/A for opt, arg in opts:
2028N/A if opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
2028N/A elif opt == "--version":
2028N/A # This option is currently private and allows creating a
2028N/A # repository with a specific format based on version.
2028N/A try:
2028N/A version = int(arg)
2028N/A except ValueError:
2028N/A usage(_("Version must be an integer value."),
2028N/A cmd=subcommand)
1968N/A
2028N/A if len(pargs) > 1:
2028N/A usage(_("Only one repository location may be specified."),
2028N/A cmd=subcommand)
2028N/A elif pargs:
2028N/A conf["repo_uri"] = parse_uri(pargs[0])
2028N/A
2028N/A repo_uri = conf.get("repo_uri", None)
2028N/A if not repo_uri:
2028N/A usage(_("No repository location specified."), cmd=subcommand)
2028N/A if repo_uri.scheme != "file":
2028N/A usage(_("Network repositories are not currently supported "
2028N/A "for this operation."), cmd=subcommand)
2028N/A
2028N/A # Attempt to create a repository at the specified location. Allow
2028N/A # whatever exceptions are raised to bubble up.
2028N/A sr.repository_create(repo_uri, version=version)
2028N/A
2028N/A return EXIT_OK
2028N/A
2028N/A
2028N/Adef subcmd_get(conf, args):
2028N/A """Display repository properties."""
2028N/A
2028N/A subcommand = "get"
1968N/A omit_headers = False
1968N/A out_format = "default"
2028N/A pubs = set()
1968N/A
2028N/A opts, pargs = getopt.getopt(args, "F:Hp:s:")
1968N/A for opt, arg in opts:
1968N/A if opt == "-F":
1968N/A out_format = arg
1968N/A if out_format not in LISTING_FORMATS:
1968N/A usage(_("Unrecognized format %(format)s."
1968N/A " Supported formats: %(valid)s") % \
1968N/A { "format": out_format,
2510N/A "valid": LISTING_FORMATS }, cmd=subcommand)
1968N/A return EXIT_OOPS
1968N/A elif opt == "-H":
1968N/A omit_headers = True
2028N/A elif opt == "-p":
2028N/A pubs.add(arg)
2028N/A elif opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
2028N/A
2028N/A # Setup transport so configuration can be retrieved.
2028N/A if not conf.get("repo_uri", None):
2028N/A usage(_("A package repository location must be provided "
2028N/A "using -s."), cmd=subcommand)
2073N/A xport, xpub, tmp_dir = setup_transport(conf, subcommand=subcommand)
2028N/A
2028N/A # Get properties.
2028N/A if pubs:
2028N/A return _get_pub(conf, subcommand, xport, xpub, omit_headers,
2028N/A out_format, pubs, pargs)
2028N/A return _get_repo(conf, subcommand, xport, xpub, omit_headers,
2028N/A out_format, pargs)
2028N/A
2028N/A
2028N/Adef _get_repo(conf, subcommand, xport, xpub, omit_headers, out_format, pargs):
2028N/A """Display repository properties."""
1968N/A
1968N/A # Configuration index is indexed by section name and property name.
2028N/A # Retrieve and flatten it to simplify listing process.
2028N/A stat_idx = xport.get_status(xpub)
2028N/A cfg_idx = stat_idx.get("repository", {}).get("configuration", {})
1968N/A props = set()
1968N/A
1968N/A # Set minimum widths for section and property name columns by using the
1968N/A # length of the column headers.
1968N/A max_sname_len = len(_("SECTION"))
1968N/A max_pname_len = len(_("PROPERTY"))
1968N/A
1968N/A for sname in cfg_idx:
1968N/A max_sname_len = max(max_sname_len, len(sname))
1968N/A for pname in cfg_idx[sname]:
1968N/A max_pname_len = max(max_pname_len, len(pname))
1968N/A props.add("/".join((sname, pname)))
1968N/A
2028N/A req_props = set(pargs)
2028N/A if len(req_props) >= 1:
2028N/A found = props & req_props
2028N/A notfound = req_props - found
1968N/A del props
1968N/A else:
1968N/A found = props
1968N/A notfound = set()
1968N/A
1968N/A def gen_listing():
1968N/A for prop in sorted(found):
1968N/A sname, pname = prop.rsplit("/", 1)
2028N/A sval = cfg_idx[sname][pname]
1968N/A yield {
1968N/A "section": sname,
1968N/A "property": pname,
1968N/A "value": sval,
1968N/A }
1968N/A
1968N/A # SECTION PROPERTY VALUE
1968N/A # <sec_1> <prop_1> <prop_1_value>
1968N/A # <sec_2> <prop_2> <prop_2_value>
1968N/A # ...
1968N/A field_data = {
2510N/A "section" : [("default", "json", "tsv"), _("SECTION"), ""],
2510N/A "property" : [("default", "json", "tsv"), _("PROPERTY"), ""],
2510N/A "value" : [("default", "json", "tsv"), _("VALUE"), ""],
1968N/A }
2510N/A desired_field_order = (_("SECTION"), _("PROPERTY"), _("VALUE"))
1968N/A
1968N/A # Default output formatting.
1968N/A def_fmt = "%-" + str(max_sname_len) + "s %-" + str(max_pname_len) + \
1968N/A "s %s"
1968N/A
2028N/A if found or (not req_props and out_format == "default"):
2453N/A # print without trailing newline.
2510N/A sys.stdout.write(misc.get_listing(desired_field_order,
2453N/A field_data, gen_listing(), out_format, def_fmt,
2453N/A omit_headers))
1968N/A
1968N/A if found and notfound:
1968N/A return EXIT_PARTIAL
2028N/A if req_props and not found:
1968N/A if out_format == "default":
1968N/A # Don't pollute other output formats.
1968N/A error(_("no matching properties found"),
1968N/A cmd=subcommand)
1968N/A return EXIT_OOPS
1968N/A return EXIT_OK
1968N/A
1968N/A
2510N/Adef _get_matching_pubs(subcommand, pubs, xport, xpub, out_format="default",
2510N/A use_transport=False):
1968N/A
2028N/A # Retrieve publisher information.
2028N/A pub_data = xport.get_publisherdata(xpub)
2028N/A known_pubs = set(p.prefix for p in pub_data)
2028N/A if len(pubs) > 0 and "all" not in pubs:
2028N/A found = known_pubs & pubs
2028N/A notfound = pubs - found
2510N/A pub_data = [p for p in pub_data if p.prefix in found]
1968N/A else:
2028N/A found = known_pubs
2028N/A notfound = set()
2028N/A
2510N/A if use_transport:
2510N/A # Assign transport information.
2510N/A for p in pub_data:
2510N/A p.repository = xpub.repository
2510N/A
2028N/A # Establish initial return value and perform early exit if appropriate.
2028N/A rval = EXIT_OK
2028N/A if found and notfound:
2028N/A rval = EXIT_PARTIAL
2028N/A elif pubs and not found:
2028N/A if out_format == "default":
2028N/A # Don't pollute other output formats.
2028N/A error(_("no matching publishers found"),
2028N/A cmd=subcommand)
2132N/A return EXIT_OOPS, None, None
2132N/A return rval, found, pub_data
2132N/A
2132N/A
2132N/Adef _get_pub(conf, subcommand, xport, xpub, omit_headers, out_format, pubs,
2132N/A pargs):
2132N/A """Display publisher properties."""
2132N/A
2132N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2132N/A xpub, out_format=out_format)
2132N/A if rval == EXIT_OOPS:
2132N/A return rval
2028N/A
2028N/A # Set minimum widths for section and property name columns by using the
2028N/A # length of the column headers and data.
2028N/A max_pubname_len = str(max(
2028N/A [len(_("PUBLISHER"))] + [len(p) for p in found]
2028N/A ))
2028N/A max_sname_len = len(_("SECTION"))
2028N/A max_pname_len = len(_("PROPERTY"))
2028N/A
2028N/A # For each requested publisher, retrieve the requested property data.
2028N/A pub_idx = {}
2028N/A for pub in pub_data:
2028N/A pub_idx[pub.prefix] = {
2028N/A "publisher": {
2028N/A "alias": pub.alias,
2028N/A "prefix": pub.prefix,
2028N/A },
2028N/A }
2028N/A
2310N/A pub_repo = pub.repository
2028N/A if pub_repo:
2028N/A pub_idx[pub.prefix]["repository"] = {
2028N/A "collection-type": pub_repo.collection_type,
2028N/A "description": pub_repo.description,
2028N/A "legal-uris": pub_repo.legal_uris,
2028N/A "mirrors": pub_repo.mirrors,
2028N/A "name": pub_repo.name,
2028N/A "origins": pub_repo.origins,
2028N/A "refresh-seconds": pub_repo.refresh_seconds,
2028N/A "registration-uri": pub_repo.registration_uri,
2028N/A "related-uris": pub_repo.related_uris,
2028N/A }
2028N/A else:
2028N/A pub_idx[pub.prefix]["repository"] = {
2028N/A "collection-type": "core",
2028N/A "description": "",
2028N/A "legal-uris": [],
2028N/A "mirrors": [],
2028N/A "name": "",
2028N/A "origins": [],
2028N/A "refresh-seconds": "",
2028N/A "registration-uri": "",
2028N/A "related-uris": [],
2028N/A }
1968N/A
2028N/A # Determine possible set of properties and lengths.
2028N/A props = set()
2028N/A for pub in pub_idx:
2073N/A for sname in pub_idx[pub]:
2073N/A max_sname_len = max(max_sname_len, len(sname))
2073N/A for pname in pub_idx[pub][sname]:
2073N/A max_pname_len = max(max_pname_len, len(pname))
2073N/A props.add("/".join((sname, pname)))
2028N/A
2028N/A # Determine properties to display.
2028N/A req_props = set(pargs)
2028N/A if len(req_props) >= 1:
2028N/A found = props & req_props
2028N/A notfound = req_props - found
2028N/A del props
2028N/A else:
2028N/A found = props
2028N/A notfound = set()
2028N/A
2028N/A def gen_listing():
2028N/A for pub in sorted(pub_idx.keys()):
2028N/A for prop in sorted(found):
2028N/A sname, pname = prop.rsplit("/", 1)
2028N/A sval = pub_idx[pub][sname][pname]
2028N/A yield {
2028N/A "publisher": pub,
2028N/A "section": sname,
2028N/A "property": pname,
2028N/A "value": sval,
2028N/A }
1968N/A
2028N/A # PUBLISHER SECTION PROPERTY VALUE
2028N/A # <pub_1> <sec_1> <prop_1> <prop_1_value>
2028N/A # <pub_1> <sec_2> <prop_2> <prop_2_value>
2028N/A # ...
2028N/A field_data = {
2510N/A "publisher" : [("default", "json", "tsv"), _("PUBLISHER"), ""],
2510N/A "section" : [("default", "json", "tsv"), _("SECTION"), ""],
2510N/A "property" : [("default", "json", "tsv"), _("PROPERTY"), ""],
2510N/A "value" : [("default", "json", "tsv"), _("VALUE"), ""],
2028N/A }
2028N/A desired_field_order = (_("PUBLISHER"), _("SECTION"), _("PROPERTY"),
2028N/A _("VALUE"))
1968N/A
2028N/A # Default output formatting.
2028N/A def_fmt = "%-" + str(max_pubname_len) + "s %-" + str(max_sname_len) + \
2028N/A "s %-" + str(max_pname_len) + "s %s"
2028N/A
2028N/A if found or (not req_props and out_format == "default"):
2453N/A # print without trailing newline.
2510N/A sys.stdout.write(misc.get_listing(desired_field_order,
2453N/A field_data, gen_listing(), out_format, def_fmt,
2453N/A omit_headers))
2028N/A
2028N/A if found and notfound:
2028N/A rval = EXIT_PARTIAL
2028N/A if req_props and not found:
2028N/A if out_format == "default":
2028N/A # Don't pollute other output formats.
2028N/A error(_("no matching properties found"),
2028N/A cmd=subcommand)
2028N/A rval = EXIT_OOPS
2028N/A return rval
1968N/A
1968N/A
2028N/Adef subcmd_info(conf, args):
1968N/A """Display a list of known publishers and a summary of known packages
1968N/A and when the package data for the given publisher was last updated.
1968N/A """
1968N/A
2028N/A subcommand = "info"
1968N/A omit_headers = False
1968N/A out_format = "default"
2028N/A pubs = set()
1968N/A
2028N/A opts, pargs = getopt.getopt(args, "F:Hp:s:")
1968N/A for opt, arg in opts:
1968N/A if opt == "-F":
2028N/A if arg not in LISTING_FORMATS:
1968N/A usage(_("Unrecognized format %(format)s."
1968N/A " Supported formats: %(valid)s") % \
2028N/A { "format": arg,
2510N/A "valid": LISTING_FORMATS }, cmd=subcommand)
1968N/A return EXIT_OOPS
2028N/A out_format = arg
1968N/A elif opt == "-H":
1968N/A omit_headers = True
2028N/A elif opt == "-p":
2028N/A pubs.add(arg)
2028N/A elif opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
1968N/A
2028N/A if pargs:
2028N/A usage(_("command does not take operands"), cmd=subcommand)
1968N/A
2028N/A # Setup transport so status can be retrieved.
2028N/A if not conf.get("repo_uri", None):
2028N/A usage(_("A package repository location must be provided "
2028N/A "using -s."), cmd=subcommand)
2073N/A xport, xpub, tmp_dir = setup_transport(conf, subcommand=subcommand)
2028N/A
2028N/A # Retrieve repository status information.
2028N/A stat_idx = xport.get_status(xpub)
2028N/A pub_idx = stat_idx.get("repository", {}).get("publishers", {})
2028N/A if len(pubs) > 0 and "all" not in pubs:
2028N/A found = set(pub_idx.keys()) & pubs
2028N/A notfound = pubs - found
1968N/A else:
1968N/A found = set(pub_idx.keys())
1968N/A notfound = set()
1968N/A
1968N/A def gen_listing():
1968N/A for pfx in found:
2028N/A pdata = pub_idx[pfx]
2028N/A pkg_count = pdata.get("package-count", 0)
2028N/A last_update = pdata.get("last-catalog-update", "")
2028N/A if last_update:
2028N/A # Reformat the date into something more user
2028N/A # friendly (and locale specific).
2028N/A last_update = pkg.catalog.basic_ts_to_datetime(
2028N/A last_update)
2028N/A last_update = "%sZ" % pkg.catalog.datetime_to_ts(
2028N/A last_update)
2028N/A rstatus = _(pub_idx[pfx].get("status", "online"))
1968N/A yield {
1968N/A "publisher": pfx,
1968N/A "packages": pkg_count,
2028N/A "status": rstatus,
2028N/A "updated": last_update,
1968N/A }
1968N/A
2028N/A # PUBLISHER PACKAGES STATUS UPDATED
2028N/A # <pub_1> <num_uniq_pkgs> <status> <cat_last_modified>
2028N/A # <pub_2> <num_uniq_pkgs> <status> <cat_last_modified>
1968N/A # ...
1968N/A field_data = {
2510N/A "publisher" : [("default", "json", "tsv"), _("PUBLISHER"), ""],
2510N/A "packages" : [("default", "json", "tsv"), _("PACKAGES"), ""],
2510N/A "status" : [("default", "json", "tsv"), _("STATUS"), ""],
2510N/A "updated" : [("default", "json", "tsv"), _("UPDATED"), ""],
1968N/A }
1968N/A
2028N/A desired_field_order = (_("PUBLISHER"), "", _("PACKAGES"), _("STATUS"),
1968N/A _("UPDATED"))
1968N/A
1968N/A # Default output formatting.
2028N/A pub_len = str(max(
2028N/A [len(desired_field_order[0])] + [len(p) for p in found]
2028N/A ))
2028N/A def_fmt = "%-" + pub_len + "s %-8s %-16s %s"
1968N/A
2028N/A if found or (not pubs and out_format == "default"):
2453N/A # print without trailing newline.
2510N/A sys.stdout.write(misc.get_listing(desired_field_order,
2453N/A field_data, gen_listing(), out_format, def_fmt,
2453N/A omit_headers))
1968N/A
1968N/A if found and notfound:
1968N/A return EXIT_PARTIAL
2028N/A if pubs and not found:
1968N/A if out_format == "default":
1968N/A # Don't pollute other output formats.
1968N/A error(_("no matching publishers found"),
1968N/A cmd=subcommand)
1968N/A return EXIT_OOPS
1968N/A return EXIT_OK
1968N/A
2510N/Adef subcmd_list(conf, args):
2510N/A """List all packages matching the specified patterns."""
2510N/A
2510N/A subcommand = "list"
2510N/A omit_headers = False
2510N/A out_format = "default"
2510N/A pubs = set()
2510N/A
2510N/A opts, pargs = getopt.getopt(args, "F:Hp:s:")
2510N/A for opt, arg in opts:
2510N/A if opt == "-F":
2510N/A out_format = arg
2510N/A if out_format not in LISTING_FORMATS:
2510N/A usage(_("Unrecognized format %(format)s."
2510N/A " Supported formats: %(valid)s") %
2510N/A { "format": out_format,
2510N/A "valid": LISTING_FORMATS }, cmd=subcommand)
2510N/A return EXIT_OOPS
2510N/A elif opt == "-H":
2510N/A omit_headers = True
2510N/A elif opt == "-p":
2510N/A pubs.add(arg)
2510N/A elif opt == "-s":
2510N/A conf["repo_uri"] = parse_uri(arg)
2510N/A
2510N/A # Setup transport so configuration can be retrieved.
2510N/A if not conf.get("repo_uri", None):
2510N/A usage(_("A package repository location must be provided "
2510N/A "using -s."), cmd=subcommand)
2510N/A xport, xpub, tmp_dir = setup_transport(conf, subcommand=subcommand)
2510N/A
2510N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2510N/A xpub, out_format=out_format, use_transport=True)
2510N/A if rval == EXIT_OOPS:
2510N/A return rval
2510N/A
2510N/A temp_root = misc.config_temp_root()
2510N/A progtrack = get_tracker()
2510N/A for pub in pub_data:
2510N/A meta_root = tempfile.mkdtemp(dir=temp_root)
2510N/A tmpdirs.append(meta_root)
2510N/A pub.meta_root = meta_root
2510N/A pub.transport = xport
2510N/A
2510N/A progtrack.catalog_start(pub.prefix)
2510N/A try:
2510N/A pub.refresh(True, True)
2510N/A except apx.TransportError:
2510N/A # Assume that a catalog doesn't exist for the target
2510N/A # publisher and drive on.
2510N/A pass
2510N/A finally:
2510N/A progtrack.catalog_done()
2510N/A
2510N/A listed = {}
2510N/A matched = set()
2510N/A unmatched = set()
2510N/A
2510N/A def gen_listing():
2510N/A collect_attrs = out_format.startswith("json")
2510N/A for pub in pub_data:
2510N/A cat = pub.catalog
2510N/A for f, states, attrs in cat.gen_packages(
2510N/A collect_attrs=collect_attrs, matched=matched,
2510N/A patterns=pargs, pubs=[pub.prefix],
2510N/A unmatched=unmatched, return_fmris=True):
2510N/A if not listed:
2510N/A listed["packages"] = True
2510N/A
2510N/A state = None
2510N/A if out_format == "default" or \
2510N/A out_format == "tsv":
2510N/A if cat.PKG_STATE_OBSOLETE in states:
2510N/A state = "o"
2510N/A elif cat.PKG_STATE_RENAMED in states:
2510N/A state = "r"
2510N/A
2510N/A ret = {
2510N/A "publisher": f.publisher,
2510N/A "name": f.pkg_name,
2510N/A "version": str(f.version),
2510N/A "release": str(f.version.release),
2510N/A "build-release":
2510N/A str(f.version.build_release),
2510N/A "branch": str(f.version.branch),
2510N/A "timestamp":
2510N/A str(f.version.timestr),
2510N/A "pkg.fmri": str(f),
2510N/A "short_state": state,
2510N/A }
2510N/A
2510N/A for attr in attrs:
2510N/A ret[attr] = []
2510N/A for mods in attrs[attr]:
2510N/A d = dict(mods)
2510N/A d["value"] = \
2510N/A attrs[attr][mods]
2510N/A ret[attr].append(d)
2510N/A yield ret
2510N/A
2510N/A unmatched.difference_update(matched)
2510N/A
2510N/A field_data = {
2510N/A "publisher": [("default", "json", "tsv"), _("PUBLISHER"), ""],
2510N/A "name": [("default", "json", "tsv"), _("NAME"), ""],
2510N/A "version": [("default", "json"), _("VERSION"), ""],
2510N/A "release": [("json", "tsv",), _("RELEASE"), ""],
2510N/A "build-release": [("json", "tsv",), _("BUILD RELEASE"), ""],
2510N/A "branch": [("json", "tsv",), _("BRANCH"), ""],
2510N/A "timestamp": [("json", "tsv",), _("PACKAGING DATE"), ""],
2510N/A "pkg.fmri": [("json", "tsv",), _("FMRI"), ""],
2510N/A "short_state": [("default", "tsv"), "O", ""],
2510N/A }
2510N/A
2510N/A desired_field_order = (_("PUBLISHER"), _("NAME"), "O", _("VERSION"),
2510N/A _("SUMMARY"), _("DESCRIPTION"), _("CATEGORIES"), _("RELEASE"),
2510N/A _("BUILD RELEASE"), _("BRANCH"), _("PACKAGING DATE"), _("FMRI"),
2510N/A _("STATE"))
2510N/A
2510N/A # Default output formatting.
2510N/A max_pub_name_len = str(
2510N/A max(list(len(p) for p in found) + [len(_("PUBLISHER"))]))
2510N/A def_fmt = "%-" + max_pub_name_len + "s %-45s %-1s %-s"
2510N/A
2510N/A # print without trailing newline.
2510N/A sys.stdout.write(misc.get_listing(
2510N/A desired_field_order, field_data, gen_listing(),
2510N/A out_format, def_fmt, omit_headers))
2510N/A
2510N/A if not listed and pargs:
2510N/A # No matching packages.
2510N/A logger.error("")
2510N/A if not unmatched:
2510N/A unmatched = pargs
2510N/A error(apx.PackageMatchErrors(unmatched_fmris=unmatched),
2510N/A cmd=subcommand)
2510N/A return EXIT_OOPS
2510N/A elif unmatched:
2510N/A # One or more patterns didn't match a package from any
2510N/A # publisher; only display the error.
2510N/A logger.error("")
2510N/A error(apx.PackageMatchErrors(unmatched_fmris=unmatched),
2510N/A cmd=subcommand)
2510N/A return EXIT_PARTIAL
2510N/A
2510N/A return EXIT_OK
2510N/A
1968N/A
1968N/Adef subcmd_rebuild(conf, args):
1968N/A """Rebuild the repository's catalog and index data (as permitted)."""
1968N/A
1968N/A subcommand = "rebuild"
2028N/A build_catalog = True
1968N/A build_index = True
2028N/A
2132N/A opts, pargs = getopt.getopt(args, "p:s:", ["no-catalog", "no-index"])
2132N/A pubs = set()
1968N/A for opt, arg in opts:
2132N/A if opt == "-p":
2132N/A if not misc.valid_pub_prefix(arg):
2132N/A error(_("Invalid publisher prefix '%s'") % arg,
2132N/A cmd=subcommand)
2132N/A pubs.add(arg)
2132N/A elif opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
2028N/A elif opt == "--no-catalog":
2028N/A build_catalog = False
2028N/A elif opt == "--no-index":
1968N/A build_index = False
1968N/A
1968N/A if pargs:
1968N/A usage(_("command does not take operands"), cmd=subcommand)
1968N/A
2028N/A if not build_catalog and not build_index:
2028N/A # Why? Who knows; but do what was requested--nothing!
2028N/A return EXIT_OK
1968N/A
2028N/A # Setup transport so operation can be performed.
2028N/A if not conf.get("repo_uri", None):
2028N/A usage(_("A package repository location must be provided "
2028N/A "using -s."), cmd=subcommand)
2132N/A
2132N/A def do_rebuild(xport, xpub):
2132N/A if build_catalog and build_index:
2132N/A xport.publish_rebuild(xpub)
2132N/A elif build_catalog:
2132N/A xport.publish_rebuild_packages(xpub)
2132N/A elif build_index:
2132N/A xport.publish_rebuild_indexes(xpub)
2028N/A
2132N/A xport, xpub, tmp_dir = setup_transport(conf, subcommand=subcommand)
2132N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2132N/A xpub)
2132N/A if rval == EXIT_OOPS:
2132N/A return rval
1968N/A
2132N/A logger.info("Initiating repository rebuild.")
2132N/A for pfx in found:
2132N/A xpub.prefix = pfx
2132N/A do_rebuild(xport, xpub)
2132N/A
2132N/A return rval
1968N/A
1968N/A
1968N/Adef subcmd_refresh(conf, args):
1968N/A """Refresh the repository's catalog and index data (as permitted)."""
1968N/A
1968N/A subcommand = "refresh"
1968N/A add_content = True
1968N/A refresh_index = True
2028N/A
2132N/A opts, pargs = getopt.getopt(args, "p:s:", ["no-catalog", "no-index"])
2132N/A pubs = set()
1968N/A for opt, arg in opts:
2132N/A if opt == "-p":
2132N/A if not misc.valid_pub_prefix(arg):
2132N/A error(_("Invalid publisher prefix '%s'") % arg,
2132N/A cmd=subcommand)
2132N/A pubs.add(arg)
2132N/A elif opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
2028N/A elif opt == "--no-catalog":
1968N/A add_content = False
1968N/A elif opt == "--no-index":
1968N/A refresh_index = False
1968N/A
1968N/A if pargs:
1968N/A usage(_("command does not take operands"), cmd=subcommand)
1968N/A
1968N/A if not add_content and not refresh_index:
1968N/A # Why? Who knows; but do what was requested--nothing!
1968N/A return EXIT_OK
1968N/A
2028N/A # Setup transport so operation can be performed.
2028N/A if not conf.get("repo_uri", None):
2028N/A usage(_("A package repository location must be provided "
2028N/A "using -s."), cmd=subcommand)
2132N/A
2132N/A def do_refresh(xport, xpub):
2132N/A if add_content and refresh_index:
2132N/A xport.publish_refresh(xpub)
2132N/A elif add_content:
2132N/A xport.publish_refresh_packages(xpub)
2132N/A elif refresh_index:
2132N/A xport.publish_refresh_indexes(xpub)
2028N/A
2132N/A xport, xpub, tmp_dir = setup_transport(conf, subcommand=subcommand)
2132N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2132N/A xpub)
2132N/A if rval == EXIT_OOPS:
2132N/A return rval
2132N/A
2132N/A logger.info("Initiating repository refresh.")
2132N/A for pfx in found:
2132N/A xpub.prefix = pfx
2132N/A do_refresh(xport, xpub)
2132N/A
2132N/A return rval
2028N/A
2028N/A
2028N/Adef subcmd_set(conf, args):
2028N/A """Set repository properties."""
2028N/A
2028N/A subcommand = "set"
2028N/A pubs = set()
2028N/A
2028N/A opts, pargs = getopt.getopt(args, "p:s:")
2028N/A for opt, arg in opts:
2028N/A if opt == "-p":
2028N/A pubs.add(arg)
2028N/A elif opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
2028N/A
2028N/A bad_args = False
2028N/A props = {}
2028N/A if not pargs:
2028N/A bad_args = True
2028N/A else:
2028N/A for arg in pargs:
2028N/A try:
2028N/A # Attempt to parse property into components.
2028N/A prop, val = arg.split("=", 1)
2028N/A sname, pname = prop.rsplit("/", 1)
2028N/A
2028N/A # Store property values by section.
2028N/A props.setdefault(sname, {})
2028N/A
2028N/A # Parse the property value into a list if
2028N/A # necessary, otherwise append it to the list
2028N/A # of values for the property.
2028N/A if len(val) > 0 and val[0] == "(" and \
2028N/A val[-1] == ")":
2028N/A val = shlex.split(val.strip("()"))
2028N/A
2028N/A if sname in props and pname in props[sname]:
2028N/A # Determine if previous value is already
2028N/A # a list, and if not, convert and append
2028N/A # the value.
2028N/A pval = props[sname][pname]
2028N/A if not isinstance(pval, list):
2028N/A pval = [pval]
2028N/A if isinstance(val, list):
2028N/A pval.extend(val)
2028N/A else:
2028N/A pval.append(val)
2028N/A props[sname][pname] = pval
2028N/A else:
2028N/A # Otherwise, just store the value.
2028N/A props[sname][pname] = val
2028N/A except ValueError:
2028N/A bad_args = True
2028N/A break
2028N/A
2028N/A if bad_args:
2028N/A usage(_("a property name and value must be provided in the "
2028N/A "form <section/property>=<value> or "
2028N/A "<section/property>=([\"<value>\" ...])"))
2028N/A
2028N/A # Get repository object.
2028N/A if not conf.get("repo_uri", None):
2028N/A usage(_("A package repository location must be provided "
2028N/A "using -s."), cmd=subcommand)
2028N/A repo = get_repo(conf, read_only=False, subcommand=subcommand)
2028N/A
2028N/A # Set properties.
2028N/A if pubs:
2142N/A return _set_pub(conf, subcommand, props, pubs, repo)
2028N/A
2142N/A return _set_repo(conf, subcommand, props, repo)
2028N/A
1968N/A
2142N/Adef _set_pub(conf, subcommand, props, pubs, repo):
2028N/A """Set publisher properties."""
2028N/A
2028N/A for sname, sprops in props.iteritems():
2028N/A if sname not in ("publisher", "repository"):
2028N/A usage(_("unknown property section "
2028N/A "'%s'") % sname, cmd=subcommand)
2028N/A for pname in sprops:
2073N/A if sname == "publisher" and pname == "prefix":
2028N/A usage(_("'%s' may not be set using "
2028N/A "this command" % pname))
2028N/A attrname = pname.replace("-", "_")
2028N/A if not hasattr(publisher.Publisher, attrname) and \
2028N/A not hasattr(publisher.Repository, attrname):
2028N/A usage(_("unknown property '%s'") %
2028N/A pname, cmd=subcommand)
2028N/A
2028N/A if "all" in pubs:
2028N/A # Default to list of all publishers.
2028N/A pubs = repo.publishers
2028N/A if not pubs:
2028N/A # If there are still no known publishers, this
2028N/A # operation cannot succeed, so fail now.
2028N/A usage(_("One or more publishers must be specified to "
2028N/A "create and set properties for as none exist yet."),
2028N/A cmd=subcommand)
2028N/A
2028N/A # Get publishers and update properties.
2028N/A failed = []
2028N/A new_pub = False
2028N/A for pfx in pubs:
2028N/A try:
2028N/A # Get a copy of the existing publisher.
2028N/A pub = copy.copy(repo.get_publisher(pfx))
2028N/A except sr.RepositoryUnknownPublisher, e:
2028N/A pub = publisher.Publisher(pfx)
2028N/A new_pub = True
2028N/A except sr.RepositoryError, e:
2028N/A failed.append((pfx, e))
2028N/A continue
2028N/A
2028N/A try:
2028N/A # Set/update the publisher's properties.
2028N/A for sname, sprops in props.iteritems():
2028N/A if sname == "publisher":
2028N/A target = pub
2028N/A elif sname == "repository":
2310N/A target = pub.repository
2028N/A if not target:
2028N/A target = publisher.Repository()
2310N/A pub.repository = target
2028N/A
2028N/A for pname, val in sprops.iteritems():
2028N/A attrname = pname.replace("-", "_")
2028N/A pval = getattr(target, attrname)
2028N/A if isinstance(pval, list) and \
2028N/A not isinstance(val, list):
2028N/A # If the target property expects
2028N/A # a list, transform the provided
2028N/A # value into one if it isn't
2028N/A # already.
2028N/A if val == "":
2028N/A val = []
2028N/A else:
2028N/A val = [val]
2028N/A setattr(target, attrname, val)
2028N/A except apx.ApiException, e:
2028N/A failed.append((pfx, e))
2028N/A continue
2028N/A
2028N/A if new_pub:
2028N/A repo.add_publisher(pub)
2028N/A else:
2028N/A repo.update_publisher(pub)
2028N/A
2028N/A if failed:
2028N/A for pfx, details in failed:
2028N/A error(_("Unable to set properties for publisher "
2028N/A "'%(pfx)s':\n%(details)s") % locals())
2028N/A if len(failed) < len(pubs):
2028N/A return EXIT_PARTIAL
2028N/A return EXIT_OOPS
2028N/A return EXIT_OK
2028N/A
2028N/A
2142N/Adef _set_repo(conf, subcommand, props, repo):
2028N/A """Set repository properties."""
2028N/A
2028N/A # Set properties.
2028N/A for sname, props in props.iteritems():
2028N/A for pname, val in props.iteritems():
2028N/A repo.cfg.set_property(sname, pname, val)
2028N/A repo.write_config()
1968N/A
1968N/A return EXIT_OK
1968N/A
1968N/A
1968N/Adef subcmd_version(conf, args):
1968N/A """Display the version of the pkg(5) API."""
1968N/A
1968N/A subcommand = "version"
1968N/A if args:
1968N/A usage(_("command does not take operands"), cmd=subcommand)
1968N/A msg(pkg.VERSION)
1968N/A return EXIT_OK
1968N/A
1968N/A
1968N/Adef main_func():
1968N/A global_settings.client_name = PKG_CLIENT_NAME
1968N/A
1968N/A try:
1968N/A opts, pargs = getopt.getopt(sys.argv[1:], "s:?",
1968N/A ["help"])
1968N/A except getopt.GetoptError, e:
1968N/A usage(_("illegal global option -- %s") % e.opt)
1968N/A
1968N/A conf = {}
1968N/A show_usage = False
1968N/A for opt, arg in opts:
1968N/A if opt == "-s":
2028N/A conf["repo_uri"] = parse_uri(arg)
1968N/A elif opt in ("--help", "-?"):
1968N/A show_usage = True
1968N/A
1968N/A subcommand = None
1968N/A if pargs:
1968N/A subcommand = pargs.pop(0)
1968N/A if subcommand == "help":
1968N/A show_usage = True
1968N/A
1968N/A if show_usage:
1968N/A usage(retcode=0, full=True)
1968N/A elif not subcommand:
1968N/A usage(_("no subcommand specified"))
1968N/A
1968N/A subcommand = subcommand.replace("-", "_")
1968N/A func = globals().get("subcmd_%s" % subcommand, None)
1968N/A if not func:
2028N/A subcommand = subcommand.replace("_", "-")
1968N/A usage(_("unknown subcommand '%s'") % subcommand)
1968N/A
1968N/A try:
1968N/A return func(conf, pargs)
1968N/A except getopt.GetoptError, e:
1968N/A if e.opt in ("help", "?"):
1968N/A usage(full=True)
1968N/A usage(_("illegal option -- %s") % e.opt, cmd=subcommand)
1968N/A
2028N/A
1968N/A#
1968N/A# Establish a specific exit status which means: "python barfed an exception"
1968N/A# so that we can more easily detect these in testing of the CLI commands.
1968N/A#
1968N/Adef handle_errors(func, *args, **kwargs):
1968N/A """Catch exceptions raised by the main program function and then print
1968N/A a message and/or exit with an appropriate return code.
1968N/A """
1968N/A
2569N/A traceback_str = misc.get_traceback_message()
2126N/A
1968N/A try:
1968N/A # Out of memory errors can be raised as EnvironmentErrors with
1968N/A # an errno of ENOMEM, so in order to handle those exceptions
1968N/A # with other errnos, we nest this try block and have the outer
1968N/A # one handle the other instances.
1968N/A try:
1968N/A __ret = func(*args, **kwargs)
1968N/A except (MemoryError, EnvironmentError), __e:
1968N/A if isinstance(__e, EnvironmentError) and \
1968N/A __e.errno != errno.ENOMEM:
1968N/A raise
1968N/A error("\n" + misc.out_of_memory())
1968N/A __ret = EXIT_OOPS
1968N/A except SystemExit, __e:
1968N/A raise __e
2510N/A except (IOError, PipeError, KeyboardInterrupt), __e:
1968N/A # Don't display any messages here to prevent possible further
1968N/A # broken pipe (EPIPE) errors.
2510N/A if isinstance(__e, IOError) and __e.errno != errno.EPIPE:
2510N/A error(str(__e))
1968N/A __ret = EXIT_OOPS
1968N/A except apx.VersionException, __e:
1968N/A error(_("The pkgrepo command appears out of sync with the "
1968N/A "libraries provided\nby pkg:/package/pkg. The client "
1968N/A "version is %(client)s while the library\nAPI version is "
1968N/A "%(api)s.") % {'client': __e.received_version,
1968N/A 'api': __e.expected_version
1968N/A })
1968N/A __ret = EXIT_OOPS
2028N/A except apx.BadRepositoryURI, __e:
2028N/A error(str(__e))
2028N/A __ret = EXIT_BADOPT
1968N/A except (apx.ApiException, sr.RepositoryError), __e:
1968N/A error(str(__e))
1968N/A __ret = EXIT_OOPS
1968N/A except:
1968N/A traceback.print_exc()
1968N/A error(traceback_str)
1968N/A __ret = 99
1968N/A return __ret
1968N/A
1968N/A
1968N/Aif __name__ == "__main__":
1968N/A misc.setlocale(locale.LC_ALL, "", error)
1968N/A gettext.install("pkg", "/usr/share/locale")
1968N/A
1968N/A # Make all warnings be errors.
1968N/A warnings.simplefilter('error')
1968N/A
1968N/A __retval = handle_errors(main_func)
1968N/A try:
1968N/A logging.shutdown()
1968N/A except IOError:
1968N/A # Ignore python's spurious pipe problems.
1968N/A pass
1968N/A sys.exit(__retval)