pkgrepo.py revision 3447
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch#!/usr/bin/python2.7
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# CDDL HEADER START
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen# The contents of this file are subject to the terms of the
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen# Common Development and Distribution License (the "License").
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen# You may not use this file except in compliance with the License.
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen# or http://www.opensolaris.org/os/licensing.
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen# See the License for the specific language governing permissions
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# and limitations under the License.
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# When distributing Covered Code, include this CDDL HEADER in each
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# If applicable, add the following below this CDDL HEADER, with the
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# fields enclosed by brackets "[]" replaced with your own identifying
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# information: Portions Copyright [yyyy] [name of copyright owner]
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# CDDL HEADER END
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen# Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo SirainenPKG_CLIENT_NAME = "pkgrepo"
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen# pkgrepo exit codes
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo SirainenEXIT_OK = 0
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo SirainenEXIT_OOPS = 1
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo SirainenEXIT_BADOPT = 2
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo SirainenEXIT_PARTIAL = 3
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo SirainenEXIT_DIFF = 10
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen# listing constants
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo SirainenLISTING_FORMATS = ("default", "json", "json-formatted", "tsv")
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
d6684856fb99e51bc22a6346e08b2d81c996f963Josef 'Jeff' Sipek# diff type
d6684856fb99e51bc22a6346e08b2d81c996f963Josef 'Jeff' SipekMINUS = -1
d6684856fb99e51bc22a6346e08b2d81c996f963Josef 'Jeff' SipekPLUS = 1
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo SirainenCOMMON = 0
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainendiff_type_f = {MINUS: "- ", PLUS: "+ ", COMMON: ""}
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen# globals
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainentmpdirs = []
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenimport atexit
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenimport collections
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenimport copy
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenimport errno
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenimport getopt
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekimport gettext
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekimport locale
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekimport logging
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekimport os
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekimport operator
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekimport shlex
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenimport shutil
f4169072aebf5607b272f954fb405c838e1aa4d6Josef 'Jeff' Sipekimport six
e9fbe5e18b798728041b7e2ffc6c4fa964fc35a3Josef 'Jeff' Sipekimport sys
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainenimport tempfile
f4169072aebf5607b272f954fb405c838e1aa4d6Josef 'Jeff' Sipekimport textwrap
f4169072aebf5607b272f954fb405c838e1aa4d6Josef 'Jeff' Sipekimport traceback
5a1b498b646b5c5dbd1b3f3861df766f560578c5Timo Sirainenimport warnings
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenimport itertools
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenfrom imp import reload
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenfrom pkg.client import global_settings
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenfrom pkg.client.debugvalues import DebugValues
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenfrom pkg.misc import msg, PipeError
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenfrom prettytable import PrettyTable
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenimport pkg
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenimport pkg.catalog
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenimport pkg.client.api_errors as apx
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenimport pkg.client.pkgdefs as pkgdefs
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenimport pkg.client.progress
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenimport pkg.client.publisher as publisher
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainenimport pkg.client.transport.transport as transport
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenimport pkg.fmri as fmri
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenimport pkg.misc as misc
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainenimport pkg.server.repository as sr
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainenimport simplejson as json
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenlogger = global_settings.logger
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen@atexit.register
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainendef cleanup():
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen """To be called at program finish."""
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for d in tmpdirs:
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen shutil.rmtree(d, True)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainendef error(text, cmd=None):
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen """Emit an error message prefixed by the command name """
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen if cmd:
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen text = "{0}: {1}".format(cmd, text)
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen pkg_cmd = "pkgrepo "
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen else:
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen pkg_cmd = "pkgrepo: "
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen # If we get passed something like an Exception, we can convert
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen # it down to a string.
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen text = str(text)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen # If the message starts with whitespace, assume that it should come
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen # *before* the command-name prefix.
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen text_nows = text.lstrip()
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen ws = text[:len(text) - len(text_nows)]
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen # This has to be a constant value as we can't reliably get our actual
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen # program name on all platforms.
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen logger.error(ws + pkg_cmd + text_nows)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
660ecbaf80e6b3cf3a70ed1e0cdf7f8af6d895d0Josef 'Jeff' Sipekdef get_tracker(quiet=False):
660ecbaf80e6b3cf3a70ed1e0cdf7f8af6d895d0Josef 'Jeff' Sipek if quiet:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen progtrack = pkg.client.progress.QuietProgressTracker()
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen else:
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen try:
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen progtrack = \
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen pkg.client.progress.FancyUNIXProgressTracker()
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen except pkg.client.progress.ProgressTrackerException:
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen progtrack = \
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen pkg.client.progress.CommandLineProgressTracker()
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return progtrack
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainendef usage(usage_error=None, cmd=None, retcode=2, full=False):
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen """Emit a usage message and optionally prefix it with a more
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen specific error message. Causes program to exit.
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen """
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen if usage_error:
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen error(usage_error, cmd=cmd)
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen if not full:
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen # The full usage message isn't desired.
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen logger.error(_("Try `pkgrepo --help or -?' for more "
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen "information."))
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen sys.exit(retcode)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen msg(_("""\
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo SirainenUsage:
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen pkgrepo [options] command [cmd_options] [operands]
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
463f6ea04af934a68facaca0ff089bc306de3f98Timo SirainenSubcommands:
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen pkgrepo create [--version ver] uri_or_path
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen pkgrepo add-publisher -s repo_uri_or_path publisher ...
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen pkgrepo remove-publisher [-n] [--synchronous] -s repo_uri_or_path
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen publisher ...
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen pkgrepo get [-F format] [-p publisher ...] -s repo_uri_or_path
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen [--key ssl_key ... --cert ssl_cert ...] [section/property ...]
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen pkgrepo info [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen [--key ssl_key ... --cert ssl_cert ...]
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen pkgrepo list [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen [--key ssl_key ... --cert ssl_cert ...] [pkg_fmri_pattern ...]
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen pkgrepo contents [-m] [-t action_type ...] -s repo_uri_or_path
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen [--key ssl_key ... --cert ssl_cert ...] [pkg_fmri_pattern ...]
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen
0b6924ad1943fe5c6917fc49f675d8f316b0d939Timo Sirainen pkgrepo rebuild [-p publisher ...] -s repo_uri_or_path [--key ssl_key ...
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen --cert ssl_cert ...] [--no-catalog] [--no-index]
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen pkgrepo refresh [-p publisher ...] -s repo_uri_or_path [--key ssl_key ...
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen --cert ssl_cert ...] [--no-catalog] [--no-index]
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen pkgrepo remove [-n] [-p publisher ...] -s repo_uri_or_path
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen pkg_fmri_pattern ...
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen pkgrepo set [-p publisher ...] -s repo_uri_or_path
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen section/property[+|-]=[value] ... or
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen section/property[+|-]=([value]) ...
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen pkgrepo verify [-d] [-p publisher ...] [-i ignored_dep_file ...]
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen [--disable verification ...] -s repo_uri_or_path
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen pkgrepo fix [-v] [-p publisher ...] -s repo_uri_or_path
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen pkgrepo diff [-vq] [--strict] [--parsable] [-p publisher ...]
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen -s first_repo_uri_or_path [--key ssl_key ... --cert ssl_cert ...]
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen -s second_repo_uri_or_path [--key ssl_key ... --cert ssl_cert ...]
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen pkgrepo help
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen pkgrepo version
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo SirainenOptions:
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen --help or -?
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen Displays a usage message."""))
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen sys.exit(retcode)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainenclass OptionError(Exception):
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen """Option exception. """
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen def __init__(self, *args):
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen Exception.__init__(self, *args)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainendef parse_uri(uri):
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen """Parse the repository location provided and attempt to transform it
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen into a valid repository URI.
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen """
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainen return publisher.RepositoryURI(misc.parse_uri(uri))
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainen
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainen
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainendef subcmd_remove(conf, args):
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainen subcommand = "remove"
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainen
ba55ec8fdf79301bfce9838d773250cef9486178Timo Sirainen opts, pargs = getopt.getopt(args, "np:s:")
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen dry_run = False
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen pubs = set()
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen for opt, arg in opts:
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen if opt == "-n":
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen dry_run = True
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen elif opt == "-p":
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen pubs.add(arg)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen elif opt == "-s":
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen conf["repo_uri"] = parse_uri(arg)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen if not pargs:
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen usage(_("At least one package pattern must be provided."),
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen cmd=subcommand)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen # Get repository object.
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen if not conf.get("repo_uri", None):
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen usage(_("A package repository location must be provided "
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen "using -s."), cmd=subcommand)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen repo = get_repo(conf, read_only=False, subcommand=subcommand)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen if "all" in pubs:
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen pubs = set()
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen # Find matching packages.
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen try:
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen matching, refs = repo.get_matching_fmris(pargs, pubs=pubs)
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen except apx.PackageMatchErrors as e:
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen error(str(e), cmd=subcommand)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return EXIT_OOPS
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen if dry_run:
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen # Don't make any changes; display list of packages to be
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen # removed and exit.
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen packages = set(f for m in matching.values() for f in m)
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen count = len(packages)
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen plist = "\n".join("\t{0}".format(
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen p.get_fmri(include_build=False))
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen for p in sorted(packages))
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen logger.info(_("{count:d} package(s) will be removed:\n"
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen "{plist}").format(**locals()))
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen return EXIT_OK
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen progtrack = get_tracker()
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen packages = collections.defaultdict(list)
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen for m in matching.values():
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen for f in m:
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen packages[f.publisher].append(f)
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen for pub in packages:
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen logger.info(
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen _("Removing packages for publisher {0} ...").format(pub))
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen repo.remove_packages(packages[pub], progtrack=progtrack,
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen pub=pub)
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen if len(packages) > 1:
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen # Add a newline between each publisher.
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen logger.info("")
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen return EXIT_OK
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainendef get_repo(conf, allow_invalid=False, read_only=True, subcommand=None):
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen """Return the repository object for current program configuration.
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen 'allow_invalid' specifies whether potentially corrupt repositories are
66967e15d8ed35f30493dd9694a0aabe1a266f9dTimo Sirainen allowed; should only be True if performing a rebuild operation."""
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen repo_uri = conf["repo_uri"]
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen if repo_uri.scheme != "file":
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen usage(_("Network repositories are not currently supported "
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "for this operation."), cmd=subcommand)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen path = repo_uri.get_pathname()
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if not path:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen # Bad URI?
a2738cdb6d2733fb3e186331d68009421a19ea00Timo Sirainen raise sr.RepositoryInvalidError(str(repo_uri))
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen return sr.Repository(allow_invalid=allow_invalid, read_only=read_only,
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen root=path)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainendef setup_transport(repo_uri, subcommand=None, prefix=None, verbose=False,
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen remote_prefix=True, ssl_key=None, ssl_cert=None):
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if not repo_uri:
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen usage(_("No repository location specified."), cmd=subcommand)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen global tmpdirs
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen temp_root = misc.config_temp_root()
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
7af4788b402346c94496095dd819f95ce03fe431Timo Sirainen tmp_dir = tempfile.mkdtemp(dir=temp_root)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen tmpdirs.append(tmp_dir)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen incoming_dir = tempfile.mkdtemp(dir=temp_root)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen tmpdirs.append(incoming_dir)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen cache_dir = tempfile.mkdtemp(dir=temp_root)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen tmpdirs.append(cache_dir)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen # Create transport and transport config.
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen xport, xport_cfg = transport.setup_transport()
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen xport_cfg.add_cache(cache_dir, readonly=False)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen xport_cfg.incoming_root = incoming_dir
c24ef531ca58abad996482f5c2e8992be9ae8981Timo Sirainen xport_cfg.pkg_root = tmp_dir
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen if not prefix:
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen pub = "target"
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen else:
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen pub = prefix
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen # Configure target publisher.
2524ef7b34965a1b1895d6140fd8296bf57c78d2Timo Sirainen src_pub = transport.setup_publisher(str(repo_uri), pub, xport,
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen xport_cfg, remote_prefix=remote_prefix, ssl_key=ssl_key,
a215abacb2d2d1e1bcd475756aab809038ae4277Timo Sirainen ssl_cert=ssl_cert)
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen return xport, src_pub, tmp_dir
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainendef subcmd_add_publisher(conf, args):
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen """Add publisher(s) to the specified repository."""
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen subcommand = "add-publisher"
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen opts, pargs = getopt.getopt(args, "s:")
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen for opt, arg in opts:
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen if opt == "-s":
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen conf["repo_uri"] = parse_uri(arg)
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen repo_uri = conf.get("repo_uri", None)
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen if not repo_uri:
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen usage(_("No repository location specified."), cmd=subcommand)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if repo_uri.scheme != "file":
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen usage(_("Network repositories are not currently supported "
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen "for this operation."), cmd=subcommand)
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen if not pargs:
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen usage(_("At least one publisher must be specified"),
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen cmd=subcommand)
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen abort = False
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen for pfx in pargs:
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen if not misc.valid_pub_prefix(pfx):
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen error(_("Invalid publisher prefix '{0}'").format(pfx),
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen cmd=subcommand)
e0fab14602b73ff590b2a9c5d9e67e2dfb5d1f9eTimo Sirainen abort = True
a215abacb2d2d1e1bcd475756aab809038ae4277Timo Sirainen if abort:
a215abacb2d2d1e1bcd475756aab809038ae4277Timo Sirainen return EXIT_OOPS
a215abacb2d2d1e1bcd475756aab809038ae4277Timo Sirainen
a215abacb2d2d1e1bcd475756aab809038ae4277Timo Sirainen repo = get_repo(conf, read_only=False, subcommand=subcommand)
a215abacb2d2d1e1bcd475756aab809038ae4277Timo Sirainen make_default = not repo.publishers
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen existing = repo.publishers & set(pargs)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # Elide the publishers that already exist, but retain the order
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # publishers were specified in.
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen new_pubs = [
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen pfx for pfx in pargs
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if pfx not in repo.publishers
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen ]
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # Tricky logic; _set_pub will happily add new publishers if necessary
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # and not set any properties if you didn't specify any.
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen rval = _set_pub(conf, subcommand, {}, new_pubs, repo)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen if make_default:
66dc739bb67d678770e1b7a7bc75f4f6f9523d2aTimo Sirainen # No publisher existed previously, so set the default publisher
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # to be the first new one that was added.
66dc739bb67d678770e1b7a7bc75f4f6f9523d2aTimo Sirainen _set_repo(conf, subcommand, { "publisher": {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen "prefix": new_pubs[0] } }, repo)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if rval == EXIT_OK and existing:
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # Some of the publishers that were requested for addition
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen # were already known.
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen error(_("specified publisher(s) already exist: {0}").format(
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen ", ".join(existing)), cmd=subcommand)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if new_pubs:
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return EXIT_PARTIAL
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return EXIT_OOPS
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return rval
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainendef subcmd_remove_publisher(conf, args):
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen """Remove publisher(s) from a repository"""
c6b6ac7819931dfa92c0182ffaa7db07ac6ab0daTimo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen subcommand = "remove-publisher"
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen dry_run = False
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen synch = False
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen opts, pargs = getopt.getopt(args, "ns:", ["synchronous"])
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen for opt, arg in opts:
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen if opt == "-s":
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen conf["repo_uri"] = parse_uri(arg)
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen elif opt == "-n":
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen dry_run = True
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen elif opt == "--synchronous":
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen synch = True
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen repo_uri = conf.get("repo_uri", None)
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen if not repo_uri:
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen usage(_("No repository location specified."), cmd=subcommand)
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen if repo_uri.scheme != "file":
785ee8becdb11e41abaaf64c28eb3923215d1f27Timo Sirainen usage(_("Network repositories are not currently supported "
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen "for this operation."), cmd=subcommand)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if not pargs:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen usage(_("At least one publisher must be specified"),
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cmd=subcommand)
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen inv_pfxs = []
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen for pfx in pargs:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if not misc.valid_pub_prefix(pfx):
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen inv_pfxs.append(pfx)
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen if inv_pfxs:
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen error(_("Invalid publisher prefix(es):\n {0}").format(
54bd0fec0be357266e299466a582f3c9269884e9Timo Sirainen "\n ".join(inv_pfxs)), cmd=subcommand)
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen return EXIT_OOPS
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen repo = get_repo(conf, read_only=False, subcommand=subcommand)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen existing = repo.publishers & set(pargs)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen noexisting = [pfx for pfx in pargs
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if pfx not in repo.publishers]
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen # Publishers left if remove succeeds.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen left = [pfx for pfx in repo.publishers if pfx not in pargs]
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi if noexisting:
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen error(_("The following publisher(s) could not be found:\n "
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen "{0}").format("\n ".join(noexisting)), cmd=subcommand)
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen return EXIT_OOPS
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen logger.info(_("Removing publisher(s)"))
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen for pfx in existing:
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen rstore = repo.get_pub_rstore(pfx)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen numpkg = rstore.catalog.package_count
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen logger.info(_("\'{pfx}\'\t({num} package(s))").format(
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen pfx=pfx, num=str(numpkg)))
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if dry_run:
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return EXIT_OK
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen defaultpfx = repo.cfg.get_property("publisher", "prefix")
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen repo_path = repo_uri.get_pathname()
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen repo.remove_publisher(existing, repo_path, synch)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen # Change the repository publisher/prefix property, if necessary.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if defaultpfx in existing:
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if len(left) == 1:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen _set_repo(conf, subcommand, { "publisher" : {
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen "prefix" : left[0]} }, repo)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen msg(_("The default publisher was removed."
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen " Setting 'publisher/prefix' to '{0}',"
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen " the only publisher left").format(left[0]))
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi else:
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi _set_repo(conf, subcommand, { "publisher": {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen "prefix" : ""} }, repo)
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi msg(_("The default publisher was removed."
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen " The 'publisher/prefix' property has been"
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen " unset"))
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return EXIT_OK
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainendef subcmd_create(conf, args):
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen """Create a package repository at the given location."""
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen subcommand = "create"
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi opts, pargs = getopt.getopt(args, "s:", ["version="])
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen version = None
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen for opt, arg in opts:
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if opt == "-s":
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen conf["repo_uri"] = parse_uri(arg)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen elif opt == "--version":
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen # This option is currently private and allows creating a
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen # repository with a specific format based on version.
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen try:
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen version = int(arg)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen except ValueError:
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi usage(_("Version must be an integer value."),
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen cmd=subcommand)
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if len(pargs) > 1:
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen usage(_("Only one repository location may be specified."),
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen cmd=subcommand)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen elif pargs:
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen conf["repo_uri"] = parse_uri(pargs[0])
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen repo_uri = conf.get("repo_uri", None)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if not repo_uri:
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen usage(_("No repository location specified."), cmd=subcommand)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if repo_uri.scheme != "file":
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi usage(_("Network repositories are not currently supported "
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen "for this operation."), cmd=subcommand)
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainen
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi # Attempt to create a repository at the specified location. Allow
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi # whatever exceptions are raised to bubble up.
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen sr.repository_create(repo_uri, version=version)
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return EXIT_OK
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainendef subcmd_get(conf, args):
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen """Display repository properties."""
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen subcommand = "get"
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen omit_headers = False
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi out_format = "default"
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen pubs = set()
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen key = None
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi cert = None
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi opts, pargs = getopt.getopt(args, "F:Hp:s:", ["key=", "cert="])
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi for opt, arg in opts:
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if opt == "-F":
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if arg not in LISTING_FORMATS:
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen raise apx.InvalidOptionError(
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen apx.InvalidOptionError.ARG_INVALID,
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen [arg, opt])
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen out_format = arg
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen elif opt == "-H":
1bc075e2e4ed422f9590c95c3ae223422b97ce6aTimo Sirainen omit_headers = True
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen elif opt == "-p":
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen pubs.add(arg)
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen elif opt == "-s":
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen conf["repo_uri"] = parse_uri(arg)
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen elif opt == "--key":
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen key = arg
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen elif opt == "--cert":
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen cert = arg
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen # Setup transport so configuration can be retrieved.
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen if not conf.get("repo_uri", None):
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen usage(_("A package repository location must be provided "
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen "using -s."), cmd=subcommand)
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen subcommand=subcommand, ssl_key=key, ssl_cert=cert)
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen # Get properties.
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if pubs:
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen return _get_pub(conf, subcommand, xport, xpub, omit_headers,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen out_format, pubs, pargs)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen return _get_repo(conf, subcommand, xport, xpub, omit_headers,
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen out_format, pargs)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainendef _get_repo(conf, subcommand, xport, xpub, omit_headers, out_format, pargs):
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen """Display repository properties."""
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen # Configuration index is indexed by section name and property name.
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen # Retrieve and flatten it to simplify listing process.
e6bc5e739360377c5badf0da24208f4836722eaeStephan Bosch stat_idx = xport.get_status(xpub)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen cfg_idx = stat_idx.get("repository", {}).get("configuration", {})
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen props = set()
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen # Set minimum widths for section and property name columns by using the
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen # length of the column headers.
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen max_sname_len = len(_("SECTION"))
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen max_pname_len = len(_("PROPERTY"))
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen for sname in cfg_idx:
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen max_sname_len = max(max_sname_len, len(sname))
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen for pname in cfg_idx[sname]:
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen max_pname_len = max(max_pname_len, len(pname))
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen props.add("/".join((sname, pname)))
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen req_props = set(pargs)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if len(req_props) >= 1:
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen found = props & req_props
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen notfound = req_props - found
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen del props
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen else:
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen found = props
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen notfound = set()
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen def gen_listing():
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen for prop in sorted(found):
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen sname, pname = prop.rsplit("/", 1)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen sval = cfg_idx[sname][pname]
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen yield {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen "section": sname,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen "property": pname,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen "value": sval,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen # SECTION PROPERTY VALUE
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen # <sec_1> <prop_1> <prop_1_value>
7a77b948806106b46a33f3e6a3869657f49877fdTimo Sirainen # <sec_2> <prop_2> <prop_2_value>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen # ...
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen field_data = {
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen "section" : [("default", "json", "tsv"), _("SECTION"), ""],
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen "property" : [("default", "json", "tsv"), _("PROPERTY"), ""],
b7651d283ca261015ef3c445f1f27f340f0864e2Timo Sirainen "value" : [("default", "json", "tsv"), _("VALUE"), ""],
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen }
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen desired_field_order = (_("SECTION"), _("PROPERTY"), _("VALUE"))
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen # Default output formatting.
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen def_fmt = "{0:" + str(max_sname_len) + "} {1:" + str(max_pname_len) + \
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "} {2}"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen if found or (not req_props and out_format == "default"):
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen # print without trailing newline.
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen sys.stdout.write(misc.get_listing(desired_field_order,
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen field_data, gen_listing(), out_format, def_fmt,
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen omit_headers))
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen if found and notfound:
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen return EXIT_PARTIAL
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen if req_props and not found:
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen if out_format == "default":
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen # Don't pollute other output formats.
89c8d5f336e44cca091a1f588d51ba26e5145ad2Timo Sirainen error(_("no matching properties found"),
89c8d5f336e44cca091a1f588d51ba26e5145ad2Timo Sirainen cmd=subcommand)
89c8d5f336e44cca091a1f588d51ba26e5145ad2Timo Sirainen return EXIT_OOPS
89c8d5f336e44cca091a1f588d51ba26e5145ad2Timo Sirainen return EXIT_OK
f20e7fbdc9bdbe8fecb9c661c9b8175f3bb78c69Timo Sirainen
c0b1543512bc3e0a3a9f526056a3678a07ce32f5Timo Sirainen
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainendef _get_matching_pubs(subcommand, pubs, xport, xpub, out_format="default",
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen use_transport=False, repo_uri=None):
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen # Retrieve publisher information.
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen pub_data = xport.get_publisherdata(xpub)
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen known_pubs = set(p.prefix for p in pub_data)
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen if len(pubs) > 0 and "all" not in pubs:
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen found = known_pubs & pubs
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen notfound = pubs - found
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen pub_data = [p for p in pub_data if p.prefix in found]
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen else:
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen found = known_pubs
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen notfound = set()
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if use_transport:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen # Assign transport information.
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen for p in pub_data:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen p.repository = xpub.repository
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen # Establish initial return value and perform early exit if appropriate.
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen rval = EXIT_OK
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen if found and notfound:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen rval = EXIT_PARTIAL
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen elif pubs and not found:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen if out_format == "default":
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen # Don't pollute other output formats.
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen err_msg = _("no matching publishers found")
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen if repo_uri:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen err_msg = _("no matching publishers found in "
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "repository: {0}").format(repo_uri)
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen error(err_msg, cmd=subcommand)
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen return EXIT_OOPS, None, None
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen return rval, found, pub_data
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainendef _get_pub(conf, subcommand, xport, xpub, omit_headers, out_format, pubs,
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen pargs):
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen """Display publisher properties."""
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen xpub, out_format=out_format)
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if rval == EXIT_OOPS:
5965eaa2d972e6264cecaf54091cd43019bc7d1fTimo Sirainen return rval
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen # Set minimum widths for section and property name columns by using the
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen # length of the column headers and data.
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen max_pubname_len = str(max(
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen [len(_("PUBLISHER"))] + [len(p) for p in found]
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen ))
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen max_sname_len = len(_("SECTION"))
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen max_pname_len = len(_("PROPERTY"))
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen # For each requested publisher, retrieve the requested property data.
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen pub_idx = {}
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen for pub in pub_data:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen pub_idx[pub.prefix] = {
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "publisher": {
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "alias": pub.alias,
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "prefix": pub.prefix,
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen },
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen }
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen pub_repo = pub.repository
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen if pub_repo:
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen pub_idx[pub.prefix]["repository"] = {
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen "collection-type": pub_repo.collection_type,
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen "description": pub_repo.description,
5965eaa2d972e6264cecaf54091cd43019bc7d1fTimo Sirainen "legal-uris": pub_repo.legal_uris,
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "mirrors": pub_repo.mirrors,
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "name": pub_repo.name,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "origins": pub_repo.origins,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "refresh-seconds": pub_repo.refresh_seconds,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "registration-uri": pub_repo.registration_uri,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "related-uris": pub_repo.related_uris,
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen }
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen else:
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen pub_idx[pub.prefix]["repository"] = {
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen "collection-type": "core",
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "description": "",
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen "legal-uris": [],
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen "mirrors": [],
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen "name": "",
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen "origins": [],
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "refresh-seconds": "",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "registration-uri": "",
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen "related-uris": [],
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen # Determine possible set of properties and lengths.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen props = set()
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for pub in pub_idx:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for sname in pub_idx[pub]:
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen max_sname_len = max(max_sname_len, len(sname))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for pname in pub_idx[pub][sname]:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen max_pname_len = max(max_pname_len, len(pname))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen props.add("/".join((sname, pname)))
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen # Determine properties to display.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen req_props = set(pargs)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if len(req_props) >= 1:
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen found = props & req_props
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen notfound = req_props - found
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen del props
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen else:
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen found = props
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen notfound = set()
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen def gen_listing():
f4bbeadda12fbd7c219063db68f3e78646d83c2cTimo Sirainen for pub in sorted(pub_idx.keys()):
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen for prop in sorted(found):
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen sname, pname = prop.rsplit("/", 1)
abe8754852e70763e92f74caabbcc13d0917714cTimo Sirainen sval = pub_idx[pub][sname][pname]
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen yield {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "publisher": pub,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "section": sname,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "property": pname,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "value": sval,
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen }
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen # PUBLISHER SECTION PROPERTY VALUE
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen # <pub_1> <sec_1> <prop_1> <prop_1_value>
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen # <pub_1> <sec_2> <prop_2> <prop_2_value>
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen # ...
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen field_data = {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen "publisher" : [("default", "json", "tsv"), _("PUBLISHER"), ""],
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen "section" : [("default", "json", "tsv"), _("SECTION"), ""],
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen "property" : [("default", "json", "tsv"), _("PROPERTY"), ""],
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen "value" : [("default", "json", "tsv"), _("VALUE"), ""],
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen }
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen desired_field_order = (_("PUBLISHER"), _("SECTION"), _("PROPERTY"),
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen _("VALUE"))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen # Default output formatting.
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen def_fmt = "{0:" + str(max_pubname_len) + "} {1:" + str(max_sname_len) + \
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen "} {2:" + str(max_pname_len) + "} {3}"
fa39c427573ada77265b833db687ca7e854794a1Timo Sirainen
fa39c427573ada77265b833db687ca7e854794a1Timo Sirainen if found or (not req_props and out_format == "default"):
fa39c427573ada77265b833db687ca7e854794a1Timo Sirainen # print without trailing newline.
6d404348751c19ac37cfb42375abdd3c5f298e30Timo Sirainen sys.stdout.write(misc.get_listing(desired_field_order,
6d404348751c19ac37cfb42375abdd3c5f298e30Timo Sirainen field_data, gen_listing(), out_format, def_fmt,
6d404348751c19ac37cfb42375abdd3c5f298e30Timo Sirainen omit_headers))
6d404348751c19ac37cfb42375abdd3c5f298e30Timo Sirainen
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen if found and notfound:
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen rval = EXIT_PARTIAL
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen if req_props and not found:
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen if out_format == "default":
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen # Don't pollute other output formats.
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen error(_("no matching properties found"),
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen cmd=subcommand)
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen rval = EXIT_OOPS
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen return rval
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainendef subcmd_info(conf, args):
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen """Display a list of known publishers and a summary of known packages
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen and when the package data for the given publisher was last updated.
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen """
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen subcommand = "info"
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen omit_headers = False
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen out_format = "default"
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen pubs = set()
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen key = None
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen cert = None
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen opts, pargs = getopt.getopt(args, "F:Hp:s:", ["key=", "cert="])
5aa1aa87a24ff39d566a3ad90d0f8d054118cfe7Timo Sirainen for opt, arg in opts:
5aa1aa87a24ff39d566a3ad90d0f8d054118cfe7Timo Sirainen if opt == "-F":
5aa1aa87a24ff39d566a3ad90d0f8d054118cfe7Timo Sirainen if arg not in LISTING_FORMATS:
5aa1aa87a24ff39d566a3ad90d0f8d054118cfe7Timo Sirainen raise apx.InvalidOptionError(
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen apx.InvalidOptionError.ARG_INVALID,
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen [arg, opt])
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen out_format = arg
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen elif opt == "-H":
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen omit_headers = True
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen elif opt == "-p":
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen pubs.add(arg)
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen elif opt == "-s":
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen conf["repo_uri"] = parse_uri(arg)
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen elif opt == "--key":
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen key = arg
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen elif opt == "--cert":
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen cert = arg
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen if pargs:
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen usage(_("command does not take operands"), cmd=subcommand)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen # Setup transport so status can be retrieved.
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if not conf.get("repo_uri", None):
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen usage(_("A package repository location must be provided "
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen "using -s."), cmd=subcommand)
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen subcommand=subcommand, ssl_key=key, ssl_cert=cert)
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen # Retrieve repository status information.
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen stat_idx = xport.get_status(xpub)
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen pub_idx = stat_idx.get("repository", {}).get("publishers", {})
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if len(pubs) > 0 and "all" not in pubs:
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen found = set(pub_idx.keys()) & pubs
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen notfound = pubs - found
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen else:
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen found = set(pub_idx.keys())
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen notfound = set()
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen def gen_listing():
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen for pfx in sorted(found):
14175321ddb88619015866978c05a27786ca4814Timo Sirainen pdata = pub_idx[pfx]
14175321ddb88619015866978c05a27786ca4814Timo Sirainen pkg_count = pdata.get("package-count", 0)
14175321ddb88619015866978c05a27786ca4814Timo Sirainen last_update = pdata.get("last-catalog-update", "")
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if last_update:
14175321ddb88619015866978c05a27786ca4814Timo Sirainen # Reformat the date into something more user
14175321ddb88619015866978c05a27786ca4814Timo Sirainen # friendly (and locale specific).
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen last_update = pkg.catalog.basic_ts_to_datetime(
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen last_update)
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen last_update = "{0}Z".format(
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen pkg.catalog.datetime_to_ts(last_update))
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen rstatus = _(pub_idx[pfx].get("status", "online"))
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen yield {
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen "publisher": pfx,
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen "packages": pkg_count,
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen "status": rstatus,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen "updated": last_update,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen }
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen # PUBLISHER PACKAGES STATUS UPDATED
14175321ddb88619015866978c05a27786ca4814Timo Sirainen # <pub_1> <num_uniq_pkgs> <status> <cat_last_modified>
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen # <pub_2> <num_uniq_pkgs> <status> <cat_last_modified>
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen # ...
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen field_data = {
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen "publisher" : [("default", "json", "tsv"), _("PUBLISHER"), ""],
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen "packages" : [("default", "json", "tsv"), _("PACKAGES"), ""],
14175321ddb88619015866978c05a27786ca4814Timo Sirainen "status" : [("default", "json", "tsv"), _("STATUS"), ""],
14175321ddb88619015866978c05a27786ca4814Timo Sirainen "updated" : [("default", "json", "tsv"), _("UPDATED"), ""],
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen }
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen desired_field_order = (_("PUBLISHER"), "", _("PACKAGES"), _("STATUS"),
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen _("UPDATED"))
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen # Default output formatting.
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen pub_len = str(max(
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen [len(desired_field_order[0])] + [len(p) for p in found]
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen ))
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen def_fmt = "{0:" + pub_len + "} {1:8} {2:16} {3}"
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen if found or (not pubs and out_format == "default"):
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen # print without trailing newline.
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen sys.stdout.write(misc.get_listing(desired_field_order,
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen field_data, gen_listing(), out_format, def_fmt,
51d379cbc50242a13462d0fded50e013eb00cc07Timo Sirainen omit_headers))
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen if found and notfound:
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen return EXIT_PARTIAL
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen if pubs and not found:
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen if out_format == "default":
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen # Don't pollute other output formats.
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen error(_("no matching publishers found"),
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen cmd=subcommand)
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen return EXIT_OOPS
5ca745ae8e9903ef5265360e4c882e9314595020Timo Sirainen return EXIT_OK
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainendef subcmd_list(conf, args):
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen """List all packages matching the specified patterns."""
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen subcommand = "list"
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen omit_headers = False
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen out_format = "default"
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen pubs = set()
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen key = None
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen cert = None
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen opts, pargs = getopt.getopt(args, "F:Hp:s:", ["key=", "cert="])
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen for opt, arg in opts:
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen if opt == "-F":
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen if arg not in LISTING_FORMATS:
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen raise apx.InvalidOptionError(
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen apx.InvalidOptionError.ARG_INVALID,
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen [arg, opt])
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen out_format = arg
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen elif opt == "-H":
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen omit_headers = True
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen elif opt == "-p":
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen pubs.add(arg)
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen elif opt == "-s":
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen conf["repo_uri"] = parse_uri(arg)
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen elif opt == "--key":
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen key = arg
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen elif opt == "--cert":
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen cert = arg
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen # Setup transport so configuration can be retrieved.
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen if not conf.get("repo_uri", None):
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen usage(_("A package repository location must be provided "
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen "using -s."), cmd=subcommand)
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen subcommand=subcommand, ssl_key=key, ssl_cert=cert)
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen xpub, out_format=out_format, use_transport=True)
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch if rval == EXIT_OOPS:
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen return rval
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch
b3b813a6473d1210eee94bd60eaa6bafd2131ed3Stephan Bosch refresh_pub(pub_data, xport)
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen listed = {}
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch matched = set()
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch unmatched = set()
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch def gen_listing():
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen collect_attrs = out_format.startswith("json")
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen for pub in sorted(pub_data):
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen cat = pub.catalog
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen for f, states, attrs in cat.gen_packages(
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch collect_attrs=collect_attrs, matched=matched,
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch patterns=pargs, pubs=[pub.prefix],
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch unmatched=unmatched, return_fmris=True):
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch if not listed:
038c2831447440bf0bef89b43dd0968afc298abcStephan Bosch listed["packages"] = True
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen state = None
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen if out_format == "default" or \
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen out_format == "tsv":
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen if pkgdefs.PKG_STATE_OBSOLETE in \
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen states:
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen state = "o"
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen elif pkgdefs.PKG_STATE_RENAMED in \
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen states:
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen state = "r"
5a9bbcb13b2dd2cf9a825c7ed09fe7ee143b1275Timo Sirainen
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen if out_format == "default":
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen fver = str(f.version.get_version(
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen include_build=False))
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen ffmri = str(f.get_fmri(include_build=False))
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen else:
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen fver = str(f.version)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen ffmri = str(f)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen ret = {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen "publisher": f.publisher,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen "name": f.pkg_name,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen "version": fver,
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen "release": str(f.version.release),
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen "build-release":
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen str(f.version.build_release),
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen "branch": str(f.version.branch),
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen "timestamp":
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen str(f.version.timestr),
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen "pkg.fmri": ffmri,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen "short_state": state,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen }
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen for attr in attrs:
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ret[attr] = []
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen for mods in attrs[attr]:
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen d = dict(mods)
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen d["value"] = \
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen attrs[attr][mods]
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen ret[attr].append(d)
54bd0fec0be357266e299466a582f3c9269884e9Timo Sirainen yield ret
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen unmatched.difference_update(matched)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen field_data = {
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "publisher": [("default", "json", "tsv"), _("PUBLISHER"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "name": [("default", "json", "tsv"), _("NAME"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "version": [("default", "json"), _("VERSION"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "release": [("json", "tsv",), _("RELEASE"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "build-release": [("json", "tsv",), _("BUILD RELEASE"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "branch": [("json", "tsv",), _("BRANCH"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "timestamp": [("json", "tsv",), _("PACKAGING DATE"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "pkg.fmri": [("json", "tsv",), _("FMRI"), ""],
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen "short_state": [("default", "tsv"), "O", ""],
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen }
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen desired_field_order = (_("PUBLISHER"), _("NAME"), "O", _("VERSION"),
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen _("SUMMARY"), _("DESCRIPTION"), _("CATEGORIES"), _("RELEASE"),
66d84e6f0ae34a3cf5b8fa8e009d6caf025b6a2aTimo Sirainen _("BUILD RELEASE"), _("BRANCH"), _("PACKAGING DATE"), _("FMRI"),
66d84e6f0ae34a3cf5b8fa8e009d6caf025b6a2aTimo Sirainen _("STATE"))
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen # Default output formatting.
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen max_pub_name_len = str(
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen max(list(len(p) for p in found) + [len(_("PUBLISHER"))]))
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen def_fmt = "{0:" + max_pub_name_len + "} {1:45} {2:1} {3}"
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
b80c329a25b5b07f5e68cd44ef0fef9db6613486Timo Sirainen # print without trailing newline.
b80c329a25b5b07f5e68cd44ef0fef9db6613486Timo Sirainen sys.stdout.write(misc.get_listing(
b80c329a25b5b07f5e68cd44ef0fef9db6613486Timo Sirainen desired_field_order, field_data, gen_listing(),
b80c329a25b5b07f5e68cd44ef0fef9db6613486Timo Sirainen out_format, def_fmt, omit_headers))
73b9c241e7fb6abdccda88e88fbd32e844dbc185Timo Sirainen
73b9c241e7fb6abdccda88e88fbd32e844dbc185Timo Sirainen if not listed and pargs:
73b9c241e7fb6abdccda88e88fbd32e844dbc185Timo Sirainen # No matching packages.
73b9c241e7fb6abdccda88e88fbd32e844dbc185Timo Sirainen logger.error("")
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen if not unmatched:
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen unmatched = pargs
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen error(apx.PackageMatchErrors(unmatched_fmris=unmatched),
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen cmd=subcommand)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return EXIT_OOPS
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen elif unmatched:
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen # One or more patterns didn't match a package from any
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen # publisher; only display the error.
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen logger.error("")
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen error(apx.PackageMatchErrors(unmatched_fmris=unmatched),
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen cmd=subcommand)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return EXIT_PARTIAL
d9ab8a13b51c9d8f4e13e1bf785eeadce6702b3bTimo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return EXIT_OK
5df8396a7cbad0b38b83a86667fb3d4c223f6f7cTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainendef refresh_pub(pub_data, xport):
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen """A helper function to refresh all specified publishers."""
5df8396a7cbad0b38b83a86667fb3d4c223f6f7cTimo Sirainen
d5b3f66491101aba8667369586c95c615cb26ae6Timo Sirainen global tmpdirs
5df8396a7cbad0b38b83a86667fb3d4c223f6f7cTimo Sirainen temp_root = misc.config_temp_root()
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen progtrack = get_tracker()
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen progtrack.set_purpose(progtrack.PURPOSE_LISTING)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen progtrack.refresh_start(pub_cnt=len(pub_data), full_refresh=True,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen target_catalog=False)
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen for pub in pub_data:
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen progtrack.refresh_start_pub(pub)
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen meta_root = tempfile.mkdtemp(dir=temp_root)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen tmpdirs.append(meta_root)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen pub.meta_root = meta_root
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen pub.transport = xport
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen try:
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen pub.refresh(True, True, progtrack=progtrack)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen except apx.TransportError:
6eb0192085073a63ea03443860c412bb5312dd7eTimo Sirainen # Assume that a catalog doesn't exist for the target
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen # publisher and drive on.
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen pass
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen progtrack.refresh_end_pub(pub)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen progtrack.refresh_done()
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainendef subcmd_contents(conf, args):
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen """List package contents."""
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen subcommand = "contents"
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen display_raw = False
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen pubs = set()
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen key = None
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen cert = None
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen attrs = []
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen action_types = []
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen opts, pargs = getopt.getopt(args, "ms:t:", ["key=", "cert="])
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen for opt, arg in opts:
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen if opt == "-s":
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen conf["repo_uri"] = parse_uri(arg)
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen elif opt == "-m":
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen display_raw = True
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen elif opt == "-t":
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen action_types.extend(arg.split(","))
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen elif opt == "--key":
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen key = arg
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen elif opt == "--cert":
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen cert = arg
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen # Setup transport so configuration can be retrieved.
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen if not conf.get("repo_uri", None):
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen usage(_("A package repository location must be provided "
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen "using -s."), cmd=subcommand)
82ea464c113f43aaa2b2e23de334cf3081c332beTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen subcommand=subcommand, ssl_key=key, ssl_cert=cert)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen xpub, use_transport=True)
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if rval == EXIT_OOPS:
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return rval
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen # Default output prints out the raw manifest. The -m option is implicit
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen # for now and supported to make the interface equivalent to pkg
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen # contents.
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if not attrs or display_raw:
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen attrs = ["action.raw"]
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen refresh_pub(pub_data, xport)
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen listed = False
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen matched = set()
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen unmatched = set()
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen manifests = []
89a89730a1dd98edb5c16a5b65f693389eb81124Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen for pub in pub_data:
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen cat = pub.catalog
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen for f, states, attr in cat.gen_packages(matched=matched,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen patterns=pargs, pubs=[pub.prefix],
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen unmatched=unmatched, return_fmris=True):
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if not listed:
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen listed = True
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch manifests.append(xport.get_manifest(f))
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen unmatched.difference_update(matched)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen # Build a generator expression based on whether specific action types
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen # were provided.
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if action_types:
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen # If query is limited to specific action types, use the more
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi # efficient type-based generation mechanism.
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi gen_expr = (
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi (m.fmri, a, None, None, None)
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi for m in manifests
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for a in m.gen_actions_by_types(action_types)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen )
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen else:
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen gen_expr = (
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen (m.fmri, a, None, None, None)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen for m in manifests
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen for a in m.gen_actions()
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen )
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen # Determine if the query returned any results by "peeking" at the first
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen # value returned from the generator expression.
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen try:
d14e62b7b37dc78fcc940aca25042eceb358b156Timo Sirainen got = next(gen_expr)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen except StopIteration:
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen got = None
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen actionlist = []
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen if got:
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen actionlist = itertools.chain([got], gen_expr)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen rval = EXIT_OK
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen if action_types and manifests and not got:
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen logger.error(_(gettext.ngettext("""\
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenpkgrepo: contents: This package contains no actions with the types specified
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenusing the -t option""", """\
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenpkgrepo: contents: These packages contain no actions with the types specified
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenusing the -t option.""", len(pargs))))
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen rval = EXIT_OOPS
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen if manifests and rval == EXIT_OK:
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen lines = misc.list_actions_by_attrs(actionlist, attrs)
for line in lines:
text = ("{0}".format(*line)).rstrip()
if not text:
continue
msg(text)
if unmatched:
if manifests:
logger.error("")
logger.error(_("""\
pkgrepo: contents: no packages matching the following patterns you specified
were found in the repository."""))
logger.error("")
for p in unmatched:
logger.error(" {0}".format(p))
rval = EXIT_OOPS
return rval
def __rebuild_local(subcommand, conf, pubs, build_catalog, build_index):
"""In an attempt to allow operations on potentially corrupt
repositories, 'local' repositories (filesystem-basd ones) are handled
separately."""
repo = get_repo(conf, allow_invalid=build_catalog, read_only=False,
subcommand=subcommand)
rpubs = set(repo.publishers)
if not pubs:
found = rpubs
else:
found = rpubs & pubs
notfound = pubs - found
rval = EXIT_OK
if found and notfound:
rval = EXIT_PARTIAL
elif pubs and not found:
error(_("no matching publishers found"), cmd=subcommand)
return EXIT_OOPS
logger.info("Initiating repository rebuild.")
for pfx in found:
repo.rebuild(build_catalog=build_catalog,
build_index=build_index, pub=pfx)
return rval
def __rebuild_remote(subcommand, conf, pubs, key, cert, build_catalog,
build_index):
def do_rebuild(xport, xpub):
if build_catalog and build_index:
xport.publish_rebuild(xpub)
elif build_catalog:
xport.publish_rebuild_packages(xpub)
elif build_index:
xport.publish_rebuild_indexes(xpub)
xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
subcommand=subcommand, ssl_key=key, ssl_cert=cert)
rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
xpub)
if rval == EXIT_OOPS:
return rval
logger.info("Initiating repository rebuild.")
for pfx in found:
xpub.prefix = pfx
do_rebuild(xport, xpub)
return rval
def subcmd_rebuild(conf, args):
"""Rebuild the repository's catalog and index data (as permitted)."""
subcommand = "rebuild"
build_catalog = True
build_index = True
key = None
cert = None
opts, pargs = getopt.getopt(args, "p:s:", ["no-catalog", "no-index",
"key=", "cert="])
pubs = set()
for opt, arg in opts:
if opt == "-p":
if not misc.valid_pub_prefix(arg):
error(_("Invalid publisher prefix '{0}'").format(
arg), cmd=subcommand)
pubs.add(arg)
elif opt == "-s":
conf["repo_uri"] = parse_uri(arg)
elif opt == "--no-catalog":
build_catalog = False
elif opt == "--no-index":
build_index = False
elif opt == "--key":
key = arg
elif opt == "--cert":
cert = arg
if pargs:
usage(_("command does not take operands"), cmd=subcommand)
if not build_catalog and not build_index:
# Why? Who knows; but do what was requested--nothing!
return EXIT_OK
# Setup transport so operation can be performed.
if not conf.get("repo_uri", None):
usage(_("A package repository location must be provided "
"using -s."), cmd=subcommand)
if conf["repo_uri"].scheme == "file":
return __rebuild_local(subcommand, conf, pubs, build_catalog,
build_index)
return __rebuild_remote(subcommand, conf, pubs, key, cert,
build_catalog, build_index)
def subcmd_refresh(conf, args):
"""Refresh the repository's catalog and index data (as permitted)."""
subcommand = "refresh"
add_content = True
refresh_index = True
key = None
cert = None
opts, pargs = getopt.getopt(args, "p:s:", ["no-catalog", "no-index",
"key=", "cert="])
pubs = set()
for opt, arg in opts:
if opt == "-p":
if not misc.valid_pub_prefix(arg):
error(_("Invalid publisher prefix '{0}'").format(
arg), cmd=subcommand)
pubs.add(arg)
elif opt == "-s":
conf["repo_uri"] = parse_uri(arg)
elif opt == "--no-catalog":
add_content = False
elif opt == "--no-index":
refresh_index = False
elif opt == "--key":
key = arg
elif opt == "--cert":
cert = arg
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
# Setup transport so operation can be performed.
if not conf.get("repo_uri", None):
usage(_("A package repository location must be provided "
"using -s."), cmd=subcommand)
def do_refresh(xport, xpub):
if add_content and refresh_index:
xport.publish_refresh(xpub)
elif add_content:
xport.publish_refresh_packages(xpub)
elif refresh_index:
xport.publish_refresh_indexes(xpub)
xport, xpub, tmp_dir = setup_transport(conf.get("repo_uri"),
subcommand=subcommand, ssl_key=key, ssl_cert=cert)
rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
xpub)
if rval == EXIT_OOPS:
return rval
logger.info("Initiating repository refresh.")
for pfx in found:
xpub.prefix = pfx
do_refresh(xport, xpub)
return rval
def subcmd_set(conf, args):
"""Set repository properties."""
subcommand = "set"
pubs = set()
opts, pargs = getopt.getopt(args, "p:s:")
for opt, arg in opts:
if opt == "-p":
pubs.add(arg)
elif opt == "-s":
conf["repo_uri"] = parse_uri(arg)
bad_args = False
props = {}
if not pargs:
bad_args = True
else:
for arg in pargs:
try:
# Attempt to parse property into components.
prop, val = arg.split("=", 1)
sname, pname = prop.rsplit("/", 1)
# Store property values by section.
props.setdefault(sname, {})
# Parse the property value into a list if
# necessary, otherwise append it to the list
# of values for the property.
if len(val) > 0 and val[0] == "(" and \
val[-1] == ")":
val = shlex.split(val.strip("()"))
if sname in props and pname in props[sname]:
# Determine if previous value is already
# a list, and if not, convert and append
# the value.
pval = props[sname][pname]
if not isinstance(pval, list):
pval = [pval]
if isinstance(val, list):
pval.extend(val)
else:
pval.append(val)
props[sname][pname] = pval
else:
# Otherwise, just store the value.
props[sname][pname] = val
except ValueError:
bad_args = True
break
if bad_args:
usage(_("a property name and value must be provided in the "
"form <section/property>=<value> or "
"<section/property>=([\"<value>\" ...])"))
# Get repository object.
if not conf.get("repo_uri", None):
usage(_("A package repository location must be provided "
"using -s."), cmd=subcommand)
repo = get_repo(conf, read_only=False, subcommand=subcommand)
# Set properties.
if pubs:
return _set_pub(conf, subcommand, props, pubs, repo)
return _set_repo(conf, subcommand, props, repo)
def _set_pub(conf, subcommand, props, pubs, repo):
"""Set publisher properties."""
for sname, sprops in six.iteritems(props):
if sname not in ("publisher", "repository"):
usage(_("unknown property section "
"'{0}'").format(sname), cmd=subcommand)
for pname in sprops:
if sname == "publisher" and pname == "prefix":
usage(_("'{0}' may not be set using "
"this command".format(pname)))
attrname = pname.replace("-", "_")
if not hasattr(publisher.Publisher, attrname) and \
not hasattr(publisher.Repository, attrname):
usage(_("unknown property '{0}'").format(
pname), cmd=subcommand)
if "all" in pubs:
# Default to list of all publishers.
pubs = repo.publishers
if not pubs:
# If there are still no known publishers, this
# operation cannot succeed, so fail now.
usage(_("One or more publishers must be specified to "
"create and set properties for as none exist yet."),
cmd=subcommand)
# Get publishers and update properties.
failed = []
new_pub = False
for pfx in pubs:
try:
# Get a copy of the existing publisher.
pub = copy.copy(repo.get_publisher(pfx))
except sr.RepositoryUnknownPublisher as e:
pub = publisher.Publisher(pfx)
new_pub = True
except sr.RepositoryError as e:
failed.append((pfx, e))
continue
try:
# Set/update the publisher's properties.
for sname, sprops in six.iteritems(props):
if sname == "publisher":
target = pub
elif sname == "repository":
target = pub.repository
if not target:
target = publisher.Repository()
pub.repository = target
for pname, val in six.iteritems(sprops):
attrname = pname.replace("-", "_")
pval = getattr(target, attrname)
if isinstance(pval, list) and \
not isinstance(val, list):
# If the target property expects
# a list, transform the provided
# value into one if it isn't
# already.
if val == "":
val = []
else:
val = [val]
setattr(target, attrname, val)
except apx.ApiException as e:
failed.append((pfx, e))
continue
if new_pub:
repo.add_publisher(pub)
else:
repo.update_publisher(pub)
if failed:
for pfx, details in failed:
error(_("Unable to set properties for publisher "
"'{pfx}':\n{details}").format(**locals()))
if len(failed) < len(pubs):
return EXIT_PARTIAL
return EXIT_OOPS
return EXIT_OK
def _set_repo(conf, subcommand, props, repo):
"""Set repository properties."""
# Set properties.
for sname, props in six.iteritems(props):
for pname, val in six.iteritems(props):
repo.cfg.set_property(sname, pname, val)
repo.write_config()
return EXIT_OK
def subcmd_version(conf, args):
"""Display the version of the pkg(7) API."""
subcommand = "version"
if args:
usage(_("command does not take operands"), cmd=subcommand)
msg(pkg.VERSION)
return EXIT_OK
verify_error_header = None
verify_warning_header = None
verify_reason_headers = None
def __load_verify_msgs():
"""Since our gettext isn't loaded we need to ensure our globals have
correct content by calling this method. These values are used by both
fix when in verbose mode, and verify"""
global verify_error_header
global verify_warning_header
global verify_reason_headers
# A map of error detail types to the human-readable description of each
# type. These correspond to keys in the dictionary returned by
# sr.Repository.verify(..)
verify_reason_headers = {
"path": _("Repository path"),
"actual": _("Computed hash"),
"fpath": _("Path"),
"permissionspath": _("Path"),
"pkg": _("Package"),
"depend": _("Dependency"),
"type":_("Dependency type"),
"err": _("Detail")
}
verify_error_header = _("ERROR")
verify_warning_header = _("WARNING")
def __fmt_verify(verify_tuple):
"""Format a verify_tuple, of the form (error, path, message, reason)
returning a formatted error message, and an FMRI indicating what
packages within the repository are affected. Note that the returned FMRI
may not be valid, in which case a path to the broken manifest in the
repository is returned instead."""
error, path, message, reason = verify_tuple
formatted_message = "{error_type:>16}: {message}\n".format(
error_type=verify_error_header, message=message)
reason["path"] = path
if error == sr.REPO_VERIFY_BADMANIFEST:
reason_keys = ["path", "err"]
elif error in [sr.REPO_VERIFY_PERM, sr.REPO_VERIFY_MFPERM]:
reason_keys = ["pkg", "path"]
elif error == sr.REPO_VERIFY_BADHASH:
reason_keys = ["pkg", "path", "actual", "fpath"]
elif error == sr.REPO_VERIFY_UNKNOWN:
reason_keys = ["path", "err"]
elif error == sr.REPO_VERIFY_BADSIG:
reason_keys = ["pkg", "path", "err"]
elif error == sr.REPO_VERIFY_DEPENDERROR:
reason_keys = ["pkg", "depend", "type"]
elif error == sr.REPO_VERIFY_WARN_OPENPERMS:
formatted_message = \
"{error_type:>16}: {message}\n".format(
error_type=verify_warning_header, message=message)
reason_keys = ["permissionspath", "err"]
else:
# A list of the details we provide. Some error codes
# have different details associated with them.
reason_keys = ["pkg", "path", "fpath"]
# the detailed error message can be long, so we'll wrap it. If what we
# have fits on a single line, use it, otherwise begin displaying the
# message on the next line.
if "err" in reason_keys:
err_str = ""
lines = textwrap.wrap(reason["err"])
if len(lines) != 1:
for line in lines:
err_str += "{0:>18}\n".format(line)
reason["err"] = "\n" + err_str.rstrip()
else:
reason["err"] = lines[0]
for key in reason_keys:
# sometimes we don't have the key we want, for example we may
# not have a file path from the package if the error is a
# missing repository file for a 'license' action (which don't
# have 'path' attributes, hence no 'fpath' dictionary entry)
if key not in reason:
continue
formatted_message += "{key:>16}: {value}\n".format(
key=verify_reason_headers[key], value=reason[key])
formatted_message += "\n"
if error == sr.REPO_VERIFY_WARN_OPENPERMS:
return formatted_message, None
elif "depend" in reason:
return formatted_message, reason["depend"]
elif "pkg" in reason:
return formatted_message, reason["pkg"]
return formatted_message, reason["path"]
def __collect_default_ignore_dep_files(ignored_dep_files):
"""Helpler function to collect default ignored-dependency files."""
root_ignored = "/usr/share/pkg/ignored_deps"
altroot = DebugValues.get_value("ignored_deps")
if altroot:
root_ignored = altroot
if os.path.exists(root_ignored):
igfiles = os.listdir(root_ignored)
for igf in igfiles:
ignored_dep_files.append(os.path.join(root_ignored,
igf))
def subcmd_verify(conf, args):
"""Verify the repository content (file, manifest content and
dependencies only)."""
subcommand = "verify"
__load_verify_msgs()
opts, pargs = getopt.getopt(args, "dp:s:i:", ["disable="])
allowed_checks = set(sr.verify_default_checks)
force_dep_check = False
ignored_dep_files = []
pubs = set()
for opt, arg in opts:
if opt == "-s":
conf["repo_uri"] = parse_uri(arg)
elif opt == "-p":
if not misc.valid_pub_prefix(arg):
error(_("Invalid publisher prefix '{0}'").format(
arg), cmd=subcommand)
pubs.add(arg)
elif opt == "-d":
force_dep_check = True
elif opt == "--disable":
arg = arg.lower()
if arg in sr.verify_default_checks:
if arg in allowed_checks:
allowed_checks.remove(arg)
else:
usage(_("Invalid verification to be disabled, "
"please consider: {0}").format(", ".join(
sr.verify_default_checks)), cmd=subcommand)
elif opt == "-i":
ignored_dep_files.append(arg)
if pargs:
usage(_("command does not take operands"), cmd=subcommand)
repo_uri = conf.get("repo_uri", None)
if not repo_uri:
usage(_("A package repository location must be provided "
"using -s."), cmd=subcommand)
if repo_uri.scheme != "file":
usage(_("Network repositories are not currently supported "
"for this operation."), cmd=subcommand)
if sr.VERIFY_DEPENDENCY not in allowed_checks and \
(force_dep_check or len(ignored_dep_files) > 0):
usage(_("-d or -i option cannot be used when dependency "
"verification is disabled."), cmd=subcommand)
xport, xpub, tmp_dir = setup_transport(repo_uri, subcommand=subcommand)
rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
xpub)
if rval == EXIT_OOPS:
return rval
logger.info("Initiating repository verification.")
bad_fmris = set()
progtrack = get_tracker()
def report_error(verify_tuple):
message, bad_fmri = __fmt_verify(verify_tuple)
if bad_fmri:
bad_fmris.add(bad_fmri)
progtrack.repo_verify_yield_error(bad_fmri, message)
if sr.VERIFY_DEPENDENCY in allowed_checks or not force_dep_check:
__collect_default_ignore_dep_files(ignored_dep_files)
repo = sr.Repository(root=repo_uri.get_pathname())
found_pubs = []
for pfx in found:
xport, xpub, tmp_dir = setup_transport(repo_uri, prefix=pfx,
remote_prefix=False,
subcommand=subcommand)
xpub.transport = xport
found_pubs.append(xpub)
for verify_tuple in repo.verify(pubs=found_pubs,
allowed_checks=allowed_checks, force_dep_check=force_dep_check,
ignored_dep_files=ignored_dep_files, progtrack=progtrack):
report_error(verify_tuple)
if bad_fmris:
return EXIT_OOPS
return EXIT_OK
def subcmd_fix(conf, args):
"""Fix the repository content (file and manifest content only)
For index and catalog content corruption, a rebuild should be
performed."""
subcommand = "fix"
__load_verify_msgs()
verbose = False
# Dependency verification. Note fix will not force dependency check.
force_dep_check = False
ignored_dep_files = []
opts, pargs = getopt.getopt(args, "vp:s:")
pubs = set()
for opt, arg in opts:
if opt == "-s":
conf["repo_uri"] = parse_uri(arg)
if opt == "-v":
verbose = True
if opt == "-p":
if not misc.valid_pub_prefix(arg):
error(_("Invalid publisher prefix '{0}'").format(
arg), cmd=subcommand)
pubs.add(arg)
if pargs:
usage(_("command does not take operands"), cmd=subcommand)
repo_uri = conf.get("repo_uri", None)
if not repo_uri:
usage(_("A package repository location must be provided "
"using -s."), cmd=subcommand)
if repo_uri.scheme != "file":
usage(_("Network repositories are not currently supported "
"for this operation."), cmd=subcommand)
xport, xpub, tmp_dir = setup_transport(repo_uri, subcommand=subcommand)
rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
xpub)
if rval == EXIT_OOPS:
return rval
logger.info("Initiating repository fix.")
def verify_cb(tracker, verify_tuple):
"""A method passed to sr.Repository.fix(..) to emit verify
messages if verbose mode is enabled."""
if not verbose:
return
formatted_message, bad_fmri = __fmt_verify(verify_tuple)
tracker.repo_verify_yield_error(bad_fmri, formatted_message)
repo = sr.Repository(root=repo_uri.get_pathname())
bad_deps = set()
broken_fmris = set()
failed_fix_paths = set()
progtrack = get_tracker()
__collect_default_ignore_dep_files(ignored_dep_files)
found_pubs = []
for pfx in found:
xport, xpub, tmp_dir = setup_transport(repo_uri, prefix=pfx,
remote_prefix=False,
subcommand=subcommand)
xpub.transport = xport
found_pubs.append(xpub)
for status_code, path, message, reason in \
repo.fix(pubs=found_pubs, force_dep_check=force_dep_check,
ignored_dep_files=ignored_dep_files,
progtrack=progtrack,
verify_callback=verify_cb):
if status_code == sr.REPO_FIX_ITEM:
# When we can't get the FMRI, eg. in the case
# of a corrupt manifest, use the path instead.
fmri = reason["pkg"]
if not fmri:
fmri = path
broken_fmris.add(fmri)
if verbose:
progtrack.repo_fix_yield_info(fmri,
message)
elif status_code == sr.REPO_VERIFY_DEPENDERROR:
bad_deps.add(reason["depend"])
else:
failed_fix_paths.add(path)
progtrack.flush()
logger.info("")
if broken_fmris:
logger.info(_("Use pkgsend(1) or pkgrecv(1) to republish the\n"
"following packages or paths which were quarantined:\n\n\t"
"{0}").format(
"\n\t".join([str(f) for f in broken_fmris])))
if failed_fix_paths:
logger.info(_("\npkgrepo could not repair the following paths "
"in the repository:\n\n\t{0}").format(
"\n\t".join([p for p in failed_fix_paths])))
if bad_deps:
logger.info(_("\npkgrepo could not repair the following "
"dependency issues in the repository:\n\n\t{0}").format(
"\n\t".join([p for p in bad_deps])))
if not (broken_fmris or failed_fix_paths or bad_deps):
logger.info(_("No repository fixes required."))
else:
logger.info(_("Repository repairs completed."))
if failed_fix_paths or bad_deps:
return EXIT_OOPS
return EXIT_OK
def __get_pub_fmris(pub, xport, tmp_dir):
if not pub.meta_root:
# Create a temporary directory for catalog.
cat_dir = tempfile.mkdtemp(prefix="pkgrepo-diff.", dir=tmp_dir)
pub.meta_root = cat_dir
pub.transport = xport
pub.refresh(full_refresh=True, immediate=True)
pkgs, fmris, unmatched = pub.catalog.get_matching_fmris("*")
fmris = [f for f in fmris]
return fmris, pkgs
def __format_diff(diff_type, subject):
"""formatting diff output.
diff_type: can be MINUS, PLUS or COMMON.
subject: can be a publisher or a package.
"""
format_pub = "{0}{1}"
format_fmri = " {0}{1}"
format_str = " {0}{1}"
text = ""
if isinstance(subject, publisher.Publisher):
text = format_pub.format(diff_type_f[diff_type],
subject.prefix)
elif isinstance(subject, fmri.PkgFmri):
text = format_fmri.format(diff_type_f[diff_type],
str(subject))
else:
text = format_str.format(diff_type_f[diff_type],
subject)
return text
def __sorted(subject, stype=None):
if stype == "pub":
skey = operator.attrgetter("prefix")
return sorted(subject, key=skey)
return sorted(subject)
def __emit_msg(diff_type, subject):
text = __format_diff(diff_type, subject)
msg(text)
def __repo_diff(conf, pubs, xport, rpubs, rxport, tmp_dir, verbose, quiet,
compare_ts, compare_cat, parsable):
"""Determine the differences between two repositories."""
same_repo = True
if conf["repo_uri"].scheme == "file":
conf["repo_uri"] = conf["repo_uri"].get_pathname()
if conf["com_repo_uri"].scheme == "file":
conf["com_repo_uri"] = conf["com_repo_uri"].get_pathname()
foundpfx = set([pub.prefix for pub in pubs])
rfoundpfx = set([pub.prefix for pub in rpubs])
minus_pfx = __sorted(foundpfx - rfoundpfx)
minus_pubs = __sorted([pub for pub in pubs if pub.prefix in minus_pfx],
stype="pub")
plus_pfx = __sorted(rfoundpfx - foundpfx)
plus_pubs = __sorted([pub for pub in rpubs if pub.prefix in plus_pfx],
stype="pub")
if minus_pubs or plus_pubs:
same_repo = False
if quiet:
return EXIT_DIFF
pcommon_set = foundpfx & rfoundpfx
common_pubs = __sorted([p for p in pubs if p.prefix in pcommon_set],
stype="pub")
common_rpubs = __sorted([p for p in rpubs if p.prefix in pcommon_set],
stype="pub")
res_dict = {"table_legend": [["Repo1", str(conf["repo_uri"])],
["Repo2", str(conf["com_repo_uri"])]],
"table_header": [_("Publisher"),
# This is a table column header which tells that this
# row shows number of packages found in specific
# repository only.
# Use terse translation to avoid too-wide header.
_("{repo} only").format(repo="Repo1"),
_("{repo} only").format(repo="Repo2"),
# This is a table column header which tells that this
# row shows number of packages found in both
# repositories being compared together.
# Use terse translation to avoid too-wide header.
_("In both"), _("Total")],
# Row based table contents.
"table_data": []
}
verbose_res_dict = {"plus_pubs": [], "minus_pubs": [],
"common_pubs": []}
def __diff_pub_helper(pub, symbol):
fmris, pkgs = __get_pub_fmris(pub, xport, tmp_dir)
# Summary level.
if not verbose:
td_row = [pub.prefix,
{"packages": len(pkgs), "versions": len(fmris)},
None, {"packages": 0, "versions": 0},
{"packages": len(pkgs), "versions": len(fmris)}]
if symbol == PLUS:
td_row[1], td_row[2] = td_row[2], td_row[1]
res_dict["table_data"].append(td_row)
return
if parsable:
key_name = "minus_pubs"
if symbol == PLUS:
key_name = "plus_pubs"
verbose_res_dict[key_name].append(
{"publisher": pub.prefix, "packages": len(pkgs),
"versions": len(fmris)})
return
__emit_msg(symbol, pub)
__emit_msg(symbol, _("({0:d} package(s) with "
"{1:d} different version(s))").format(len(pkgs),
len(fmris)))
for pub in minus_pubs:
__diff_pub_helper(pub, MINUS)
for pub in plus_pubs:
__diff_pub_helper(pub, PLUS)
for pub, rpub in zip(common_pubs, common_rpubs):
# Indicates whether those two pubs have same pkgs.
same_pkgs = True
same_cat = True
fmris, pkgs = __get_pub_fmris(pub, xport, tmp_dir)
rfmris, rpkgs = __get_pub_fmris(rpub, rxport, tmp_dir)
fmris_str = set([str(f) for f in fmris])
rfmris_str = set([str(f) for f in rfmris])
del fmris, rfmris
minus_fmris = __sorted(fmris_str - rfmris_str)
plus_fmris = __sorted(rfmris_str - fmris_str)
if minus_fmris or plus_fmris:
same_repo = False
same_pkgs = False
if quiet:
return EXIT_DIFF
cat_lm_pub = None
cat_lm_rpub = None
if compare_cat:
cat_lm_pub = pub.catalog.last_modified.isoformat()
cat_lm_rpub = rpub.catalog.last_modified.isoformat()
same_cat = same_repo = cat_lm_pub == cat_lm_rpub
if not same_cat and quiet:
return EXIT_DIFF
common_fmris = fmris_str & rfmris_str
pkg_set = set(pkgs.keys())
rpkg_set = set(rpkgs.keys())
del pkgs, rpkgs
common_pkgs = pkg_set & rpkg_set
# Print summary.
if not verbose:
if not same_cat:
# Common publishers with different catalog
# modification time.
res_dict.setdefault("nonstrict_pubs", []
).append(pub.prefix)
# Add to the table only if there are differences
# for this publisher.
if not same_pkgs:
minus_pkgs = pkg_set - rpkg_set
minus_pkg_vers = {"packages": len(minus_pkgs),
"versions": len(minus_fmris)}
del minus_pkgs, minus_fmris
plus_pkgs = rpkg_set - pkg_set
plus_pkg_vers = {"packages": len(plus_pkgs),
"versions": len(plus_fmris)}
del plus_pkgs, plus_fmris
total_pkgs = pkg_set | rpkg_set
total_fmris = fmris_str | rfmris_str
total_pkg_vers = {"packages": len(total_pkgs),
"versions": len(total_fmris)}
del total_pkgs, total_fmris
com_pkg_vers = {"packages": len(common_pkgs),
"versions": len(common_fmris)}
res_dict["table_data"].append([pub.prefix,
minus_pkg_vers, plus_pkg_vers,
com_pkg_vers,
total_pkg_vers])
del common_pkgs, common_fmris, pkg_set, rpkg_set
continue
com_pub_info = {}
# Emit publisher name if there are differences.
if not same_pkgs or not same_cat:
if parsable:
com_pub_info["publisher"] = pub.prefix
com_pub_info["+"] = []
com_pub_info["-"] = []
else:
__emit_msg(COMMON, pub)
# Emit catalog differences.
if not same_cat:
omsg = _("catalog last modified: {0}")
minus_cat = omsg.format(cat_lm_pub)
plus_cat = omsg.format(cat_lm_rpub)
if parsable:
com_pub_info["catalog"] = {"-": minus_cat,
"+": plus_cat}
else:
__emit_msg(MINUS, minus_cat)
__emit_msg(PLUS, plus_cat)
for f in minus_fmris:
if parsable:
com_pub_info["-"].append(str(f))
else:
__emit_msg(MINUS, f)
del minus_fmris
for f in plus_fmris:
if parsable:
com_pub_info["+"].append(str(f))
else:
__emit_msg(PLUS, f)
del plus_fmris
if not same_pkgs:
if parsable:
com_pub_info["common"] = {
"packages": len(common_pkgs),
"versions": len(common_fmris)}
else:
msg(_(" ({0:d} pkg(s) with {1:d} "
"version(s) are in both repositories.)"
).format(len(common_pkgs),
len(common_fmris)))
del common_pkgs, common_fmris, pkg_set, rpkg_set
if com_pub_info:
verbose_res_dict["common_pubs"].append(com_pub_info)
if same_repo:
# Same repo. Will use EXIT_OK to represent.
return EXIT_OK
if verbose:
if parsable:
msg(json.dumps(verbose_res_dict))
return EXIT_DIFF
if not parsable:
ftemp = "{0:d} [{1:{2}d}]"
if "nonstrict_pubs" in res_dict and res_dict["nonstrict_pubs"]:
msg("")
msg(_("The catalog for the following publisher(s) "
"in repository {0} is not an exact copy of the "
"one for the same publisher in repository {1}:"
"\n {2}").format(conf["repo_uri"],
conf["com_repo_uri"],
", ".join(res_dict["nonstrict_pubs"])))
if res_dict["table_data"]:
info_table = PrettyTable(res_dict["table_header"],
encoding=locale.getpreferredencoding())
info_table.align = "r"
info_table.align[misc.force_text(_("Publisher"),
locale.getpreferredencoding())] = "l"
# Calculate column wise maximum number for formatting.
col_maxs = 4 * [0]
for td in res_dict["table_data"]:
for idx, cell in enumerate(td):
if idx > 0 and isinstance(cell, dict):
col_maxs[idx-1] = max(
col_maxs[idx-1],
cell["versions"])
for td in res_dict["table_data"]:
t_row = []
for idx, cell in enumerate(td):
if not cell:
t_row.append("-")
elif isinstance(cell, six.string_types):
t_row.append(cell)
elif isinstance(cell, dict):
t_row.append(ftemp.format(
cell["packages"],
cell["versions"], len(str(
col_maxs[idx-1]))))
info_table.add_row(t_row)
# This message explains that each cell of the table
# shows two numbers in a format e.g. "4870 [10227]".
# Here "number of packages" and "total distinct
# versions" are shown outside and inside of square
# brackets respectively.
msg(_("""
The table below shows the number of packages [total distinct versions]
by publisher in the specified repositories.
"""))
for leg in res_dict["table_legend"]:
msg("* " + leg[0] + ": " + leg[1])
msg("")
msg(info_table)
else:
msg(json.dumps(res_dict))
return EXIT_DIFF
def subcmd_diff(conf, args):
"""Compare two repositories."""
opts, pargs = getopt.getopt(args, "vqp:s:", ["strict", "parsable",
"key=", "cert="])
subcommand = "diff"
pubs = set()
verbose = 0
quiet = False
compare_ts = True
compare_cat = False
parsable = False
def key_cert_conf_helper(conf_type, arg):
"""Helper function for collecting key and cert."""
if conf.get("repo_uri") and not conf.get("com_repo_uri"):
conf["repo_" + conf_type] = arg
elif conf.get("com_repo_uri"):
conf["com_repo_" + conf_type] = arg
else:
usage(_("--{0} must be specified following a "
"-s").format(conf_type), cmd=subcommand)
for opt, arg in opts:
if opt == "-s":
if "repo_uri" not in conf:
conf["repo_uri"] = parse_uri(arg)
elif "com_repo_uri" not in conf:
conf["com_repo_uri"] = parse_uri(arg)
else:
usage(_("only two repositories can be "
"specified"), cmd=subcommand)
if opt == "-v":
verbose += 1
elif opt == "-q":
quiet = True
elif opt == "--strict":
compare_cat = True
elif opt == "--parsable":
parsable = True
elif opt == "-p":
if not misc.valid_pub_prefix(arg):
error(_("Invalid publisher prefix '{0}'").format(
arg), cmd=subcommand)
return EXIT_OOPS
pubs.add(arg)
elif opt == "--key":
key_cert_conf_helper("key", arg)
elif opt == "--cert":
key_cert_conf_helper("cert", arg)
if len(pargs) > 0:
usage(_("command does not take any operands"), cmd=subcommand)
if quiet and verbose:
usage(_("-q and -v can not be combined"), cmd=subcommand)
repo_uri = conf.get("repo_uri")
if not repo_uri:
usage(_("Two package repository locations must be provided "
"using -s."), cmd=subcommand)
com_repo_uri = conf.get("com_repo_uri")
if not com_repo_uri:
usage(_("A second package repository location must also be "
"provided using -s."), cmd=subcommand)
xport, xpub, tmp_dir = setup_transport(repo_uri, subcommand=subcommand,
ssl_key=conf.get("repo_key"), ssl_cert=conf.get("repo_cert"))
cxport, cxpub, c_tmp_dir = setup_transport(com_repo_uri,
subcommand=subcommand, prefix="com",
ssl_key=conf.get("com_repo_key"),
ssl_cert=conf.get("com_repo_cert"))
rval, found, pub_data = _get_matching_pubs(subcommand, pubs, xport,
xpub, use_transport=True, repo_uri=repo_uri)
if rval == EXIT_OOPS:
return rval
rval, cfound, cpub_data = _get_matching_pubs(subcommand, pubs, cxport,
cxpub, use_transport=True, repo_uri=com_repo_uri)
if rval == EXIT_OOPS:
return rval
return __repo_diff(conf, pub_data, xport, cpub_data, cxport, tmp_dir,
verbose, quiet, compare_ts, compare_cat, parsable)
def main_func():
global_settings.client_name = PKG_CLIENT_NAME
try:
opts, pargs = getopt.getopt(sys.argv[1:], "s:D:?",
["help", "debug="])
except getopt.GetoptError as e:
usage(_("illegal global option -- {0}").format(e.opt))
conf = {}
show_usage = False
for opt, arg in opts:
if opt == "-s":
conf["repo_uri"] = parse_uri(arg)
elif opt in ("--help", "-?"):
show_usage = True
elif opt == "-D" or opt == "--debug":
try:
key, value = arg.split("=", 1)
except (AttributeError, ValueError):
usage(_("{opt} takes argument of form "
"name=value, not {arg}").format(
opt=opt, arg=arg))
DebugValues.set_value(key, value)
if DebugValues:
reload(pkg.digest)
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_{0}".format(subcommand), None)
if not func:
subcommand = subcommand.replace("_", "-")
usage(_("unknown subcommand '{0}'").format(subcommand))
try:
return func(conf, pargs)
except getopt.GetoptError as e:
if e.opt in ("help", "?"):
usage(full=True)
usage(_("illegal option -- {0}").format(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 = misc.get_traceback_message()
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) as __e:
if isinstance(__e, EnvironmentError) and \
__e.errno != errno.ENOMEM:
raise
error("\n" + misc.out_of_memory())
__ret = EXIT_OOPS
except SystemExit as __e:
raise __e
except (IOError, PipeError, KeyboardInterrupt) as __e:
# Don't display any messages here to prevent possible further
# broken pipe (EPIPE) errors.
if isinstance(__e, IOError) and __e.errno != errno.EPIPE:
error(str(__e))
__ret = EXIT_OOPS
except apx.VersionException as __e:
error(_("The pkgrepo command appears out of sync with the "
"libraries provided\nby pkg:/package/pkg. The client "
"version is {client} while the library\nAPI version is "
"{api}.").format(client=__e.received_version,
api=__e.expected_version))
__ret = EXIT_OOPS
except apx.BadRepositoryURI as __e:
error(str(__e))
__ret = EXIT_BADOPT
except apx.InvalidOptionError as __e:
error("{0} Supported formats: {1}".format(
str(__e), LISTING_FORMATS))
__ret = EXIT_BADOPT
except (apx.ApiException, sr.RepositoryError) as __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",
codeset=locale.getpreferredencoding())
misc.set_fd_limits(printer=error)
# Make all warnings be errors.
warnings.simplefilter('error')
if six.PY3:
# disable ResourceWarning: unclosed file
warnings.filterwarnings("ignore", category=ResourceWarning)
__retval = handle_errors(main_func)
try:
logging.shutdown()
except IOError:
# Ignore python's spurious pipe problems.
pass
sys.exit(__retval)