pkgrepo.py revision 1968
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#!/usr/bin/python2.6
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# CDDL HEADER START
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# The contents of this file are subject to the terms of the
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# Common Development and Distribution License (the "License").
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# You may not use this file except in compliance with the License.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# or http://www.opensolaris.org/os/licensing.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# See the License for the specific language governing permissions
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# and limitations under the License.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# When distributing Covered Code, include this CDDL HEADER in each
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# If applicable, add the following below this CDDL HEADER, with the
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# fields enclosed by brackets "[]" replaced with your own identifying
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# information: Portions Copyright [yyyy] [name of copyright owner]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# CDDL HEADER END
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenPKG_CLIENT_NAME = "pkgrepo"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# pkgrepo exit codes
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenEXIT_OK = 0
6ef7e31619edfaa17ed044b45861d106a86191efTimo SirainenEXIT_OOPS = 1
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenEXIT_BADOPT = 2
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenEXIT_PARTIAL = 3
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# listing constants
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenLISTING_FORMATS = ("tsv", )
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport errno
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport getopt
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport gettext
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport locale
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport logging
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport os
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport sys
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport urllib
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport urlparse
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport warnings
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenfrom pkg.client import global_settings
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenfrom pkg.misc import msg, PipeError
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport pkg
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport pkg.client.api_errors as apx
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport pkg.client.publisher as publisher
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport pkg.misc as misc
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport pkg.server.repository as sr
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport shlex
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenimport traceback
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenlogger = global_settings.logger
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef error(text, cmd=None):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Emit an error message prefixed by the command name """
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if cmd:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen text = "%s: %s" % (cmd, text)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkg_cmd = "pkgrepo "
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkg_cmd = "pkgrepo: "
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # If we get passed something like an Exception, we can convert
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # it down to a string.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen text = str(text)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # If the message starts with whitespace, assume that it should come
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # *before* the command-name prefix.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen text_nows = text.lstrip()
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ws = text[:len(text) - len(text_nows)]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # This has to be a constant value as we can't reliably get our actual
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # program name on all platforms.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen logger.error(ws + pkg_cmd + text_nows)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef usage(usage_error=None, cmd=None, retcode=2, full=False):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Emit a usage message and optionally prefix it with a more
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen specific error message. Causes program to exit.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if usage_error:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen error(usage_error, cmd=cmd)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if not full:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # The full usage message isn't desired.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen logger.error(_("Try `pkgrepo --help or -?' for more "
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "information."))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sys.exit(retcode)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen msg(_("""\
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenUsage:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkgrepo [options] subcommand [subcmd_options] [operands]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenSubcommands:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkgrepo create [repo_uri_or_path]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkgrepo publisher [pub_prefix ...]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkgrepo rebuild [--no-index]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkgrepo refresh [--no-catalog] [--no-index]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pkgrepo version
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo SirainenOptions:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen -s repo_uri_or_path The location of the repository to use for
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen operations. Network repositories are not
dcd50ecbfe796bd76f2d63483c534cc0e4e94164Timo Sirainen currently supported.
dcd50ecbfe796bd76f2d63483c534cc0e4e94164Timo Sirainen --help or -?"""))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sys.exit(retcode)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenclass OptionError(Exception):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Option exception. """
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def __init__(self, *args):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen Exception.__init__(self, *args)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef parse_uri(uri):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Parse the repository location provided and attempt to transform it
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen into a valid repository URI.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if uri.find("://") == -1 and not uri.startswith("file:/"):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Convert the file path to a URI.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uri = os.path.abspath(uri)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uri = urlparse.urlunparse(("file", "",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen urllib.pathname2url(uri), "", "", ""))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen scheme, netloc, path, params, query, fragment = \
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen urlparse.urlparse(uri, "file", allow_fragments=0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen scheme = scheme.lower()
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if scheme != "file":
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen usage(_("Network repositories are not currently supported."),
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen retcode=1)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if scheme == "file":
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # During urlunparsing below, ensure that the path starts with
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # only one '/' character, if any are present.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if path.startswith("/"):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen path = "/" + path.lstrip("/")
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Rebuild the url with the sanitized components.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uri = urlparse.urlunparse((scheme, netloc, path, params,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen query, fragment))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return publisher.RepositoryURI(uri)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef get_repo(conf, read_only=True, refresh_index=False):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Return the repository object for current program configuration."""
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen repo_uri = conf["repo_root"]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen path = repo_uri.get_pathname()
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if not path:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Bad URI?
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen raise sr.RepositoryInvalidError(str(repo_uri))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return sr.Repository(auto_create=False, read_only=read_only,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen refresh_index=refresh_index, repo_root=path)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef subcmd_create(conf, args):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Create a package repository at the given location."""
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen subcommand = "create"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen opts, pargs = getopt.getopt(args, "")
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if len(pargs) > 1:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen usage(_("Only one repository location may be specified."),
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen cmd=subcommand)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen elif pargs:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen conf["repo_root"] = parse_uri(pargs[0])
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen repo_root = conf.get("repo_root", None)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if not repo_root:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen usage(_("No repository location specified."), cmd=subcommand)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Attempt to create a repository at the specified location. Allow
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # whatever exceptions are raised to bubble up.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sr.repository_create(repo_root)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return EXIT_OK
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef print_col_listing(desired_field_order, field_data, field_values, out_format,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def_fmt, omit_headers):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen """Print a columnar listing defined by provided values."""
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Custom sort function for preserving field ordering
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def sort_fields(one, two):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return desired_field_order.index(get_header(one)) - \
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen desired_field_order.index(get_header(two))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Functions for manipulating field_data records
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def filter_default(record):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return "default" in record[0]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def filter_tsv(record):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return "tsv" in record[0]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def get_header(record):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return record[1]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def get_value(record):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return record[2]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen def set_value(entry):
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen entry[0][2] = entry[1]
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if out_format == "default":
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Create a formatting string for the default output
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # format.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fmt = def_fmt
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter_func = filter_default
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen elif out_format == "tsv":
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Create a formatting string for the tsv output
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # format.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen num_fields = len(field_data.keys())
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fmt = "\t".join('%s' for x in xrange(num_fields))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter_func = filter_tsv
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Extract the list of headers from the field_data dictionary. Ensure
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # they are extracted in the desired order by using the custom sort
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # function.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen hdrs = map(get_header, sorted(filter(filter_func, field_data.values()),
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sort_fields))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen # Output a header if desired.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if not omit_headers:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen msg(fmt % tuple(hdrs))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen for entry in field_values:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen map(set_value, (
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (field_data[f], v)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen for f, v in entry.iteritems()
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen values = map(get_value, sorted(filter(filter_func,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen field_data.values()), sort_fields))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen msg(fmt % tuple(values))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendef subcmd_property(conf, args):
"""Display the list of properties for the repository."""
subcommand = "property"
repo = get_repo(conf)
omit_headers = False
out_format = "default"
opts, pargs = getopt.getopt(args, "F:H")
for opt, arg in opts:
if opt == "-F":
out_format = arg
if out_format not in LISTING_FORMATS:
usage(_("Unrecognized format %(format)s."
" Supported formats: %(valid)s") % \
{ "format": out_format,
"valid": LISTING_FORMATS }, cmd="publisher")
return EXIT_OOPS
elif opt == "-H":
omit_headers = True
# Configuration index is indexed by section name and property name.
# Flatten it to simplify listing process.
cfg_idx = repo.cfg.get_index()
props = set()
# Set minimum widths for section and property name columns by using the
# length of the column headers.
max_sname_len = len(_("SECTION"))
max_pname_len = len(_("PROPERTY"))
for sname in cfg_idx:
max_sname_len = max(max_sname_len, len(sname))
for pname in cfg_idx[sname]:
max_pname_len = max(max_pname_len, len(pname))
props.add("/".join((sname, pname)))
del cfg_idx
if len(pargs) >= 1:
found = props & set(pargs)
notfound = set(pargs) - found
del props
else:
found = props
notfound = set()
def gen_listing():
for prop in sorted(found):
sname, pname = prop.rsplit("/", 1)
sval = str(repo.cfg.get_property(sname, pname))
yield {
"section": sname,
"property": pname,
"value": sval,
}
# SECTION PROPERTY VALUE
# <sec_1> <prop_1> <prop_1_value>
# <sec_2> <prop_2> <prop_2_value>
# ...
field_data = {
"section" : [("default", "tsv"), _("SECTION"), ""],
"property" : [("default", "tsv"), _("PROPERTY"), ""],
"value" : [("default", "tsv"), _("VALUE"), ""],
}
desired_field_order = (_("SECTION"), "", _("PROPERTY"), _("VALUE"))
# Default output formatting.
def_fmt = "%-" + str(max_sname_len) + "s %-" + str(max_pname_len) + \
"s %s"
if found or (not pargs and out_format == "default"):
print_col_listing(desired_field_order, field_data,
gen_listing(), out_format, def_fmt, omit_headers)
if found and notfound:
return EXIT_PARTIAL
if pargs and not found:
if out_format == "default":
# Don't pollute other output formats.
error(_("no matching properties found"),
cmd=subcommand)
return EXIT_OOPS
return EXIT_OK
def subcmd_set_property(conf, args):
"""Set a repository property."""
subcommand = "property"
repo = get_repo(conf, read_only=False)
omit_headers = False
out_format = "default"
opts, pargs = getopt.getopt(args, "")
bad_args = False
if not pargs or len(pargs) > 1:
bad_args = True
else:
try:
if len(pargs) == 1:
prop, val = pargs[0].split("=", 1)
sname, pname = prop.rsplit("/", 1)
except ValueError:
bad_args = True
if bad_args:
usage(_("a property name and value must be provided in the "
"form <section/property>=<value> or "
"<section/property>=([\"<value>\", ...])"))
if len(val) > 0 and val[0] == "(" and val[-1] == ")":
val = shlex.split(val.strip("()"))
repo.cfg.set_property(sname, pname, val)
repo.write_config()
def subcmd_publisher(conf, args):
"""Display a list of known publishers and a summary of known packages
and when the package data for the given publisher was last updated.
"""
subcommand = "publisher"
repo = get_repo(conf)
omit_headers = False
out_format = "default"
opts, pargs = getopt.getopt(args, "F:H")
for opt, arg in opts:
if opt == "-F":
out_format = arg
if out_format not in LISTING_FORMATS:
usage(_("Unrecognized format %(format)s."
" Supported formats: %(valid)s") % \
{ "format": out_format,
"valid": LISTING_FORMATS }, cmd="publisher")
return EXIT_OOPS
elif opt == "-H":
omit_headers = True
cat = repo.catalog
pub_idx = {}
for pub, pkg_count, pkg_ver_count in cat.get_package_counts_by_pub():
pub_idx[pub] = (pkg_count, pkg_ver_count)
if len(pargs) >= 1:
found = set(pub_idx.keys()) & set(pargs)
notfound = set(pargs) - found
else:
found = set(pub_idx.keys())
notfound = set()
def gen_listing():
for pfx in found:
pkg_count, pkg_ver_count = pub_idx[pfx]
yield {
"publisher": pfx,
"packages": pkg_count,
"versions": pkg_ver_count,
"updated": "%sZ" % cat.last_modified.isoformat(),
}
# PUBLISHER PACKAGES VERSIONS UPDATED
# <pub_1> <num_uniq_pkgs> <num_pkg_vers> <cat_last_modified>
# <pub_2> <num_uniq_pkgs> <num_pkg_vers> <cat_last_modified>
# ...
field_data = {
"publisher" : [("default", "tsv"), _("PUBLISHER"), ""],
"packages" : [("default", "tsv"), _("PACKAGES"), ""],
"versions" : [("default", "tsv"), _("VERSIONS"), ""],
"updated" : [("default", "tsv"), _("UPDATED"), ""],
}
desired_field_order = (_("PUBLISHER"), "", _("PACKAGES"), _("VERSIONS"),
_("UPDATED"))
# Default output formatting.
def_fmt = "%-24s %-8s %-8s %s"
if found or (not pargs and out_format == "default"):
print_col_listing(desired_field_order, field_data,
gen_listing(), out_format, def_fmt, omit_headers)
if found and notfound:
return EXIT_PARTIAL
if pargs and not found:
if out_format == "default":
# Don't pollute other output formats.
error(_("no matching publishers found"),
cmd=subcommand)
return EXIT_OOPS
return EXIT_OK
def subcmd_rebuild(conf, args):
"""Rebuild the repository's catalog and index data (as permitted)."""
subcommand = "rebuild"
repo = get_repo(conf, read_only=False)
build_index = True
opts, pargs = getopt.getopt(args, "", ["no-index"])
for opt, arg in opts:
if opt == "--no-index":
build_index = False
if pargs:
usage(_("command does not take operands"), cmd=subcommand)
logger.info("Rebuilding package repository...")
repo.rebuild(build_index=False)
if build_index:
# Always build search indexes seperately (and if permitted).
logger.info("Building search indexes...")
repo.refresh_index()
return EXIT_OK
def subcmd_refresh(conf, args):
"""Refresh the repository's catalog and index data (as permitted)."""
subcommand = "refresh"
repo = get_repo(conf, read_only=False)
add_content = True
refresh_index = True
opts, pargs = getopt.getopt(args, "", ["no-catalog", "no-index"])
for opt, arg in opts:
if opt == "--no-catalog":
add_content = False
elif opt == "--no-index":
refresh_index = False
if pargs:
usage(_("command does not take operands"), cmd=subcommand)
if not add_content and not refresh_index:
# Why? Who knows; but do what was requested--nothing!
return EXIT_OK
if add_content:
logger.info("Adding new package content...")
repo.add_content(refresh_index=False)
if refresh_index:
# Always update search indexes separately (and if permitted).
logger.info("Updating search indexes...")
repo.refresh_index()
return EXIT_OK
def subcmd_version(conf, args):
"""Display the version of the pkg(5) API."""
subcommand = "version"
if conf.get("repo_root", None):
usage(_("-s not allowed for %s subcommand") %
subcommand)
if args:
usage(_("command does not take operands"), cmd=subcommand)
msg(pkg.VERSION)
return EXIT_OK
def main_func():
global_settings.client_name = PKG_CLIENT_NAME
try:
opts, pargs = getopt.getopt(sys.argv[1:], "s:?",
["help"])
except getopt.GetoptError, e:
usage(_("illegal global option -- %s") % e.opt)
conf = {}
show_usage = False
for opt, arg in opts:
if opt == "-s":
if not arg:
continue
conf["repo_root"] = parse_uri(arg)
elif opt in ("--help", "-?"):
show_usage = True
subcommand = None
if pargs:
subcommand = pargs.pop(0)
if subcommand == "help":
show_usage = True
if show_usage:
usage(retcode=0, full=True)
elif not subcommand:
usage(_("no subcommand specified"))
subcommand = subcommand.replace("-", "_")
func = globals().get("subcmd_%s" % subcommand, None)
if not func:
usage(_("unknown subcommand '%s'") % subcommand)
try:
if (subcommand != "create" and subcommand != "version") and \
not conf.get("repo_root", None):
usage(_("A package repository location must be "
"provided using -s."), cmd=subcommand)
return func(conf, pargs)
except getopt.GetoptError, e:
if e.opt in ("help", "?"):
usage(full=True)
usage(_("illegal option -- %s") % e.opt, cmd=subcommand)
#
# Establish a specific exit status which means: "python barfed an exception"
# so that we can more easily detect these in testing of the CLI commands.
#
def handle_errors(func, *args, **kwargs):
"""Catch exceptions raised by the main program function and then print
a message and/or exit with an appropriate return code.
"""
traceback_str = _("\n\nThis is an internal error. Please let the "
"developers know about this\nproblem by filing a bug at "
"http://defect.opensolaris.org and including the\nabove "
"traceback and this message. The version of pkg(5) is "
"'%s'.") % pkg.VERSION
try:
# Out of memory errors can be raised as EnvironmentErrors with
# an errno of ENOMEM, so in order to handle those exceptions
# with other errnos, we nest this try block and have the outer
# one handle the other instances.
try:
__ret = func(*args, **kwargs)
except (MemoryError, EnvironmentError), __e:
if isinstance(__e, EnvironmentError) and \
__e.errno != errno.ENOMEM:
raise
error("\n" + misc.out_of_memory())
__ret = EXIT_OOPS
except SystemExit, __e:
raise __e
except (PipeError, KeyboardInterrupt):
# Don't display any messages here to prevent possible further
# broken pipe (EPIPE) errors.
__ret = EXIT_OOPS
except apx.VersionException, __e:
error(_("The pkgrepo command appears out of sync with the "
"libraries provided\nby pkg:/package/pkg. The client "
"version is %(client)s while the library\nAPI version is "
"%(api)s.") % {'client': __e.received_version,
'api': __e.expected_version
})
__ret = EXIT_OOPS
except (apx.ApiException, sr.RepositoryError), __e:
error(str(__e))
__ret = EXIT_OOPS
except:
traceback.print_exc()
error(traceback_str)
__ret = 99
return __ret
if __name__ == "__main__":
misc.setlocale(locale.LC_ALL, "", error)
gettext.install("pkg", "/usr/share/locale")
# Make all warnings be errors.
warnings.simplefilter('error')
__retval = handle_errors(main_func)
try:
logging.shutdown()
except IOError:
# Ignore python's spurious pipe problems.
pass
sys.exit(__retval)