pkgrepo.py revision 3356
3177N/A#!/usr/bin/python2.7
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#
3339N/A# Copyright (c) 2010, 2016, 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
3210N/AEXIT_DIFF = 10
1968N/A
1968N/A# listing constants
3031N/ALISTING_FORMATS = ("default", "json", "json-formatted", "tsv")
1968N/A
3210N/A# diff type
3210N/AMINUS = -1
3210N/APLUS = 1
3210N/ACOMMON = 0
3210N/Adiff_type_f = {MINUS: "- ", PLUS: "+ ", COMMON: ""}
3210N/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
3138N/Aimport os
3210N/Aimport operator
2028N/Aimport shlex
2028N/Aimport shutil
3234N/Aimport six
1968N/Aimport sys
2028N/Aimport tempfile
2843N/Aimport textwrap
2028N/Aimport traceback
1968N/Aimport warnings
3041N/Aimport itertools
3245N/Afrom imp import reload
1968N/A
1968N/Afrom pkg.client import global_settings
2864N/Afrom pkg.client.debugvalues import DebugValues
1968N/Afrom pkg.misc import msg, PipeError
3210N/Afrom prettytable import PrettyTable
1968N/Aimport pkg
2028N/Aimport pkg.catalog
1968N/Aimport pkg.client.api_errors as apx
2616N/Aimport pkg.client.pkgdefs as pkgdefs
2301N/Aimport pkg.client.progress
1968N/Aimport pkg.client.publisher as publisher
2028N/Aimport pkg.client.transport.transport as transport
3210N/Aimport pkg.fmri as fmri
1968N/Aimport pkg.misc as misc
1968N/Aimport pkg.server.repository as sr
3210N/Aimport simplejson as json
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:
3158N/A text = "{0}: {1}".format(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
2970N/A pkgrepo remove-publisher [-n] [--synchronous] -s repo_uri_or_path
2970N/A publisher ...
2970N/A
3245N/A pkgrepo get [-F format] [-p publisher ...] -s repo_uri_or_path
2864N/A [--key ssl_key ... --cert ssl_cert ...] [section/property ...]
2028N/A
2864N/A pkgrepo info [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
2864N/A [--key ssl_key ... --cert ssl_cert ...]
1978N/A
3245N/A pkgrepo list [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
2864N/A [--key ssl_key ... --cert ssl_cert ...] [pkg_fmri_pattern ...]
2510N/A
3041N/A pkgrepo contents [-m] [-t action_type ...] -s repo_uri_or_path
3041N/A [--key ssl_key ... --cert ssl_cert ...] [pkg_fmri_pattern ...]
3041N/A
2864N/A pkgrepo rebuild [-p publisher ...] -s repo_uri_or_path [--key ssl_key ...
2864N/A --cert ssl_cert ...] [--no-catalog] [--no-index]
2028N/A
2864N/A pkgrepo refresh [-p publisher ...] -s repo_uri_or_path [--key ssl_key ...
2864N/A --cert ssl_cert ...] [--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
3138N/A pkgrepo verify [-d] [-p publisher ...] [-i ignored_dep_file ...]
3138N/A [--disable verification ...] -s repo_uri_or_path
2843N/A
2843N/A pkgrepo fix [-v] [-p publisher ...] -s repo_uri_or_path
2843N/A
3210N/A pkgrepo diff [-vq] [--strict] [--parsable] [-p publisher ...]
3210N/A -s first_repo_uri_or_path [--key ssl_key ... --cert ssl_cert ...]
3210N/A -s second_repo_uri_or_path [--key ssl_key ... --cert ssl_cert ...]
3210N/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
3041N/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)
3171N/A except apx.PackageMatchErrors as 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)
3158N/A plist = "\n".join("\t{0}".format(
3158N/A p.get_fmri(include_build=False))
2958N/A for p in sorted(packages))
3158N/A logger.info(_("{count:d} package(s) will be removed:\n"
3158N/A "{plist}").format(**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:
3158N/A logger.info(
3158N/A _("Removing packages for publisher {0} ...").format(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
2990N/Adef get_repo(conf, allow_invalid=False, read_only=True, subcommand=None):
2990N/A """Return the repository object for current program configuration.
2990N/A
2990N/A 'allow_invalid' specifies whether potentially corrupt repositories are
2990N/A allowed; should only be True if performing a rebuild operation."""
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))
2990N/A return sr.Repository(allow_invalid=allow_invalid, read_only=read_only,
2990N/A root=path)
2028N/A
2028N/A
3210N/Adef setup_transport(repo_uri, subcommand=None, prefix=None, verbose=False,
3140N/A remote_prefix=True, ssl_key=None, ssl_cert=None):
2028N/A if not repo_uri:
2028N/A usage(_("No repository location specified."), cmd=subcommand)
2028N/A
3041N/A global tmpdirs
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
3041N/A xport_cfg.pkg_root = tmp_dir
2028N/A
3140N/A if not prefix:
3140N/A pub = "target"
3140N/A else:
3140N/A pub = prefix
3140N/A
2028N/A # Configure target publisher.
3140N/A src_pub = transport.setup_publisher(str(repo_uri), pub, xport,
3140N/A xport_cfg, remote_prefix=remote_prefix, ssl_key=ssl_key,
3140N/A ssl_cert=ssl_cert)
2028N/A
2028N/A return xport, src_pub, tmp_dir
2028N/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):
3158N/A error(_("Invalid publisher prefix '{0}'").format(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.
3158N/A error(_("specified publisher(s) already exist: {0}").format(
3158N/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
2970N/Adef subcmd_remove_publisher(conf, args):
2970N/A """Remove publisher(s) from a repository"""
2970N/A
2970N/A subcommand = "remove-publisher"
2970N/A
2970N/A dry_run = False
2970N/A synch = False
2970N/A opts, pargs = getopt.getopt(args, "ns:", ["synchronous"])
2970N/A for opt, arg in opts:
2970N/A if opt == "-s":
2970N/A conf["repo_uri"] = parse_uri(arg)
2970N/A elif opt == "-n":
2970N/A dry_run = True
2970N/A elif opt == "--synchronous":
2970N/A synch = True
2970N/A repo_uri = conf.get("repo_uri", None)
2970N/A if not repo_uri:
2970N/A usage(_("No repository location specified."), cmd=subcommand)
2970N/A if repo_uri.scheme != "file":
2970N/A usage(_("Network repositories are not currently supported "
2970N/A "for this operation."), cmd=subcommand)
2970N/A
2970N/A if not pargs:
2970N/A usage(_("At least one publisher must be specified"),
2970N/A cmd=subcommand)
2970N/A
2970N/A inv_pfxs = []
2970N/A for pfx in pargs:
2970N/A if not misc.valid_pub_prefix(pfx):
2970N/A inv_pfxs.append(pfx)
2970N/A
2970N/A if inv_pfxs:
3158N/A error(_("Invalid publisher prefix(es):\n {0}").format(
3158N/A "\n ".join(inv_pfxs)), cmd=subcommand)
2970N/A return EXIT_OOPS
2970N/A
2970N/A repo = get_repo(conf, read_only=False, subcommand=subcommand)
2970N/A existing = repo.publishers & set(pargs)
2970N/A noexisting = [pfx for pfx in pargs
2970N/A if pfx not in repo.publishers]
2970N/A # Publishers left if remove succeeds.
2970N/A left = [pfx for pfx in repo.publishers if pfx not in pargs]
2970N/A
2970N/A if noexisting:
3158N/A error(_("The following publisher(s) could not be found:\n "
3158N/A "{0}").format("\n ".join(noexisting)), cmd=subcommand)
2970N/A return EXIT_OOPS
2970N/A
2970N/A logger.info(_("Removing publisher(s)"))
2970N/A for pfx in existing:
2970N/A rstore = repo.get_pub_rstore(pfx)
2970N/A numpkg = rstore.catalog.package_count
3158N/A logger.info(_("\'{pfx}\'\t({num} package(s))").format(
3158N/A pfx=pfx, num=str(numpkg)))
2970N/A
2970N/A if dry_run:
2970N/A return EXIT_OK
2970N/A
2970N/A defaultpfx = repo.cfg.get_property("publisher", "prefix")
2970N/A repo_path = repo_uri.get_pathname()
2970N/A
2970N/A repo.remove_publisher(existing, repo_path, synch)
2970N/A # Change the repository publisher/prefix property, if necessary.
2970N/A if defaultpfx in existing:
2970N/A if len(left) == 1:
2970N/A _set_repo(conf, subcommand, { "publisher" : {
2970N/A "prefix" : left[0]} }, repo)
2970N/A msg(_("The default publisher was removed."
3158N/A " Setting 'publisher/prefix' to '{0}',"
3158N/A " the only publisher left").format(left[0]))
2970N/A else:
2970N/A _set_repo(conf, subcommand, { "publisher": {
2970N/A "prefix" : ""} }, repo)
2970N/A msg(_("The default publisher was removed."
2970N/A " The 'publisher/prefix' property has been"
2970N/A " unset"))
2970N/A
2970N/A return EXIT_OK
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()
2864N/A key = None
2864N/A cert = None
1968N/A
2864N/A opts, pargs = getopt.getopt(args, "F:Hp:s:", ["key=", "cert="])
1968N/A for opt, arg in opts:
1968N/A if opt == "-F":
3031N/A if arg not in LISTING_FORMATS:
3031N/A raise apx.InvalidOptionError(
3031N/A apx.InvalidOptionError.ARG_INVALID,
3031N/A [arg, opt])
1968N/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)
2864N/A elif opt == "--key":
2864N/A key = arg
2864N/A elif opt == "--cert":
2864N/A cert = 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)
3210N/A xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
3210N/A subcommand=subcommand, ssl_key=key, ssl_cert=cert)
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.
3158N/A def_fmt = "{0:" + str(max_sname_len) + "} {1:" + str(max_pname_len) + \
3158N/A "} {2}"
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",
3210N/A use_transport=False, repo_uri=None):
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.
3210N/A err_msg = _("no matching publishers found")
3210N/A if repo_uri:
3210N/A err_msg = _("no matching publishers found in "
3210N/A "repository: {0}").format(repo_uri)
3210N/A error(err_msg, 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.
3158N/A def_fmt = "{0:" + str(max_pubname_len) + "} {1:" + str(max_sname_len) + \
3158N/A "} {2:" + str(max_pname_len) + "} {3}"
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()
2864N/A key = None
2864N/A cert = None
1968N/A
2864N/A opts, pargs = getopt.getopt(args, "F:Hp:s:", ["key=", "cert="])
1968N/A for opt, arg in opts:
1968N/A if opt == "-F":
2028N/A if arg not in LISTING_FORMATS:
3031N/A raise apx.InvalidOptionError(
3031N/A apx.InvalidOptionError.ARG_INVALID,
3031N/A [arg, opt])
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)
2864N/A elif opt == "--key":
2864N/A key = arg
2864N/A elif opt == "--cert":
2864N/A cert = 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)
3210N/A xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
3210N/A subcommand=subcommand, ssl_key=key, ssl_cert=cert)
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():
3339N/A for pfx in sorted(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)
3158N/A last_update = "{0}Z".format(
3158N/A pkg.catalog.datetime_to_ts(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 ))
3158N/A def_fmt = "{0:" + pub_len + "} {1:8} {2:16} {3}"
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()
2864N/A key = None
2864N/A cert = None
2510N/A
2864N/A opts, pargs = getopt.getopt(args, "F:Hp:s:", ["key=", "cert="])
2510N/A for opt, arg in opts:
2510N/A if opt == "-F":
3031N/A if arg not in LISTING_FORMATS:
3031N/A raise apx.InvalidOptionError(
3031N/A apx.InvalidOptionError.ARG_INVALID,
3031N/A [arg, opt])
2510N/A out_format = arg
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)
2864N/A elif opt == "--key":
2864N/A key = arg
2864N/A elif opt == "--cert":
2864N/A cert = arg
2864N/A
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)
3210N/A xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
3210N/A subcommand=subcommand, ssl_key=key, ssl_cert=cert)
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
3041N/A refresh_pub(pub_data, xport)
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")
3339N/A for pub in sorted(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":
2616N/A if pkgdefs.PKG_STATE_OBSOLETE in \
2616N/A states:
2510N/A state = "o"
2616N/A elif pkgdefs.PKG_STATE_RENAMED in \
2616N/A states:
2510N/A state = "r"
2510N/A
2958N/A if out_format == "default":
2958N/A fver = str(f.version.get_version(
2958N/A include_build=False))
2958N/A ffmri = str(f.get_fmri(include_build=False))
2958N/A else:
2958N/A fver = str(f.version)
2958N/A ffmri = str(f)
2958N/A
2510N/A ret = {
2510N/A "publisher": f.publisher,
2510N/A "name": f.pkg_name,
2958N/A "version": fver,
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),
2958N/A "pkg.fmri": ffmri,
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"))]))
3158N/A def_fmt = "{0:" + max_pub_name_len + "} {1:45} {2:1} {3}"
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
3041N/Adef refresh_pub(pub_data, xport):
3041N/A """A helper function to refresh all specified publishers."""
3041N/A
3041N/A global tmpdirs
3041N/A temp_root = misc.config_temp_root()
3041N/A progtrack = get_tracker()
3041N/A progtrack.set_purpose(progtrack.PURPOSE_LISTING)
3041N/A
3041N/A progtrack.refresh_start(pub_cnt=len(pub_data), full_refresh=True,
3041N/A target_catalog=False)
3041N/A
3041N/A for pub in pub_data:
3041N/A progtrack.refresh_start_pub(pub)
3041N/A meta_root = tempfile.mkdtemp(dir=temp_root)
3041N/A tmpdirs.append(meta_root)
3041N/A pub.meta_root = meta_root
3041N/A pub.transport = xport
3041N/A
3041N/A try:
3041N/A pub.refresh(True, True, progtrack=progtrack)
3041N/A except apx.TransportError:
3041N/A # Assume that a catalog doesn't exist for the target
3041N/A # publisher and drive on.
3041N/A pass
3041N/A progtrack.refresh_end_pub(pub)
3041N/A
3041N/A progtrack.refresh_done()
3041N/A
3041N/A
3041N/Adef subcmd_contents(conf, args):
3041N/A """List package contents."""
3041N/A
3245N/A subcommand = "contents"
3245N/A display_raw = False
3245N/A pubs = set()
3245N/A key = None
3245N/A cert = None
3041N/A attrs = []
3041N/A action_types = []
3041N/A
3245N/A opts, pargs = getopt.getopt(args, "ms:t:", ["key=", "cert="])
3245N/A for opt, arg in opts:
3245N/A if opt == "-s":
3041N/A conf["repo_uri"] = parse_uri(arg)
3245N/A elif opt == "-m":
3245N/A display_raw = True
3245N/A elif opt == "-t":
3041N/A action_types.extend(arg.split(","))
3245N/A elif opt == "--key":
3245N/A key = arg
3245N/A elif opt == "--cert":
3245N/A cert = arg
3041N/A
3041N/A # Setup transport so configuration can be retrieved.
3041N/A if not conf.get("repo_uri", None):
3041N/A usage(_("A package repository location must be provided "
3041N/A "using -s."), cmd=subcommand)
3041N/A
3210N/A xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
3210N/A subcommand=subcommand, ssl_key=key, ssl_cert=cert)
3041N/A
3041N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
3041N/A xpub, use_transport=True)
3041N/A if rval == EXIT_OOPS:
3041N/A return rval
3041N/A
3245N/A # Default output prints out the raw manifest. The -m option is implicit
3138N/A # for now and supported to make the interface equivalent to pkg
3138N/A # contents.
3041N/A if not attrs or display_raw:
3041N/A attrs = ["action.raw"]
3041N/A
3041N/A refresh_pub(pub_data, xport)
3041N/A listed = False
3041N/A matched = set()
3041N/A unmatched = set()
3041N/A manifests = []
3041N/A
3041N/A for pub in pub_data:
3041N/A cat = pub.catalog
3041N/A for f, states, attr in cat.gen_packages(matched=matched,
3041N/A patterns=pargs, pubs=[pub.prefix],
3041N/A unmatched=unmatched, return_fmris=True):
3041N/A if not listed:
3041N/A listed = True
3041N/A manifests.append(xport.get_manifest(f))
3041N/A unmatched.difference_update(matched)
3041N/A
3041N/A # Build a generator expression based on whether specific action types
3041N/A # were provided.
3041N/A if action_types:
3041N/A # If query is limited to specific action types, use the more
3041N/A # efficient type-based generation mechanism.
3041N/A gen_expr = (
3041N/A (m.fmri, a, None, None, None)
3041N/A for m in manifests
3041N/A for a in m.gen_actions_by_types(action_types)
3041N/A )
3041N/A else:
3041N/A gen_expr = (
3041N/A (m.fmri, a, None, None, None)
3041N/A for m in manifests
3041N/A for a in m.gen_actions()
3041N/A )
3041N/A
3041N/A # Determine if the query returned any results by "peeking" at the first
3041N/A # value returned from the generator expression.
3041N/A try:
3234N/A got = next(gen_expr)
3041N/A except StopIteration:
3041N/A got = None
3041N/A actionlist = []
3041N/A
3041N/A if got:
3041N/A actionlist = itertools.chain([got], gen_expr)
3041N/A
3041N/A rval = EXIT_OK
3041N/A if action_types and manifests and not got:
3041N/A logger.error(_(gettext.ngettext("""\
3041N/Apkgrepo: contents: This package contains no actions with the types specified
3041N/Ausing the -t option""", """\
3041N/Apkgrepo: contents: These packages contain no actions with the types specified
3041N/Ausing the -t option.""", len(pargs))))
3041N/A rval = EXIT_OOPS
3041N/A
3041N/A if manifests and rval == EXIT_OK:
3041N/A lines = misc.list_actions_by_attrs(actionlist, attrs)
3041N/A for line in lines:
3158N/A text = ("{0}".format(*line)).rstrip()
3041N/A if not text:
3041N/A continue
3041N/A msg(text)
3041N/A
3041N/A if unmatched:
3041N/A if manifests:
3041N/A logger.error("")
3041N/A logger.error(_("""\
3041N/Apkgrepo: contents: no packages matching the following patterns you specified
3041N/Awere found in the repository."""))
3041N/A logger.error("")
3041N/A for p in unmatched:
3158N/A logger.error(" {0}".format(p))
3041N/A rval = EXIT_OOPS
3041N/A
3041N/A return rval
3041N/A
3041N/A
2990N/Adef __rebuild_local(subcommand, conf, pubs, build_catalog, build_index):
2990N/A """In an attempt to allow operations on potentially corrupt
2990N/A repositories, 'local' repositories (filesystem-basd ones) are handled
2990N/A separately."""
2990N/A
2990N/A repo = get_repo(conf, allow_invalid=build_catalog, read_only=False,
2990N/A subcommand=subcommand)
2990N/A
2990N/A rpubs = set(repo.publishers)
2990N/A if not pubs:
2990N/A found = rpubs
2990N/A else:
2990N/A found = rpubs & pubs
2990N/A notfound = pubs - found
2990N/A
2990N/A rval = EXIT_OK
2990N/A if found and notfound:
2990N/A rval = EXIT_PARTIAL
2990N/A elif pubs and not found:
2990N/A error(_("no matching publishers found"), cmd=subcommand)
2990N/A return EXIT_OOPS
2990N/A
2990N/A logger.info("Initiating repository rebuild.")
2990N/A for pfx in found:
2990N/A repo.rebuild(build_catalog=build_catalog,
2990N/A build_index=build_index, pub=pfx)
2990N/A
2990N/A return rval
2990N/A
2990N/A
2990N/Adef __rebuild_remote(subcommand, conf, pubs, key, cert, build_catalog,
2990N/A build_index):
2990N/A def do_rebuild(xport, xpub):
2990N/A if build_catalog and build_index:
2990N/A xport.publish_rebuild(xpub)
2990N/A elif build_catalog:
2990N/A xport.publish_rebuild_packages(xpub)
2990N/A elif build_index:
2990N/A xport.publish_rebuild_indexes(xpub)
2990N/A
3210N/A xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
3210N/A subcommand=subcommand, ssl_key=key, ssl_cert=cert)
2990N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2990N/A xpub)
2990N/A if rval == EXIT_OOPS:
2990N/A return rval
2990N/A
2990N/A logger.info("Initiating repository rebuild.")
2990N/A for pfx in found:
2990N/A xpub.prefix = pfx
2990N/A do_rebuild(xport, xpub)
2990N/A
2990N/A return rval
2990N/A
2990N/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
2864N/A key = None
2864N/A cert = None
2028N/A
2864N/A opts, pargs = getopt.getopt(args, "p:s:", ["no-catalog", "no-index",
2864N/A "key=", "cert="])
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):
3158N/A error(_("Invalid publisher prefix '{0}'").format(
3158N/A arg), 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
2864N/A elif opt == "--key":
2864N/A key = arg
2864N/A elif opt == "--cert":
2864N/A cert = arg
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
2990N/A if conf["repo_uri"].scheme == "file":
2990N/A return __rebuild_local(subcommand, conf, pubs, build_catalog,
2990N/A build_index)
2028N/A
2990N/A return __rebuild_remote(subcommand, conf, pubs, key, cert,
2990N/A build_catalog, build_index)
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
2864N/A key = None
2864N/A cert = None
2028N/A
2864N/A opts, pargs = getopt.getopt(args, "p:s:", ["no-catalog", "no-index",
2864N/A "key=", "cert="])
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):
3158N/A error(_("Invalid publisher prefix '{0}'").format(
3158N/A arg), 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
2864N/A elif opt == "--key":
2864N/A key = arg
2864N/A elif opt == "--cert":
2864N/A cert = arg
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
3210N/A xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
3210N/A subcommand=subcommand, ssl_key=key, ssl_cert=cert)
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
3234N/A for sname, sprops in six.iteritems(props):
2028N/A if sname not in ("publisher", "repository"):
2028N/A usage(_("unknown property section "
3158N/A "'{0}'").format(sname), cmd=subcommand)
2028N/A for pname in sprops:
2073N/A if sname == "publisher" and pname == "prefix":
3158N/A usage(_("'{0}' may not be set using "
3158N/A "this command".format(pname)))
2028N/A attrname = pname.replace("-", "_")
2028N/A if not hasattr(publisher.Publisher, attrname) and \
2028N/A not hasattr(publisher.Repository, attrname):
3158N/A usage(_("unknown property '{0}'").format(
3158N/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))
3171N/A except sr.RepositoryUnknownPublisher as e:
2028N/A pub = publisher.Publisher(pfx)
2028N/A new_pub = True
3171N/A except sr.RepositoryError as e:
2028N/A failed.append((pfx, e))
2028N/A continue
2028N/A
2028N/A try:
2028N/A # Set/update the publisher's properties.
3234N/A for sname, sprops in six.iteritems(props):
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
3234N/A for pname, val in six.iteritems(sprops):
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)
3171N/A except apx.ApiException as 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 "
3158N/A "'{pfx}':\n{details}").format(**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.
3234N/A for sname, props in six.iteritems(props):
3234N/A for pname, val in six.iteritems(props):
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):
3356N/A """Display the version of the pkg(7) 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
2843N/Averify_error_header = None
2843N/Averify_warning_header = None
2843N/Averify_reason_headers = None
2843N/A
2843N/Adef __load_verify_msgs():
2843N/A """Since our gettext isn't loaded we need to ensure our globals have
2843N/A correct content by calling this method. These values are used by both
2843N/A fix when in verbose mode, and verify"""
2843N/A
2843N/A global verify_error_header
2843N/A global verify_warning_header
2843N/A global verify_reason_headers
2843N/A
2843N/A # A map of error detail types to the human-readable description of each
2843N/A # type. These correspond to keys in the dictionary returned by
2843N/A # sr.Repository.verify(..)
2843N/A verify_reason_headers = {
2843N/A "path": _("Repository path"),
2843N/A "actual": _("Computed hash"),
2843N/A "fpath": _("Path"),
2843N/A "permissionspath": _("Path"),
2843N/A "pkg": _("Package"),
3138N/A "depend": _("Dependency"),
3138N/A "type":_("Dependency type"),
2843N/A "err": _("Detail")
2843N/A }
2843N/A
2843N/A verify_error_header = _("ERROR")
2843N/A verify_warning_header = _("WARNING")
2843N/A
2843N/A
2843N/Adef __fmt_verify(verify_tuple):
2843N/A """Format a verify_tuple, of the form (error, path, message, reason)
2843N/A returning a formatted error message, and an FMRI indicating what
2843N/A packages within the repository are affected. Note that the returned FMRI
2843N/A may not be valid, in which case a path to the broken manifest in the
2843N/A repository is returned instead."""
2843N/A
2843N/A error, path, message, reason = verify_tuple
2843N/A
3158N/A formatted_message = "{error_type:>16}: {message}\n".format(
3158N/A error_type=verify_error_header, message=message)
2843N/A reason["path"] = path
2843N/A
2843N/A if error == sr.REPO_VERIFY_BADMANIFEST:
2843N/A reason_keys = ["path", "err"]
2843N/A elif error in [sr.REPO_VERIFY_PERM, sr.REPO_VERIFY_MFPERM]:
2843N/A reason_keys = ["pkg", "path"]
2843N/A elif error == sr.REPO_VERIFY_BADHASH:
2843N/A reason_keys = ["pkg", "path", "actual", "fpath"]
2843N/A elif error == sr.REPO_VERIFY_UNKNOWN:
2843N/A reason_keys = ["path", "err"]
2843N/A elif error == sr.REPO_VERIFY_BADSIG:
2843N/A reason_keys = ["pkg", "path", "err"]
3138N/A elif error == sr.REPO_VERIFY_DEPENDERROR:
3138N/A reason_keys = ["pkg", "depend", "type"]
2843N/A elif error == sr.REPO_VERIFY_WARN_OPENPERMS:
2843N/A formatted_message = \
3158N/A "{error_type:>16}: {message}\n".format(
3158N/A error_type=verify_warning_header, message=message)
2843N/A reason_keys = ["permissionspath", "err"]
2843N/A else:
2843N/A # A list of the details we provide. Some error codes
2843N/A # have different details associated with them.
2843N/A reason_keys = ["pkg", "path", "fpath"]
2843N/A
2843N/A
2843N/A # the detailed error message can be long, so we'll wrap it. If what we
2843N/A # have fits on a single line, use it, otherwise begin displaying the
2843N/A # message on the next line.
2843N/A if "err" in reason_keys:
2843N/A err_str = ""
2843N/A lines = textwrap.wrap(reason["err"])
2843N/A if len(lines) != 1:
2843N/A for line in lines:
3158N/A err_str += "{0:>18}\n".format(line)
2843N/A reason["err"] = "\n" + err_str.rstrip()
2843N/A else:
2843N/A reason["err"] = lines[0]
2843N/A
2843N/A for key in reason_keys:
2843N/A # sometimes we don't have the key we want, for example we may
2843N/A # not have a file path from the package if the error is a
2843N/A # missing repository file for a 'license' action (which don't
2843N/A # have 'path' attributes, hence no 'fpath' dictionary entry)
2843N/A if key not in reason:
2843N/A continue
3158N/A formatted_message += "{key:>16}: {value}\n".format(
3158N/A key=verify_reason_headers[key], value=reason[key])
2843N/A
2843N/A formatted_message += "\n"
2843N/A
2843N/A if error == sr.REPO_VERIFY_WARN_OPENPERMS:
2843N/A return formatted_message, None
3138N/A elif "depend" in reason:
3138N/A return formatted_message, reason["depend"]
2843N/A elif "pkg" in reason:
2843N/A return formatted_message, reason["pkg"]
2843N/A return formatted_message, reason["path"]
2843N/A
2843N/A
3138N/Adef __collect_default_ignore_dep_files(ignored_dep_files):
3138N/A """Helpler function to collect default ignored-dependency files."""
3138N/A
3138N/A root_ignored = "/usr/share/pkg/ignored_deps"
3138N/A altroot = DebugValues.get_value("ignored_deps")
3138N/A if altroot:
3138N/A root_ignored = altroot
3138N/A if os.path.exists(root_ignored):
3138N/A igfiles = os.listdir(root_ignored)
3138N/A for igf in igfiles:
3138N/A ignored_dep_files.append(os.path.join(root_ignored,
3138N/A igf))
3138N/A
3138N/A
2843N/Adef subcmd_verify(conf, args):
3138N/A """Verify the repository content (file, manifest content and
3138N/A dependencies only)."""
2843N/A
2843N/A subcommand = "verify"
2843N/A __load_verify_msgs()
2843N/A
3138N/A opts, pargs = getopt.getopt(args, "dp:s:i:", ["disable="])
3140N/A allowed_checks = set(sr.verify_default_checks)
3138N/A force_dep_check = False
3138N/A ignored_dep_files = []
2843N/A pubs = set()
2843N/A for opt, arg in opts:
2843N/A if opt == "-s":
2843N/A conf["repo_uri"] = parse_uri(arg)
3138N/A elif opt == "-p":
2843N/A if not misc.valid_pub_prefix(arg):
3158N/A error(_("Invalid publisher prefix '{0}'").format(
3158N/A arg), cmd=subcommand)
2843N/A pubs.add(arg)
3138N/A elif opt == "-d":
3138N/A force_dep_check = True
3138N/A elif opt == "--disable":
3138N/A arg = arg.lower()
3140N/A if arg in sr.verify_default_checks:
3138N/A if arg in allowed_checks:
3138N/A allowed_checks.remove(arg)
3138N/A else:
3138N/A usage(_("Invalid verification to be disabled, "
3158N/A "please consider: {0}").format(", ".join(
3158N/A sr.verify_default_checks)), cmd=subcommand)
3138N/A elif opt == "-i":
3138N/A ignored_dep_files.append(arg)
2843N/A
2843N/A if pargs:
2843N/A usage(_("command does not take operands"), cmd=subcommand)
2843N/A
2843N/A repo_uri = conf.get("repo_uri", None)
2843N/A if not repo_uri:
2843N/A usage(_("A package repository location must be provided "
2843N/A "using -s."), cmd=subcommand)
2843N/A
2843N/A if repo_uri.scheme != "file":
2843N/A usage(_("Network repositories are not currently supported "
2843N/A "for this operation."), cmd=subcommand)
2843N/A
3140N/A if sr.VERIFY_DEPENDENCY not in allowed_checks and \
3138N/A (force_dep_check or len(ignored_dep_files) > 0):
3138N/A usage(_("-d or -i option cannot be used when dependency "
3138N/A "verification is disabled."), cmd=subcommand)
3138N/A
3210N/A xport, xpub, tmp_dir = setup_transport(repo_uri, subcommand=subcommand)
2843N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2843N/A xpub)
3140N/A
2843N/A if rval == EXIT_OOPS:
2843N/A return rval
2843N/A
2843N/A logger.info("Initiating repository verification.")
2843N/A bad_fmris = set()
3138N/A progtrack = get_tracker()
3138N/A
3138N/A def report_error(verify_tuple):
3138N/A message, bad_fmri = __fmt_verify(verify_tuple)
3138N/A if bad_fmri:
3138N/A bad_fmris.add(bad_fmri)
3138N/A progtrack.repo_verify_yield_error(bad_fmri, message)
3138N/A
3140N/A if sr.VERIFY_DEPENDENCY in allowed_checks or not force_dep_check:
3138N/A __collect_default_ignore_dep_files(ignored_dep_files)
3138N/A
3138N/A repo = sr.Repository(root=repo_uri.get_pathname())
3140N/A
3140N/A found_pubs = []
3140N/A for pfx in found:
3210N/A xport, xpub, tmp_dir = setup_transport(repo_uri, prefix=pfx,
3140N/A remote_prefix=False,
3140N/A subcommand=subcommand)
3140N/A xpub.transport = xport
3140N/A found_pubs.append(xpub)
3140N/A
3140N/A for verify_tuple in repo.verify(pubs=found_pubs,
3138N/A allowed_checks=allowed_checks, force_dep_check=force_dep_check,
3138N/A ignored_dep_files=ignored_dep_files, progtrack=progtrack):
3138N/A report_error(verify_tuple)
3138N/A
2843N/A if bad_fmris:
2843N/A return EXIT_OOPS
2843N/A return EXIT_OK
2843N/A
2843N/A
2843N/Adef subcmd_fix(conf, args):
2843N/A """Fix the repository content (file and manifest content only)
2843N/A For index and catalog content corruption, a rebuild should be
2843N/A performed."""
2843N/A
2843N/A subcommand = "fix"
2843N/A __load_verify_msgs()
2843N/A verbose = False
2843N/A
3138N/A # Dependency verification. Note fix will not force dependency check.
3138N/A force_dep_check = False
3138N/A ignored_dep_files = []
3138N/A
2843N/A opts, pargs = getopt.getopt(args, "vp:s:")
2843N/A pubs = set()
2843N/A for opt, arg in opts:
2843N/A if opt == "-s":
2843N/A conf["repo_uri"] = parse_uri(arg)
2843N/A if opt == "-v":
2843N/A verbose = True
2843N/A if opt == "-p":
2843N/A if not misc.valid_pub_prefix(arg):
3158N/A error(_("Invalid publisher prefix '{0}'").format(
3158N/A arg), cmd=subcommand)
2843N/A pubs.add(arg)
2843N/A
2843N/A if pargs:
2843N/A usage(_("command does not take operands"), cmd=subcommand)
2843N/A
2843N/A repo_uri = conf.get("repo_uri", None)
2843N/A if not repo_uri:
2843N/A usage(_("A package repository location must be provided "
2843N/A "using -s."), cmd=subcommand)
2843N/A
2843N/A if repo_uri.scheme != "file":
2843N/A usage(_("Network repositories are not currently supported "
2843N/A "for this operation."), cmd=subcommand)
2843N/A
3210N/A xport, xpub, tmp_dir = setup_transport(repo_uri, subcommand=subcommand)
2843N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
2843N/A xpub)
2843N/A if rval == EXIT_OOPS:
2843N/A return rval
2843N/A
2843N/A logger.info("Initiating repository fix.")
2843N/A
2843N/A def verify_cb(tracker, verify_tuple):
2843N/A """A method passed to sr.Repository.fix(..) to emit verify
2843N/A messages if verbose mode is enabled."""
2843N/A if not verbose:
2843N/A return
2843N/A formatted_message, bad_fmri = __fmt_verify(verify_tuple)
2843N/A tracker.repo_verify_yield_error(bad_fmri, formatted_message)
2843N/A
2843N/A repo = sr.Repository(root=repo_uri.get_pathname())
3138N/A bad_deps = set()
2843N/A broken_fmris = set()
2843N/A failed_fix_paths = set()
3138N/A progtrack = get_tracker()
3138N/A __collect_default_ignore_dep_files(ignored_dep_files)
3138N/A
3140N/A found_pubs = []
3140N/A for pfx in found:
3210N/A xport, xpub, tmp_dir = setup_transport(repo_uri, prefix=pfx,
3140N/A remote_prefix=False,
3140N/A subcommand=subcommand)
3140N/A xpub.transport = xport
3140N/A found_pubs.append(xpub)
3140N/A
3138N/A for status_code, path, message, reason in \
3140N/A repo.fix(pubs=found_pubs, force_dep_check=force_dep_check,
3138N/A ignored_dep_files=ignored_dep_files,
3138N/A progtrack=progtrack,
3138N/A verify_callback=verify_cb):
3138N/A if status_code == sr.REPO_FIX_ITEM:
3138N/A # When we can't get the FMRI, eg. in the case
3138N/A # of a corrupt manifest, use the path instead.
3138N/A fmri = reason["pkg"]
3138N/A if not fmri:
3138N/A fmri = path
3138N/A broken_fmris.add(fmri)
3138N/A if verbose:
3138N/A progtrack.repo_fix_yield_info(fmri,
3138N/A message)
3138N/A elif status_code == sr.REPO_VERIFY_DEPENDERROR:
3138N/A bad_deps.add(reason["depend"])
3138N/A else:
3138N/A failed_fix_paths.add(path)
2843N/A
2843N/A progtrack.flush()
2843N/A logger.info("")
2843N/A
2843N/A if broken_fmris:
2843N/A logger.info(_("Use pkgsend(1) or pkgrecv(1) to republish the\n"
2843N/A "following packages or paths which were quarantined:\n\n\t"
3158N/A "{0}").format(
3158N/A "\n\t".join([str(f) for f in broken_fmris])))
2843N/A if failed_fix_paths:
2843N/A logger.info(_("\npkgrepo could not repair the following paths "
3158N/A "in the repository:\n\n\t{0}").format(
3158N/A "\n\t".join([p for p in failed_fix_paths])))
3138N/A if bad_deps:
3138N/A logger.info(_("\npkgrepo could not repair the following "
3158N/A "dependency issues in the repository:\n\n\t{0}").format(
3158N/A "\n\t".join([p for p in bad_deps])))
3138N/A if not (broken_fmris or failed_fix_paths or bad_deps):
2843N/A logger.info(_("No repository fixes required."))
2843N/A else:
2843N/A logger.info(_("Repository repairs completed."))
2843N/A
3138N/A if failed_fix_paths or bad_deps:
2843N/A return EXIT_OOPS
2843N/A return EXIT_OK
2843N/A
3210N/Adef __get_pub_fmris(pub, xport, tmp_dir):
3210N/A if not pub.meta_root:
3210N/A # Create a temporary directory for catalog.
3210N/A cat_dir = tempfile.mkdtemp(prefix="pkgrepo-diff.", dir=tmp_dir)
3210N/A pub.meta_root = cat_dir
3210N/A pub.transport = xport
3210N/A pub.refresh(full_refresh=True, immediate=True)
3210N/A
3210N/A pkgs, fmris, unmatched = pub.catalog.get_matching_fmris("*")
3210N/A fmris = [f for f in fmris]
3210N/A return fmris, pkgs
3210N/A
3210N/Adef __format_diff(diff_type, subject):
3210N/A """formatting diff output.
3210N/A diff_type: can be MINUS, PLUS or COMMON.
3210N/A
3210N/A subject: can be a publisher or a package.
3210N/A """
3210N/A
3210N/A format_pub = "{0}{1}"
3210N/A format_fmri = " {0}{1}"
3210N/A format_str = " {0}{1}"
3210N/A text = ""
3210N/A if isinstance(subject, publisher.Publisher):
3210N/A text = format_pub.format(diff_type_f[diff_type],
3210N/A subject.prefix)
3210N/A elif isinstance(subject, fmri.PkgFmri):
3210N/A text = format_fmri.format(diff_type_f[diff_type],
3210N/A str(subject))
3210N/A else:
3210N/A text = format_str.format(diff_type_f[diff_type],
3210N/A subject)
3210N/A return text
3210N/A
3210N/Adef __sorted(subject, stype=None):
3210N/A if stype == "pub":
3210N/A skey = operator.attrgetter("prefix")
3210N/A return sorted(subject, key=skey)
3210N/A return sorted(subject)
3210N/A
3210N/Adef __emit_msg(diff_type, subject):
3210N/A text = __format_diff(diff_type, subject)
3210N/A msg(text)
3210N/A
3210N/Adef __repo_diff(conf, pubs, xport, rpubs, rxport, tmp_dir, verbose, quiet,
3210N/A compare_ts, compare_cat, parsable):
3210N/A """Determine the differences between two repositories."""
3210N/A
3210N/A same_repo = True
3210N/A if conf["repo_uri"].scheme == "file":
3210N/A conf["repo_uri"] = conf["repo_uri"].get_pathname()
3210N/A if conf["com_repo_uri"].scheme == "file":
3210N/A conf["com_repo_uri"] = conf["com_repo_uri"].get_pathname()
3210N/A
3210N/A foundpfx = set([pub.prefix for pub in pubs])
3210N/A rfoundpfx = set([pub.prefix for pub in rpubs])
3210N/A
3210N/A minus_pfx = __sorted(foundpfx - rfoundpfx)
3210N/A minus_pubs = __sorted([pub for pub in pubs if pub.prefix in minus_pfx],
3210N/A stype="pub")
3210N/A plus_pfx = __sorted(rfoundpfx - foundpfx)
3210N/A plus_pubs = __sorted([pub for pub in rpubs if pub.prefix in plus_pfx],
3210N/A stype="pub")
3210N/A
3210N/A if minus_pubs or plus_pubs:
3210N/A same_repo = False
3210N/A if quiet:
3210N/A return EXIT_DIFF
3210N/A
3210N/A pcommon_set = foundpfx & rfoundpfx
3210N/A common_pubs = __sorted([p for p in pubs if p.prefix in pcommon_set],
3210N/A stype="pub")
3210N/A common_rpubs = __sorted([p for p in rpubs if p.prefix in pcommon_set],
3210N/A stype="pub")
3210N/A
3210N/A res_dict = {"table_legend": [["Repo1", str(conf["repo_uri"])],
3210N/A ["Repo2", str(conf["com_repo_uri"])]],
3210N/A "table_header": [_("Publisher"),
3210N/A # This is a table column header which tells that this
3210N/A # row shows number of packages found in specific
3210N/A # repository only.
3210N/A # Use terse translation to avoid too-wide header.
3210N/A _("{repo} only").format(repo="Repo1"),
3210N/A _("{repo} only").format(repo="Repo2"),
3210N/A # This is a table column header which tells that this
3210N/A # row shows number of packages found in both
3210N/A # repositories being compared together.
3210N/A # Use terse translation to avoid too-wide header.
3210N/A _("In both"), _("Total")],
3210N/A # Row based table contents.
3210N/A "table_data": []
3210N/A }
3210N/A
3210N/A verbose_res_dict = {"plus_pubs": [], "minus_pubs": [],
3210N/A "common_pubs": []}
3210N/A
3210N/A def __diff_pub_helper(pub, symbol):
3210N/A fmris, pkgs = __get_pub_fmris(pub, xport, tmp_dir)
3210N/A # Summary level.
3210N/A if not verbose:
3210N/A td_row = [pub.prefix,
3210N/A {"packages": len(pkgs), "versions": len(fmris)},
3210N/A None, {"packages": 0, "versions": 0},
3210N/A {"packages": len(pkgs), "versions": len(fmris)}]
3210N/A if symbol == PLUS:
3210N/A td_row[1], td_row[2] = td_row[2], td_row[1]
3210N/A res_dict["table_data"].append(td_row)
3210N/A return
3210N/A
3210N/A if parsable:
3210N/A key_name = "minus_pubs"
3210N/A if symbol == PLUS:
3210N/A key_name = "plus_pubs"
3210N/A verbose_res_dict[key_name].append(
3210N/A {"publisher": pub.prefix, "packages": len(pkgs),
3210N/A "versions": len(fmris)})
3210N/A return
3210N/A
3210N/A __emit_msg(symbol, pub)
3210N/A __emit_msg(symbol, _("({0:d} package(s) with "
3210N/A "{1:d} different version(s))").format(len(pkgs),
3210N/A len(fmris)))
3210N/A
3210N/A for pub in minus_pubs:
3210N/A __diff_pub_helper(pub, MINUS)
3210N/A
3210N/A for pub in plus_pubs:
3210N/A __diff_pub_helper(pub, PLUS)
3210N/A
3210N/A for pub, rpub in zip(common_pubs, common_rpubs):
3210N/A # Indicates whether those two pubs have same pkgs.
3210N/A same_pkgs = True
3210N/A same_cat = True
3210N/A fmris, pkgs = __get_pub_fmris(pub, xport, tmp_dir)
3210N/A rfmris, rpkgs = __get_pub_fmris(rpub, rxport, tmp_dir)
3210N/A fmris_str = set([str(f) for f in fmris])
3210N/A rfmris_str = set([str(f) for f in rfmris])
3210N/A del fmris, rfmris
3210N/A
3210N/A minus_fmris = __sorted(fmris_str - rfmris_str)
3210N/A plus_fmris = __sorted(rfmris_str - fmris_str)
3210N/A if minus_fmris or plus_fmris:
3210N/A same_repo = False
3210N/A same_pkgs = False
3210N/A if quiet:
3210N/A return EXIT_DIFF
3210N/A
3210N/A cat_lm_pub = None
3210N/A cat_lm_rpub = None
3210N/A if compare_cat:
3210N/A cat_lm_pub = pub.catalog.last_modified.isoformat()
3210N/A cat_lm_rpub = rpub.catalog.last_modified.isoformat()
3210N/A same_cat = same_repo = cat_lm_pub == cat_lm_rpub
3210N/A if not same_cat and quiet:
3210N/A return EXIT_DIFF
3210N/A
3210N/A common_fmris = fmris_str & rfmris_str
3210N/A pkg_set = set(pkgs.keys())
3210N/A rpkg_set = set(rpkgs.keys())
3210N/A del pkgs, rpkgs
3210N/A common_pkgs = pkg_set & rpkg_set
3210N/A
3210N/A # Print summary.
3210N/A if not verbose:
3210N/A if not same_cat:
3210N/A # Common publishers with different catalog
3210N/A # modification time.
3210N/A res_dict.setdefault("nonstrict_pubs", []
3210N/A ).append(pub.prefix)
3210N/A
3210N/A # Add to the table only if there are differences
3210N/A # for this publisher.
3210N/A if not same_pkgs:
3210N/A minus_pkgs = pkg_set - rpkg_set
3210N/A minus_pkg_vers = {"packages": len(minus_pkgs),
3210N/A "versions": len(minus_fmris)}
3210N/A del minus_pkgs, minus_fmris
3210N/A
3210N/A plus_pkgs = rpkg_set - pkg_set
3210N/A plus_pkg_vers = {"packages": len(plus_pkgs),
3210N/A "versions": len(plus_fmris)}
3210N/A del plus_pkgs, plus_fmris
3210N/A
3210N/A total_pkgs = pkg_set | rpkg_set
3210N/A total_fmris = fmris_str | rfmris_str
3210N/A total_pkg_vers = {"packages": len(total_pkgs),
3210N/A "versions": len(total_fmris)}
3210N/A del total_pkgs, total_fmris
3210N/A
3210N/A com_pkg_vers = {"packages": len(common_pkgs),
3210N/A "versions": len(common_fmris)}
3210N/A
3210N/A res_dict["table_data"].append([pub.prefix,
3210N/A minus_pkg_vers, plus_pkg_vers,
3210N/A com_pkg_vers,
3210N/A total_pkg_vers])
3210N/A del common_pkgs, common_fmris, pkg_set, rpkg_set
3210N/A continue
3210N/A
3210N/A com_pub_info = {}
3210N/A # Emit publisher name if there are differences.
3210N/A if not same_pkgs or not same_cat:
3210N/A if parsable:
3210N/A com_pub_info["publisher"] = pub.prefix
3210N/A com_pub_info["+"] = []
3210N/A com_pub_info["-"] = []
3210N/A else:
3210N/A __emit_msg(COMMON, pub)
3210N/A
3210N/A # Emit catalog differences.
3210N/A if not same_cat:
3210N/A omsg = _("catalog last modified: {0}")
3210N/A minus_cat = omsg.format(cat_lm_pub)
3210N/A plus_cat = omsg.format(cat_lm_rpub)
3210N/A if parsable:
3210N/A com_pub_info["catalog"] = {"-": minus_cat,
3210N/A "+": plus_cat}
3210N/A else:
3210N/A __emit_msg(MINUS, minus_cat)
3210N/A __emit_msg(PLUS, plus_cat)
3210N/A
3210N/A for f in minus_fmris:
3210N/A if parsable:
3210N/A com_pub_info["-"].append(str(f))
3210N/A else:
3210N/A __emit_msg(MINUS, f)
3210N/A del minus_fmris
3210N/A
3210N/A for f in plus_fmris:
3210N/A if parsable:
3210N/A com_pub_info["+"].append(str(f))
3210N/A else:
3210N/A __emit_msg(PLUS, f)
3210N/A del plus_fmris
3210N/A
3210N/A if not same_pkgs:
3210N/A if parsable:
3210N/A com_pub_info["common"] = {
3210N/A "packages": len(common_pkgs),
3210N/A "versions": len(common_fmris)}
3210N/A else:
3210N/A msg(_(" ({0:d} pkg(s) with {1:d} "
3210N/A "version(s) are in both repositories.)"
3210N/A ).format(len(common_pkgs),
3210N/A len(common_fmris)))
3210N/A del common_pkgs, common_fmris, pkg_set, rpkg_set
3210N/A
3210N/A if com_pub_info:
3210N/A verbose_res_dict["common_pubs"].append(com_pub_info)
3210N/A
3210N/A if same_repo:
3210N/A # Same repo. Will use EXIT_OK to represent.
3210N/A return EXIT_OK
3210N/A
3210N/A if verbose:
3210N/A if parsable:
3210N/A msg(json.dumps(verbose_res_dict))
3210N/A return EXIT_DIFF
3210N/A
3210N/A if not parsable:
3210N/A ftemp = "{0:d} [{1:{2}d}]"
3210N/A if "nonstrict_pubs" in res_dict and res_dict["nonstrict_pubs"]:
3210N/A msg("")
3210N/A msg(_("The catalog for the following publisher(s) "
3210N/A "in repository {0} is not an exact copy of the "
3210N/A "one for the same publisher in repository {1}:"
3210N/A "\n {2}").format(conf["repo_uri"],
3210N/A conf["com_repo_uri"],
3210N/A ", ".join(res_dict["nonstrict_pubs"])))
3210N/A if res_dict["table_data"]:
3210N/A info_table = PrettyTable(res_dict["table_header"],
3210N/A encoding=locale.getpreferredencoding())
3210N/A info_table.align = "r"
3339N/A info_table.align[misc.force_text(_("Publisher"),
3210N/A locale.getpreferredencoding())] = "l"
3210N/A # Calculate column wise maximum number for formatting.
3210N/A col_maxs = 4 * [0]
3210N/A for td in res_dict["table_data"]:
3210N/A for idx, cell in enumerate(td):
3210N/A if idx > 0 and isinstance(cell, dict):
3210N/A col_maxs[idx-1] = max(
3210N/A col_maxs[idx-1],
3210N/A cell["versions"])
3210N/A
3210N/A for td in res_dict["table_data"]:
3210N/A t_row = []
3210N/A for idx, cell in enumerate(td):
3210N/A if not cell:
3210N/A t_row.append("-")
3234N/A elif isinstance(cell, six.string_types):
3210N/A t_row.append(cell)
3210N/A elif isinstance(cell, dict):
3210N/A t_row.append(ftemp.format(
3210N/A cell["packages"],
3210N/A cell["versions"], len(str(
3210N/A col_maxs[idx-1]))))
3210N/A info_table.add_row(t_row)
3210N/A
3210N/A # This message explains that each cell of the table
3210N/A # shows two numbers in a format e.g. "4870 [10227]".
3210N/A # Here "number of packages" and "total distinct
3210N/A # versions" are shown outside and inside of square
3210N/A # brackets respectively.
3210N/A msg(_("""
3210N/AThe table below shows the number of packages [total distinct versions]
3210N/Aby publisher in the specified repositories.
3210N/A"""))
3210N/A for leg in res_dict["table_legend"]:
3210N/A msg("* " + leg[0] + ": " + leg[1])
3210N/A msg("")
3210N/A msg(info_table)
3210N/A else:
3210N/A msg(json.dumps(res_dict))
3210N/A
3210N/A return EXIT_DIFF
3210N/A
3210N/A
3210N/Adef subcmd_diff(conf, args):
3210N/A """Compare two repositories."""
3210N/A
3210N/A opts, pargs = getopt.getopt(args, "vqp:s:", ["strict", "parsable",
3210N/A "key=", "cert="])
3210N/A subcommand = "diff"
3210N/A pubs = set()
3210N/A verbose = 0
3210N/A quiet = False
3210N/A compare_ts = True
3210N/A compare_cat = False
3210N/A parsable = False
3210N/A
3210N/A def key_cert_conf_helper(conf_type, arg):
3210N/A """Helper function for collecting key and cert."""
3210N/A
3210N/A if conf.get("repo_uri") and not conf.get("com_repo_uri"):
3210N/A conf["repo_" + conf_type] = arg
3210N/A elif conf.get("com_repo_uri"):
3210N/A conf["com_repo_" + conf_type] = arg
3210N/A else:
3210N/A usage(_("--{0} must be specified following a "
3210N/A "-s").format(conf_type), cmd=subcommand)
3210N/A
3210N/A for opt, arg in opts:
3210N/A if opt == "-s":
3210N/A if "repo_uri" not in conf:
3210N/A conf["repo_uri"] = parse_uri(arg)
3210N/A elif "com_repo_uri" not in conf:
3210N/A conf["com_repo_uri"] = parse_uri(arg)
3210N/A else:
3210N/A usage(_("only two repositories can be "
3210N/A "specified"), cmd=subcommand)
3210N/A if opt == "-v":
3210N/A verbose += 1
3210N/A elif opt == "-q":
3210N/A quiet = True
3210N/A elif opt == "--strict":
3210N/A compare_cat = True
3210N/A elif opt == "--parsable":
3210N/A parsable = True
3210N/A elif opt == "-p":
3210N/A if not misc.valid_pub_prefix(arg):
3210N/A error(_("Invalid publisher prefix '{0}'").format(
3210N/A arg), cmd=subcommand)
3210N/A return EXIT_OOPS
3210N/A pubs.add(arg)
3210N/A elif opt == "--key":
3210N/A key_cert_conf_helper("key", arg)
3210N/A elif opt == "--cert":
3210N/A key_cert_conf_helper("cert", arg)
3210N/A
3210N/A if len(pargs) > 0:
3210N/A usage(_("command does not take any operands"), cmd=subcommand)
3210N/A
3210N/A if quiet and verbose:
3210N/A usage(_("-q and -v can not be combined"), cmd=subcommand)
3210N/A
3210N/A repo_uri = conf.get("repo_uri")
3210N/A if not repo_uri:
3210N/A usage(_("Two package repository locations must be provided "
3210N/A "using -s."), cmd=subcommand)
3210N/A
3210N/A com_repo_uri = conf.get("com_repo_uri")
3210N/A if not com_repo_uri:
3210N/A usage(_("A second package repository location must also be "
3210N/A "provided using -s."), cmd=subcommand)
3210N/A
3210N/A xport, xpub, tmp_dir = setup_transport(repo_uri, subcommand=subcommand,
3210N/A ssl_key=conf.get("repo_key"), ssl_cert=conf.get("repo_cert"))
3210N/A cxport, cxpub, c_tmp_dir = setup_transport(com_repo_uri,
3210N/A subcommand=subcommand, prefix="com",
3210N/A ssl_key=conf.get("com_repo_key"),
3210N/A ssl_cert=conf.get("com_repo_cert"))
3210N/A rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
3210N/A xpub, use_transport=True, repo_uri=repo_uri)
3210N/A if rval == EXIT_OOPS:
3210N/A return rval
3210N/A
3210N/A rval, cfound, cpub_data = _get_matching_pubs(subcommand, pubs, cxport,
3210N/A cxpub, use_transport=True, repo_uri=com_repo_uri)
3210N/A if rval == EXIT_OOPS:
3210N/A return rval
3210N/A
3210N/A return __repo_diff(conf, pub_data, xport, cpub_data, cxport, tmp_dir,
3210N/A verbose, quiet, compare_ts, compare_cat, parsable)
3210N/A
2843N/A
1968N/Adef main_func():
1968N/A global_settings.client_name = PKG_CLIENT_NAME
1968N/A
1968N/A try:
2864N/A opts, pargs = getopt.getopt(sys.argv[1:], "s:D:?",
2864N/A ["help", "debug="])
3171N/A except getopt.GetoptError as e:
3158N/A usage(_("illegal global option -- {0}").format(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
2864N/A elif opt == "-D" or opt == "--debug":
2864N/A try:
2864N/A key, value = arg.split("=", 1)
2864N/A except (AttributeError, ValueError):
3158N/A usage(_("{opt} takes argument of form "
3158N/A "name=value, not {arg}").format(
3158N/A opt=opt, arg=arg))
2864N/A DebugValues.set_value(key, value)
1968N/A
2962N/A if DebugValues:
2962N/A reload(pkg.digest)
2962N/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("-", "_")
3158N/A func = globals().get("subcmd_{0}".format(subcommand), None)
1968N/A if not func:
2028N/A subcommand = subcommand.replace("_", "-")
3158N/A usage(_("unknown subcommand '{0}'").format(subcommand))
1968N/A
1968N/A try:
1968N/A return func(conf, pargs)
3171N/A except getopt.GetoptError as e:
1968N/A if e.opt in ("help", "?"):
1968N/A usage(full=True)
3158N/A usage(_("illegal option -- {0}").format(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)
3171N/A except (MemoryError, EnvironmentError) as __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
3171N/A except SystemExit as __e:
1968N/A raise __e
3171N/A except (IOError, PipeError, KeyboardInterrupt) as __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
3171N/A except apx.VersionException as __e:
1968N/A error(_("The pkgrepo command appears out of sync with the "
1968N/A "libraries provided\nby pkg:/package/pkg. The client "
3158N/A "version is {client} while the library\nAPI version is "
3158N/A "{api}.").format(client=__e.received_version,
3158N/A api=__e.expected_version))
1968N/A __ret = EXIT_OOPS
3171N/A except apx.BadRepositoryURI as __e:
2028N/A error(str(__e))
2028N/A __ret = EXIT_BADOPT
3171N/A except apx.InvalidOptionError as __e:
3158N/A error("{0} Supported formats: {1}".format(
3158N/A str(__e), LISTING_FORMATS))
3031N/A __ret = EXIT_BADOPT
3171N/A except (apx.ApiException, sr.RepositoryError) as __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)
2728N/A gettext.install("pkg", "/usr/share/locale",
2728N/A codeset=locale.getpreferredencoding())
1968N/A
1968N/A # Make all warnings be errors.
1968N/A warnings.simplefilter('error')
3339N/A if six.PY3:
3339N/A # disable ResourceWarning: unclosed file
3339N/A warnings.filterwarnings("ignore", category=ResourceWarning)
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)