5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
24 # Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
27 PKG_CLIENT_NAME
= "pkgrepo"
36 LISTING_FORMATS
= ("default", "json", "json-formatted", "tsv")
59 from pkg
.client
import global_settings
60 from pkg
.client
.debugvalues
import DebugValues
61 from pkg
.misc
import msg
, PipeError
64 import pkg
.client
.api_errors
as apx
65 import pkg
.client
.pkgdefs
as pkgdefs
66 import pkg
.client
.progress
67 import pkg
.client
.publisher
as publisher
68 import pkg
.client
.transport
.transport
as transport
69 import pkg
.misc
as misc
70 import pkg
.server
.repository
as sr
72 logger
= global_settings
.logger
76 """To be called at program finish."""
78 shutil
.rmtree(d
, True)
81 def error(text
, cmd
=None):
82 """Emit an error message prefixed by the command name """
85 text
= "{0}: {1}".format(cmd
, text
)
90 # If we get passed something like an Exception, we can convert
91 # it down to a string.
94 # If the message starts with whitespace, assume that it should come
95 # *before* the command-name prefix.
96 text_nows
= text
.lstrip()
97 ws
= text
[:len(text
) - len(text_nows
)]
99 # This has to be a constant value as we can't reliably get our actual
100 # program name on all platforms.
101 logger
.error(ws
+ pkg_cmd
+ text_nows
)
104 def get_tracker(quiet
=False):
106 progtrack
= pkg
.client
.progress
.QuietProgressTracker()
110 pkg
.client
.progress
.FancyUNIXProgressTracker()
111 except pkg
.client
.progress
.ProgressTrackerException
:
113 pkg
.client
.progress
.CommandLineProgressTracker()
117 def usage(usage_error
=None, cmd
=None, retcode
=2, full
=False):
118 """Emit a usage message and optionally prefix it with a more
119 specific error message. Causes program to exit.
123 error(usage_error
, cmd
=cmd
)
126 # The full usage message isn't desired.
127 logger
.error(_("Try `pkgrepo --help or -?' for more "
133 pkgrepo [options] command [cmd_options] [operands]
136 pkgrepo create [--version ver] uri_or_path
138 pkgrepo add-publisher -s repo_uri_or_path publisher ...
140 pkgrepo remove-publisher [-n] [--synchronous] -s repo_uri_or_path
143 pkgrepo get [-F format] [-p publisher ...] -s repo_uri_or_path
144 [--key ssl_key ... --cert ssl_cert ...] [section/property ...]
146 pkgrepo info [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
147 [--key ssl_key ... --cert ssl_cert ...]
149 pkgrepo list [-F format] [-H] [-p publisher ...] -s repo_uri_or_path
150 [--key ssl_key ... --cert ssl_cert ...] [pkg_fmri_pattern ...]
152 pkgrepo contents [-m] [-t action_type ...] -s repo_uri_or_path
153 [--key ssl_key ... --cert ssl_cert ...] [pkg_fmri_pattern ...]
155 pkgrepo rebuild [-p publisher ...] -s repo_uri_or_path [--key ssl_key ...
156 --cert ssl_cert ...] [--no-catalog] [--no-index]
158 pkgrepo refresh [-p publisher ...] -s repo_uri_or_path [--key ssl_key ...
159 --cert ssl_cert ...] [--no-catalog] [--no-index]
161 pkgrepo remove [-n] [-p publisher ...] -s repo_uri_or_path
164 pkgrepo set [-p publisher ...] -s repo_uri_or_path
165 section/property[+|-]=[value] ... or
166 section/property[+|-]=([value]) ...
168 pkgrepo verify [-d] [-p publisher ...] [-i ignored_dep_file ...]
169 [--disable verification ...] -s repo_uri_or_path
171 pkgrepo fix [-v] [-p publisher ...] -s repo_uri_or_path
178 Displays a usage message."""))
183 class OptionError(Exception):
184 """Option exception. """
186 def __init__(self
, *args
):
187 Exception.__init
__(self
, *args
)
191 """Parse the repository location provided and attempt to transform it
192 into a valid repository URI.
195 return publisher
.RepositoryURI(misc
.parse_uri(uri
))
198 def subcmd_remove(conf
, args
):
199 subcommand
= "remove"
201 opts
, pargs
= getopt
.getopt(args
, "np:s:")
205 for opt
, arg
in opts
:
211 conf
["repo_uri"] = parse_uri(arg
)
214 usage(_("At least one package pattern must be provided."),
217 # Get repository object.
218 if not conf
.get("repo_uri", None):
219 usage(_("A package repository location must be provided "
220 "using -s."), cmd
=subcommand
)
221 repo
= get_repo(conf
, read_only
=False, subcommand
=subcommand
)
226 # Find matching packages.
228 matching
, refs
= repo
.get_matching_fmris(pargs
, pubs
=pubs
)
229 except apx
.PackageMatchErrors
as e
:
230 error(str(e
), cmd
=subcommand
)
234 # Don't make any changes; display list of packages to be
236 packages
= set(f
for m
in matching
.values() for f
in m
)
237 count
= len(packages
)
238 plist
= "\n".join("\t{0}".format(
239 p
.get_fmri(include_build
=False))
240 for p
in sorted(packages
))
241 logger
.info(_("{count:d} package(s) will be removed:\n"
242 "{plist}").format(**locals()))
245 progtrack
= get_tracker()
246 packages
= collections
.defaultdict(list)
247 for m
in matching
.values():
249 packages
[f
.publisher
].append(f
)
253 _("Removing packages for publisher {0} ...").format(pub
))
254 repo
.remove_packages(packages
[pub
], progtrack
=progtrack
,
256 if len(packages
) > 1:
257 # Add a newline between each publisher.
263 def get_repo(conf
, allow_invalid
=False, read_only
=True, subcommand
=None):
264 """Return the repository object for current program configuration.
266 'allow_invalid' specifies whether potentially corrupt repositories are
267 allowed; should only be True if performing a rebuild operation."""
269 repo_uri
= conf
["repo_uri"]
270 if repo_uri
.scheme
!= "file":
271 usage(_("Network repositories are not currently supported "
272 "for this operation."), cmd
=subcommand
)
274 path
= repo_uri
.get_pathname()
277 raise sr
.RepositoryInvalidError(str(repo_uri
))
278 return sr
.Repository(allow_invalid
=allow_invalid
, read_only
=read_only
,
282 def setup_transport(conf
, subcommand
=None, prefix
=None, verbose
=False,
283 remote_prefix
=True, ssl_key
=None, ssl_cert
=None):
284 repo_uri
= conf
.get("repo_uri", None)
286 usage(_("No repository location specified."), cmd
=subcommand
)
289 temp_root
= misc
.config_temp_root()
291 tmp_dir
= tempfile
.mkdtemp(dir=temp_root
)
292 tmpdirs
.append(tmp_dir
)
294 incoming_dir
= tempfile
.mkdtemp(dir=temp_root
)
295 tmpdirs
.append(incoming_dir
)
297 cache_dir
= tempfile
.mkdtemp(dir=temp_root
)
298 tmpdirs
.append(cache_dir
)
300 # Create transport and transport config.
301 xport
, xport_cfg
= transport
.setup_transport()
302 xport_cfg
.add_cache(cache_dir
, readonly
=False)
303 xport_cfg
.incoming_root
= incoming_dir
304 xport_cfg
.pkg_root
= tmp_dir
311 # Configure target publisher.
312 src_pub
= transport
.setup_publisher(str(repo_uri
), pub
, xport
,
313 xport_cfg
, remote_prefix
=remote_prefix
, ssl_key
=ssl_key
,
316 return xport
, src_pub
, tmp_dir
319 def subcmd_add_publisher(conf
, args
):
320 """Add publisher(s) to the specified repository."""
322 subcommand
= "add-publisher"
324 opts
, pargs
= getopt
.getopt(args
, "s:")
325 for opt
, arg
in opts
:
327 conf
["repo_uri"] = parse_uri(arg
)
329 repo_uri
= conf
.get("repo_uri", None)
331 usage(_("No repository location specified."), cmd
=subcommand
)
332 if repo_uri
.scheme
!= "file":
333 usage(_("Network repositories are not currently supported "
334 "for this operation."), cmd
=subcommand
)
337 usage(_("At least one publisher must be specified"),
342 if not misc
.valid_pub_prefix(pfx
):
343 error(_("Invalid publisher prefix '{0}'").format(pfx
),
349 repo
= get_repo(conf
, read_only
=False, subcommand
=subcommand
)
350 make_default
= not repo
.publishers
351 existing
= repo
.publishers
& set(pargs
)
353 # Elide the publishers that already exist, but retain the order
354 # publishers were specified in.
357 if pfx
not in repo
.publishers
360 # Tricky logic; _set_pub will happily add new publishers if necessary
361 # and not set any properties if you didn't specify any.
362 rval
= _set_pub(conf
, subcommand
, {}, new_pubs
, repo
)
365 # No publisher existed previously, so set the default publisher
366 # to be the first new one that was added.
367 _set_repo(conf
, subcommand
, { "publisher": {
368 "prefix": new_pubs
[0] } }, repo
)
370 if rval
== EXIT_OK
and existing
:
371 # Some of the publishers that were requested for addition
372 # were already known.
373 error(_("specified publisher(s) already exist: {0}").format(
374 ", ".join(existing
)), cmd
=subcommand
)
380 def subcmd_remove_publisher(conf
, args
):
381 """Remove publisher(s) from a repository"""
383 subcommand
= "remove-publisher"
387 opts
, pargs
= getopt
.getopt(args
, "ns:", ["synchronous"])
388 for opt
, arg
in opts
:
390 conf
["repo_uri"] = parse_uri(arg
)
393 elif opt
== "--synchronous":
395 repo_uri
= conf
.get("repo_uri", None)
397 usage(_("No repository location specified."), cmd
=subcommand
)
398 if repo_uri
.scheme
!= "file":
399 usage(_("Network repositories are not currently supported "
400 "for this operation."), cmd
=subcommand
)
403 usage(_("At least one publisher must be specified"),
408 if not misc
.valid_pub_prefix(pfx
):
412 error(_("Invalid publisher prefix(es):\n {0}").format(
413 "\n ".join(inv_pfxs
)), cmd
=subcommand
)
416 repo
= get_repo(conf
, read_only
=False, subcommand
=subcommand
)
417 existing
= repo
.publishers
& set(pargs
)
418 noexisting
= [pfx
for pfx
in pargs
419 if pfx
not in repo
.publishers
]
420 # Publishers left if remove succeeds.
421 left
= [pfx
for pfx
in repo
.publishers
if pfx
not in pargs
]
424 error(_("The following publisher(s) could not be found:\n "
425 "{0}").format("\n ".join(noexisting
)), cmd
=subcommand
)
428 logger
.info(_("Removing publisher(s)"))
430 rstore
= repo
.get_pub_rstore(pfx
)
431 numpkg
= rstore
.catalog
.package_count
432 logger
.info(_("\'{pfx}\'\t({num} package(s))").format(
433 pfx
=pfx
, num
=str(numpkg
)))
438 defaultpfx
= repo
.cfg
.get_property("publisher", "prefix")
439 repo_path
= repo_uri
.get_pathname()
441 repo
.remove_publisher(existing
, repo_path
, synch
)
442 # Change the repository publisher/prefix property, if necessary.
443 if defaultpfx
in existing
:
445 _set_repo(conf
, subcommand
, { "publisher" : {
446 "prefix" : left
[0]} }, repo
)
447 msg(_("The default publisher was removed."
448 " Setting 'publisher/prefix' to '{0}',"
449 " the only publisher left").format(left
[0]))
451 _set_repo(conf
, subcommand
, { "publisher": {
452 "prefix" : ""} }, repo
)
453 msg(_("The default publisher was removed."
454 " The 'publisher/prefix' property has been"
459 def subcmd_create(conf
, args
):
460 """Create a package repository at the given location."""
462 subcommand
= "create"
464 opts
, pargs
= getopt
.getopt(args
, "s:", ["version="])
467 for opt
, arg
in opts
:
469 conf
["repo_uri"] = parse_uri(arg
)
470 elif opt
== "--version":
471 # This option is currently private and allows creating a
472 # repository with a specific format based on version.
476 usage(_("Version must be an integer value."),
480 usage(_("Only one repository location may be specified."),
483 conf
["repo_uri"] = parse_uri(pargs
[0])
485 repo_uri
= conf
.get("repo_uri", None)
487 usage(_("No repository location specified."), cmd
=subcommand
)
488 if repo_uri
.scheme
!= "file":
489 usage(_("Network repositories are not currently supported "
490 "for this operation."), cmd
=subcommand
)
492 # Attempt to create a repository at the specified location. Allow
493 # whatever exceptions are raised to bubble up.
494 sr
.repository_create(repo_uri
, version
=version
)
499 def subcmd_get(conf
, args
):
500 """Display repository properties."""
504 out_format
= "default"
509 opts
, pargs
= getopt
.getopt(args
, "F:Hp:s:", ["key=", "cert="])
510 for opt
, arg
in opts
:
512 if arg
not in LISTING_FORMATS
:
513 raise apx
.InvalidOptionError(
514 apx
.InvalidOptionError
.ARG_INVALID
,
522 conf
["repo_uri"] = parse_uri(arg
)
525 elif opt
== "--cert":
528 # Setup transport so configuration can be retrieved.
529 if not conf
.get("repo_uri", None):
530 usage(_("A package repository location must be provided "
531 "using -s."), cmd
=subcommand
)
532 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
,
533 ssl_key
=key
, ssl_cert
=cert
)
537 return _get_pub(conf
, subcommand
, xport
, xpub
, omit_headers
,
538 out_format
, pubs
, pargs
)
539 return _get_repo(conf
, subcommand
, xport
, xpub
, omit_headers
,
543 def _get_repo(conf
, subcommand
, xport
, xpub
, omit_headers
, out_format
, pargs
):
544 """Display repository properties."""
546 # Configuration index is indexed by section name and property name.
547 # Retrieve and flatten it to simplify listing process.
548 stat_idx
= xport
.get_status(xpub
)
549 cfg_idx
= stat_idx
.get("repository", {}).get("configuration", {})
552 # Set minimum widths for section and property name columns by using the
553 # length of the column headers.
554 max_sname_len
= len(_("SECTION"))
555 max_pname_len
= len(_("PROPERTY"))
557 for sname
in cfg_idx
:
558 max_sname_len
= max(max_sname_len
, len(sname
))
559 for pname
in cfg_idx
[sname
]:
560 max_pname_len
= max(max_pname_len
, len(pname
))
561 props
.add("/".join((sname
, pname
)))
563 req_props
= set(pargs
)
564 if len(req_props
) >= 1:
565 found
= props
& req_props
566 notfound
= req_props
- found
573 for prop
in sorted(found
):
574 sname
, pname
= prop
.rsplit("/", 1)
575 sval
= cfg_idx
[sname
][pname
]
582 # SECTION PROPERTY VALUE
583 # <sec_1> <prop_1> <prop_1_value>
584 # <sec_2> <prop_2> <prop_2_value>
587 "section" : [("default", "json", "tsv"), _("SECTION"), ""],
588 "property" : [("default", "json", "tsv"), _("PROPERTY"), ""],
589 "value" : [("default", "json", "tsv"), _("VALUE"), ""],
591 desired_field_order
= (_("SECTION"), _("PROPERTY"), _("VALUE"))
593 # Default output formatting.
594 def_fmt
= "{0:" + str(max_sname_len
) + "} {1:" + str(max_pname_len
) + \
597 if found
or (not req_props
and out_format
== "default"):
598 # print without trailing newline.
599 sys
.stdout
.write(misc
.get_listing(desired_field_order
,
600 field_data
, gen_listing(), out_format
, def_fmt
,
603 if found
and notfound
:
605 if req_props
and not found
:
606 if out_format
== "default":
607 # Don't pollute other output formats.
608 error(_("no matching properties found"),
614 def _get_matching_pubs(subcommand
, pubs
, xport
, xpub
, out_format
="default",
615 use_transport
=False):
617 # Retrieve publisher information.
618 pub_data
= xport
.get_publisherdata(xpub
)
619 known_pubs
= set(p
.prefix
for p
in pub_data
)
620 if len(pubs
) > 0 and "all" not in pubs
:
621 found
= known_pubs
& pubs
622 notfound
= pubs
- found
623 pub_data
= [p
for p
in pub_data
if p
.prefix
in found
]
629 # Assign transport information.
631 p
.repository
= xpub
.repository
633 # Establish initial return value and perform early exit if appropriate.
635 if found
and notfound
:
637 elif pubs
and not found
:
638 if out_format
== "default":
639 # Don't pollute other output formats.
640 error(_("no matching publishers found"),
642 return EXIT_OOPS
, None, None
643 return rval
, found
, pub_data
646 def _get_pub(conf
, subcommand
, xport
, xpub
, omit_headers
, out_format
, pubs
,
648 """Display publisher properties."""
650 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
651 xpub
, out_format
=out_format
)
652 if rval
== EXIT_OOPS
:
655 # Set minimum widths for section and property name columns by using the
656 # length of the column headers and data.
657 max_pubname_len
= str(max(
658 [len(_("PUBLISHER"))] + [len(p
) for p
in found
]
660 max_sname_len
= len(_("SECTION"))
661 max_pname_len
= len(_("PROPERTY"))
663 # For each requested publisher, retrieve the requested property data.
666 pub_idx
[pub
.prefix
] = {
669 "prefix": pub
.prefix
,
673 pub_repo
= pub
.repository
675 pub_idx
[pub
.prefix
]["repository"] = {
676 "collection-type": pub_repo
.collection_type
,
677 "description": pub_repo
.description
,
678 "legal-uris": pub_repo
.legal_uris
,
679 "mirrors": pub_repo
.mirrors
,
680 "name": pub_repo
.name
,
681 "origins": pub_repo
.origins
,
682 "refresh-seconds": pub_repo
.refresh_seconds
,
683 "registration-uri": pub_repo
.registration_uri
,
684 "related-uris": pub_repo
.related_uris
,
687 pub_idx
[pub
.prefix
]["repository"] = {
688 "collection-type": "core",
694 "refresh-seconds": "",
695 "registration-uri": "",
699 # Determine possible set of properties and lengths.
702 for sname
in pub_idx
[pub
]:
703 max_sname_len
= max(max_sname_len
, len(sname
))
704 for pname
in pub_idx
[pub
][sname
]:
705 max_pname_len
= max(max_pname_len
, len(pname
))
706 props
.add("/".join((sname
, pname
)))
708 # Determine properties to display.
709 req_props
= set(pargs
)
710 if len(req_props
) >= 1:
711 found
= props
& req_props
712 notfound
= req_props
- found
719 for pub
in sorted(pub_idx
.keys()):
720 for prop
in sorted(found
):
721 sname
, pname
= prop
.rsplit("/", 1)
722 sval
= pub_idx
[pub
][sname
][pname
]
730 # PUBLISHER SECTION PROPERTY VALUE
731 # <pub_1> <sec_1> <prop_1> <prop_1_value>
732 # <pub_1> <sec_2> <prop_2> <prop_2_value>
735 "publisher" : [("default", "json", "tsv"), _("PUBLISHER"), ""],
736 "section" : [("default", "json", "tsv"), _("SECTION"), ""],
737 "property" : [("default", "json", "tsv"), _("PROPERTY"), ""],
738 "value" : [("default", "json", "tsv"), _("VALUE"), ""],
740 desired_field_order
= (_("PUBLISHER"), _("SECTION"), _("PROPERTY"),
743 # Default output formatting.
744 def_fmt
= "{0:" + str(max_pubname_len
) + "} {1:" + str(max_sname_len
) + \
745 "} {2:" + str(max_pname_len
) + "} {3}"
747 if found
or (not req_props
and out_format
== "default"):
748 # print without trailing newline.
749 sys
.stdout
.write(misc
.get_listing(desired_field_order
,
750 field_data
, gen_listing(), out_format
, def_fmt
,
753 if found
and notfound
:
755 if req_props
and not found
:
756 if out_format
== "default":
757 # Don't pollute other output formats.
758 error(_("no matching properties found"),
764 def subcmd_info(conf
, args
):
765 """Display a list of known publishers and a summary of known packages
766 and when the package data for the given publisher was last updated.
771 out_format
= "default"
776 opts
, pargs
= getopt
.getopt(args
, "F:Hp:s:", ["key=", "cert="])
777 for opt
, arg
in opts
:
779 if arg
not in LISTING_FORMATS
:
780 raise apx
.InvalidOptionError(
781 apx
.InvalidOptionError
.ARG_INVALID
,
789 conf
["repo_uri"] = parse_uri(arg
)
792 elif opt
== "--cert":
796 usage(_("command does not take operands"), cmd
=subcommand
)
798 # Setup transport so status can be retrieved.
799 if not conf
.get("repo_uri", None):
800 usage(_("A package repository location must be provided "
801 "using -s."), cmd
=subcommand
)
802 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
,
803 ssl_key
=key
, ssl_cert
=cert
)
805 # Retrieve repository status information.
806 stat_idx
= xport
.get_status(xpub
)
807 pub_idx
= stat_idx
.get("repository", {}).get("publishers", {})
808 if len(pubs
) > 0 and "all" not in pubs
:
809 found
= set(pub_idx
.keys()) & pubs
810 notfound
= pubs
- found
812 found
= set(pub_idx
.keys())
818 pkg_count
= pdata
.get("package-count", 0)
819 last_update
= pdata
.get("last-catalog-update", "")
821 # Reformat the date into something more user
822 # friendly (and locale specific).
823 last_update
= pkg
.catalog
.basic_ts_to_datetime(
825 last_update
= "{0}Z".format(
826 pkg
.catalog
.datetime_to_ts(last_update
))
827 rstatus
= _(pub_idx
[pfx
].get("status", "online"))
830 "packages": pkg_count
,
832 "updated": last_update
,
835 # PUBLISHER PACKAGES STATUS UPDATED
836 # <pub_1> <num_uniq_pkgs> <status> <cat_last_modified>
837 # <pub_2> <num_uniq_pkgs> <status> <cat_last_modified>
840 "publisher" : [("default", "json", "tsv"), _("PUBLISHER"), ""],
841 "packages" : [("default", "json", "tsv"), _("PACKAGES"), ""],
842 "status" : [("default", "json", "tsv"), _("STATUS"), ""],
843 "updated" : [("default", "json", "tsv"), _("UPDATED"), ""],
846 desired_field_order
= (_("PUBLISHER"), "", _("PACKAGES"), _("STATUS"),
849 # Default output formatting.
851 [len(desired_field_order
[0])] + [len(p
) for p
in found
]
853 def_fmt
= "{0:" + pub_len
+ "} {1:8} {2:16} {3}"
855 if found
or (not pubs
and out_format
== "default"):
856 # print without trailing newline.
857 sys
.stdout
.write(misc
.get_listing(desired_field_order
,
858 field_data
, gen_listing(), out_format
, def_fmt
,
861 if found
and notfound
:
863 if pubs
and not found
:
864 if out_format
== "default":
865 # Don't pollute other output formats.
866 error(_("no matching publishers found"),
871 def subcmd_list(conf
, args
):
872 """List all packages matching the specified patterns."""
876 out_format
= "default"
881 opts
, pargs
= getopt
.getopt(args
, "F:Hp:s:", ["key=", "cert="])
882 for opt
, arg
in opts
:
884 if arg
not in LISTING_FORMATS
:
885 raise apx
.InvalidOptionError(
886 apx
.InvalidOptionError
.ARG_INVALID
,
894 conf
["repo_uri"] = parse_uri(arg
)
897 elif opt
== "--cert":
901 # Setup transport so configuration can be retrieved.
902 if not conf
.get("repo_uri", None):
903 usage(_("A package repository location must be provided "
904 "using -s."), cmd
=subcommand
)
905 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
,
906 ssl_key
=key
, ssl_cert
=cert
)
908 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
909 xpub
, out_format
=out_format
, use_transport
=True)
910 if rval
== EXIT_OOPS
:
913 refresh_pub(pub_data
, xport
)
919 collect_attrs
= out_format
.startswith("json")
922 for f
, states
, attrs
in cat
.gen_packages(
923 collect_attrs
=collect_attrs
, matched
=matched
,
924 patterns
=pargs
, pubs
=[pub
.prefix
],
925 unmatched
=unmatched
, return_fmris
=True):
927 listed
["packages"] = True
930 if out_format
== "default" or \
932 if pkgdefs
.PKG_STATE_OBSOLETE
in \
935 elif pkgdefs
.PKG_STATE_RENAMED
in \
939 if out_format
== "default":
940 fver
= str(f
.version
.get_version(
941 include_build
=False))
942 ffmri
= str(f
.get_fmri(include_build
=False))
944 fver
= str(f
.version
)
948 "publisher": f
.publisher
,
951 "release": str(f
.version
.release
),
953 str(f
.version
.build_release
),
954 "branch": str(f
.version
.branch
),
956 str(f
.version
.timestr
),
958 "short_state": state
,
963 for mods
in attrs
[attr
]:
970 unmatched
.difference_update(matched
)
973 "publisher": [("default", "json", "tsv"), _("PUBLISHER"), ""],
974 "name": [("default", "json", "tsv"), _("NAME"), ""],
975 "version": [("default", "json"), _("VERSION"), ""],
976 "release": [("json", "tsv",), _("RELEASE"), ""],
977 "build-release": [("json", "tsv",), _("BUILD RELEASE"), ""],
978 "branch": [("json", "tsv",), _("BRANCH"), ""],
979 "timestamp": [("json", "tsv",), _("PACKAGING DATE"), ""],
980 "pkg.fmri": [("json", "tsv",), _("FMRI"), ""],
981 "short_state": [("default", "tsv"), "O", ""],
984 desired_field_order
= (_("PUBLISHER"), _("NAME"), "O", _("VERSION"),
985 _("SUMMARY"), _("DESCRIPTION"), _("CATEGORIES"), _("RELEASE"),
986 _("BUILD RELEASE"), _("BRANCH"), _("PACKAGING DATE"), _("FMRI"),
989 # Default output formatting.
990 max_pub_name_len
= str(
991 max(list(len(p
) for p
in found
) + [len(_("PUBLISHER"))]))
992 def_fmt
= "{0:" + max_pub_name_len
+ "} {1:45} {2:1} {3}"
994 # print without trailing newline.
995 sys
.stdout
.write(misc
.get_listing(
996 desired_field_order
, field_data
, gen_listing(),
997 out_format
, def_fmt
, omit_headers
))
999 if not listed
and pargs
:
1000 # No matching packages.
1004 error(apx
.PackageMatchErrors(unmatched_fmris
=unmatched
),
1008 # One or more patterns didn't match a package from any
1009 # publisher; only display the error.
1011 error(apx
.PackageMatchErrors(unmatched_fmris
=unmatched
),
1018 def refresh_pub(pub_data
, xport
):
1019 """A helper function to refresh all specified publishers."""
1022 temp_root
= misc
.config_temp_root()
1023 progtrack
= get_tracker()
1024 progtrack
.set_purpose(progtrack
.PURPOSE_LISTING
)
1026 progtrack
.refresh_start(pub_cnt
=len(pub_data
), full_refresh
=True,
1027 target_catalog
=False)
1029 for pub
in pub_data
:
1030 progtrack
.refresh_start_pub(pub
)
1031 meta_root
= tempfile
.mkdtemp(dir=temp_root
)
1032 tmpdirs
.append(meta_root
)
1033 pub
.meta_root
= meta_root
1034 pub
.transport
= xport
1037 pub
.refresh(True, True, progtrack
=progtrack
)
1038 except apx
.TransportError
:
1039 # Assume that a catalog doesn't exist for the target
1040 # publisher and drive on.
1042 progtrack
.refresh_end_pub(pub
)
1044 progtrack
.refresh_done()
1047 def subcmd_contents(conf
, args
):
1048 """List package contents."""
1050 subcommand
= "contents"
1058 opts
, pargs
= getopt
.getopt(args
, "ms:t:", ["key=", "cert="])
1059 for opt
, arg
in opts
:
1061 conf
["repo_uri"] = parse_uri(arg
)
1065 action_types
.extend(arg
.split(","))
1066 elif opt
== "--key":
1068 elif opt
== "--cert":
1071 # Setup transport so configuration can be retrieved.
1072 if not conf
.get("repo_uri", None):
1073 usage(_("A package repository location must be provided "
1074 "using -s."), cmd
=subcommand
)
1076 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
,
1077 ssl_key
=key
, ssl_cert
=cert
)
1079 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
1080 xpub
, use_transport
=True)
1081 if rval
== EXIT_OOPS
:
1084 # Default output prints out the raw manifest. The -m option is implicit
1085 # for now and supported to make the interface equivalent to pkg
1087 if not attrs
or display_raw
:
1088 attrs
= ["action.raw"]
1090 refresh_pub(pub_data
, xport
)
1096 for pub
in pub_data
:
1098 for f
, states
, attr
in cat
.gen_packages(matched
=matched
,
1099 patterns
=pargs
, pubs
=[pub
.prefix
],
1100 unmatched
=unmatched
, return_fmris
=True):
1103 manifests
.append(xport
.get_manifest(f
))
1104 unmatched
.difference_update(matched
)
1106 # Build a generator expression based on whether specific action types
1109 # If query is limited to specific action types, use the more
1110 # efficient type-based generation mechanism.
1112 (m
.fmri
, a
, None, None, None)
1114 for a
in m
.gen_actions_by_types(action_types
)
1118 (m
.fmri
, a
, None, None, None)
1120 for a
in m
.gen_actions()
1123 # Determine if the query returned any results by "peeking" at the first
1124 # value returned from the generator expression.
1126 got
= gen_expr
.next()
1127 except StopIteration:
1132 actionlist
= itertools
.chain([got
], gen_expr
)
1135 if action_types
and manifests
and not got
:
1136 logger
.error(_(gettext
.ngettext("""\
1137 pkgrepo: contents: This package contains no actions with the types specified
1138 using the -t option""", """\
1139 pkgrepo: contents: These packages contain no actions with the types specified
1140 using the -t option.""", len(pargs
))))
1143 if manifests
and rval
== EXIT_OK
:
1144 lines
= misc
.list_actions_by_attrs(actionlist
, attrs
)
1146 text
= ("{0}".format(*line
)).rstrip()
1155 pkgrepo: contents: no packages matching the following patterns you specified
1156 were found in the repository."""))
1159 logger
.error(" {0}".format(p
))
1165 def __rebuild_local(subcommand
, conf
, pubs
, build_catalog
, build_index
):
1166 """In an attempt to allow operations on potentially corrupt
1167 repositories, 'local' repositories (filesystem-basd ones) are handled
1170 repo
= get_repo(conf
, allow_invalid
=build_catalog
, read_only
=False,
1171 subcommand
=subcommand
)
1173 rpubs
= set(repo
.publishers
)
1177 found
= rpubs
& pubs
1178 notfound
= pubs
- found
1181 if found
and notfound
:
1183 elif pubs
and not found
:
1184 error(_("no matching publishers found"), cmd
=subcommand
)
1187 logger
.info("Initiating repository rebuild.")
1189 repo
.rebuild(build_catalog
=build_catalog
,
1190 build_index
=build_index
, pub
=pfx
)
1195 def __rebuild_remote(subcommand
, conf
, pubs
, key
, cert
, build_catalog
,
1197 def do_rebuild(xport
, xpub
):
1198 if build_catalog
and build_index
:
1199 xport
.publish_rebuild(xpub
)
1201 xport
.publish_rebuild_packages(xpub
)
1203 xport
.publish_rebuild_indexes(xpub
)
1205 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
,
1206 ssl_key
=key
, ssl_cert
=cert
)
1207 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
1209 if rval
== EXIT_OOPS
:
1212 logger
.info("Initiating repository rebuild.")
1215 do_rebuild(xport
, xpub
)
1220 def subcmd_rebuild(conf
, args
):
1221 """Rebuild the repository's catalog and index data (as permitted)."""
1223 subcommand
= "rebuild"
1224 build_catalog
= True
1229 opts
, pargs
= getopt
.getopt(args
, "p:s:", ["no-catalog", "no-index",
1232 for opt
, arg
in opts
:
1234 if not misc
.valid_pub_prefix(arg
):
1235 error(_("Invalid publisher prefix '{0}'").format(
1236 arg
), cmd
=subcommand
)
1239 conf
["repo_uri"] = parse_uri(arg
)
1240 elif opt
== "--no-catalog":
1241 build_catalog
= False
1242 elif opt
== "--no-index":
1244 elif opt
== "--key":
1246 elif opt
== "--cert":
1250 usage(_("command does not take operands"), cmd
=subcommand
)
1252 if not build_catalog
and not build_index
:
1253 # Why? Who knows; but do what was requested--nothing!
1256 # Setup transport so operation can be performed.
1257 if not conf
.get("repo_uri", None):
1258 usage(_("A package repository location must be provided "
1259 "using -s."), cmd
=subcommand
)
1261 if conf
["repo_uri"].scheme
== "file":
1262 return __rebuild_local(subcommand
, conf
, pubs
, build_catalog
,
1265 return __rebuild_remote(subcommand
, conf
, pubs
, key
, cert
,
1266 build_catalog
, build_index
)
1269 def subcmd_refresh(conf
, args
):
1270 """Refresh the repository's catalog and index data (as permitted)."""
1272 subcommand
= "refresh"
1274 refresh_index
= True
1278 opts
, pargs
= getopt
.getopt(args
, "p:s:", ["no-catalog", "no-index",
1281 for opt
, arg
in opts
:
1283 if not misc
.valid_pub_prefix(arg
):
1284 error(_("Invalid publisher prefix '{0}'").format(
1285 arg
), cmd
=subcommand
)
1288 conf
["repo_uri"] = parse_uri(arg
)
1289 elif opt
== "--no-catalog":
1291 elif opt
== "--no-index":
1292 refresh_index
= False
1293 elif opt
== "--key":
1295 elif opt
== "--cert":
1299 usage(_("command does not take operands"), cmd
=subcommand
)
1301 if not add_content
and not refresh_index
:
1302 # Why? Who knows; but do what was requested--nothing!
1305 # Setup transport so operation can be performed.
1306 if not conf
.get("repo_uri", None):
1307 usage(_("A package repository location must be provided "
1308 "using -s."), cmd
=subcommand
)
1310 def do_refresh(xport
, xpub
):
1311 if add_content
and refresh_index
:
1312 xport
.publish_refresh(xpub
)
1314 xport
.publish_refresh_packages(xpub
)
1316 xport
.publish_refresh_indexes(xpub
)
1318 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
,
1319 ssl_key
=key
, ssl_cert
=cert
)
1320 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
1322 if rval
== EXIT_OOPS
:
1325 logger
.info("Initiating repository refresh.")
1328 do_refresh(xport
, xpub
)
1333 def subcmd_set(conf
, args
):
1334 """Set repository properties."""
1339 opts
, pargs
= getopt
.getopt(args
, "p:s:")
1340 for opt
, arg
in opts
:
1344 conf
["repo_uri"] = parse_uri(arg
)
1353 # Attempt to parse property into components.
1354 prop
, val
= arg
.split("=", 1)
1355 sname
, pname
= prop
.rsplit("/", 1)
1357 # Store property values by section.
1358 props
.setdefault(sname
, {})
1360 # Parse the property value into a list if
1361 # necessary, otherwise append it to the list
1362 # of values for the property.
1363 if len(val
) > 0 and val
[0] == "(" and \
1365 val
= shlex
.split(val
.strip("()"))
1367 if sname
in props
and pname
in props
[sname
]:
1368 # Determine if previous value is already
1369 # a list, and if not, convert and append
1371 pval
= props
[sname
][pname
]
1372 if not isinstance(pval
, list):
1374 if isinstance(val
, list):
1378 props
[sname
][pname
] = pval
1380 # Otherwise, just store the value.
1381 props
[sname
][pname
] = val
1387 usage(_("a property name and value must be provided in the "
1388 "form <section/property>=<value> or "
1389 "<section/property>=([\"<value>\" ...])"))
1391 # Get repository object.
1392 if not conf
.get("repo_uri", None):
1393 usage(_("A package repository location must be provided "
1394 "using -s."), cmd
=subcommand
)
1395 repo
= get_repo(conf
, read_only
=False, subcommand
=subcommand
)
1399 return _set_pub(conf
, subcommand
, props
, pubs
, repo
)
1401 return _set_repo(conf
, subcommand
, props
, repo
)
1404 def _set_pub(conf
, subcommand
, props
, pubs
, repo
):
1405 """Set publisher properties."""
1407 for sname
, sprops
in props
.iteritems():
1408 if sname
not in ("publisher", "repository"):
1409 usage(_("unknown property section "
1410 "'{0}'").format(sname
), cmd
=subcommand
)
1411 for pname
in sprops
:
1412 if sname
== "publisher" and pname
== "prefix":
1413 usage(_("'{0}' may not be set using "
1414 "this command".format(pname
)))
1415 attrname
= pname
.replace("-", "_")
1416 if not hasattr(publisher
.Publisher
, attrname
) and \
1417 not hasattr(publisher
.Repository
, attrname
):
1418 usage(_("unknown property '{0}'").format(
1419 pname
), cmd
=subcommand
)
1422 # Default to list of all publishers.
1423 pubs
= repo
.publishers
1425 # If there are still no known publishers, this
1426 # operation cannot succeed, so fail now.
1427 usage(_("One or more publishers must be specified to "
1428 "create and set properties for as none exist yet."),
1431 # Get publishers and update properties.
1436 # Get a copy of the existing publisher.
1437 pub
= copy
.copy(repo
.get_publisher(pfx
))
1438 except sr
.RepositoryUnknownPublisher
as e
:
1439 pub
= publisher
.Publisher(pfx
)
1441 except sr
.RepositoryError
as e
:
1442 failed
.append((pfx
, e
))
1446 # Set/update the publisher's properties.
1447 for sname
, sprops
in props
.iteritems():
1448 if sname
== "publisher":
1450 elif sname
== "repository":
1451 target
= pub
.repository
1453 target
= publisher
.Repository()
1454 pub
.repository
= target
1456 for pname
, val
in sprops
.iteritems():
1457 attrname
= pname
.replace("-", "_")
1458 pval
= getattr(target
, attrname
)
1459 if isinstance(pval
, list) and \
1460 not isinstance(val
, list):
1461 # If the target property expects
1462 # a list, transform the provided
1463 # value into one if it isn't
1469 setattr(target
, attrname
, val
)
1470 except apx
.ApiException
as e
:
1471 failed
.append((pfx
, e
))
1475 repo
.add_publisher(pub
)
1477 repo
.update_publisher(pub
)
1480 for pfx
, details
in failed
:
1481 error(_("Unable to set properties for publisher "
1482 "'{pfx}':\n{details}").format(**locals()))
1483 if len(failed
) < len(pubs
):
1489 def _set_repo(conf
, subcommand
, props
, repo
):
1490 """Set repository properties."""
1493 for sname
, props
in props
.iteritems():
1494 for pname
, val
in props
.iteritems():
1495 repo
.cfg
.set_property(sname
, pname
, val
)
1501 def subcmd_version(conf
, args
):
1502 """Display the version of the pkg(5) API."""
1504 subcommand
= "version"
1506 usage(_("command does not take operands"), cmd
=subcommand
)
1511 verify_error_header
= None
1512 verify_warning_header
= None
1513 verify_reason_headers
= None
1515 def __load_verify_msgs():
1516 """Since our gettext isn't loaded we need to ensure our globals have
1517 correct content by calling this method. These values are used by both
1518 fix when in verbose mode, and verify"""
1520 global verify_error_header
1521 global verify_warning_header
1522 global verify_reason_headers
1524 # A map of error detail types to the human-readable description of each
1525 # type. These correspond to keys in the dictionary returned by
1526 # sr.Repository.verify(..)
1527 verify_reason_headers
= {
1528 "path": _("Repository path"),
1529 "actual": _("Computed hash"),
1531 "permissionspath": _("Path"),
1532 "pkg": _("Package"),
1533 "depend": _("Dependency"),
1534 "type":_("Dependency type"),
1538 verify_error_header
= _("ERROR")
1539 verify_warning_header
= _("WARNING")
1542 def __fmt_verify(verify_tuple
):
1543 """Format a verify_tuple, of the form (error, path, message, reason)
1544 returning a formatted error message, and an FMRI indicating what
1545 packages within the repository are affected. Note that the returned FMRI
1546 may not be valid, in which case a path to the broken manifest in the
1547 repository is returned instead."""
1549 error
, path
, message
, reason
= verify_tuple
1551 formatted_message
= "{error_type:>16}: {message}\n".format(
1552 error_type
=verify_error_header
, message
=message
)
1553 reason
["path"] = path
1555 if error
== sr
.REPO_VERIFY_BADMANIFEST
:
1556 reason_keys
= ["path", "err"]
1557 elif error
in [sr
.REPO_VERIFY_PERM
, sr
.REPO_VERIFY_MFPERM
]:
1558 reason_keys
= ["pkg", "path"]
1559 elif error
== sr
.REPO_VERIFY_BADHASH
:
1560 reason_keys
= ["pkg", "path", "actual", "fpath"]
1561 elif error
== sr
.REPO_VERIFY_UNKNOWN
:
1562 reason_keys
= ["path", "err"]
1563 elif error
== sr
.REPO_VERIFY_BADSIG
:
1564 reason_keys
= ["pkg", "path", "err"]
1565 elif error
== sr
.REPO_VERIFY_DEPENDERROR
:
1566 reason_keys
= ["pkg", "depend", "type"]
1567 elif error
== sr
.REPO_VERIFY_WARN_OPENPERMS
:
1568 formatted_message
= \
1569 "{error_type:>16}: {message}\n".format(
1570 error_type
=verify_warning_header
, message
=message
)
1571 reason_keys
= ["permissionspath", "err"]
1573 # A list of the details we provide. Some error codes
1574 # have different details associated with them.
1575 reason_keys
= ["pkg", "path", "fpath"]
1578 # the detailed error message can be long, so we'll wrap it. If what we
1579 # have fits on a single line, use it, otherwise begin displaying the
1580 # message on the next line.
1581 if "err" in reason_keys
:
1583 lines
= textwrap
.wrap(reason
["err"])
1586 err_str
+= "{0:>18}\n".format(line
)
1587 reason
["err"] = "\n" + err_str
.rstrip()
1589 reason
["err"] = lines
[0]
1591 for key
in reason_keys
:
1592 # sometimes we don't have the key we want, for example we may
1593 # not have a file path from the package if the error is a
1594 # missing repository file for a 'license' action (which don't
1595 # have 'path' attributes, hence no 'fpath' dictionary entry)
1596 if key
not in reason
:
1598 formatted_message
+= "{key:>16}: {value}\n".format(
1599 key
=verify_reason_headers
[key
], value
=reason
[key
])
1601 formatted_message
+= "\n"
1603 if error
== sr
.REPO_VERIFY_WARN_OPENPERMS
:
1604 return formatted_message
, None
1605 elif "depend" in reason
:
1606 return formatted_message
, reason
["depend"]
1607 elif "pkg" in reason
:
1608 return formatted_message
, reason
["pkg"]
1609 return formatted_message
, reason
["path"]
1612 def __collect_default_ignore_dep_files(ignored_dep_files
):
1613 """Helpler function to collect default ignored-dependency files."""
1615 root_ignored
= "/usr/share/pkg/ignored_deps"
1616 altroot
= DebugValues
.get_value("ignored_deps")
1618 root_ignored
= altroot
1619 if os
.path
.exists(root_ignored
):
1620 igfiles
= os
.listdir(root_ignored
)
1622 ignored_dep_files
.append(os
.path
.join(root_ignored
,
1626 def subcmd_verify(conf
, args
):
1627 """Verify the repository content (file, manifest content and
1628 dependencies only)."""
1630 subcommand
= "verify"
1631 __load_verify_msgs()
1633 opts
, pargs
= getopt
.getopt(args
, "dp:s:i:", ["disable="])
1634 allowed_checks
= set(sr
.verify_default_checks
)
1635 force_dep_check
= False
1636 ignored_dep_files
= []
1638 for opt
, arg
in opts
:
1640 conf
["repo_uri"] = parse_uri(arg
)
1642 if not misc
.valid_pub_prefix(arg
):
1643 error(_("Invalid publisher prefix '{0}'").format(
1644 arg
), cmd
=subcommand
)
1647 force_dep_check
= True
1648 elif opt
== "--disable":
1650 if arg
in sr
.verify_default_checks
:
1651 if arg
in allowed_checks
:
1652 allowed_checks
.remove(arg
)
1654 usage(_("Invalid verification to be disabled, "
1655 "please consider: {0}").format(", ".join(
1656 sr
.verify_default_checks
)), cmd
=subcommand
)
1658 ignored_dep_files
.append(arg
)
1661 usage(_("command does not take operands"), cmd
=subcommand
)
1663 repo_uri
= conf
.get("repo_uri", None)
1665 usage(_("A package repository location must be provided "
1666 "using -s."), cmd
=subcommand
)
1668 if repo_uri
.scheme
!= "file":
1669 usage(_("Network repositories are not currently supported "
1670 "for this operation."), cmd
=subcommand
)
1672 if sr
.VERIFY_DEPENDENCY
not in allowed_checks
and \
1673 (force_dep_check
or len(ignored_dep_files
) > 0):
1674 usage(_("-d or -i option cannot be used when dependency "
1675 "verification is disabled."), cmd
=subcommand
)
1677 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
)
1678 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
1681 if rval
== EXIT_OOPS
:
1684 logger
.info("Initiating repository verification.")
1686 progtrack
= get_tracker()
1688 def report_error(verify_tuple
):
1689 message
, bad_fmri
= __fmt_verify(verify_tuple
)
1691 bad_fmris
.add(bad_fmri
)
1692 progtrack
.repo_verify_yield_error(bad_fmri
, message
)
1694 if sr
.VERIFY_DEPENDENCY
in allowed_checks
or not force_dep_check
:
1695 __collect_default_ignore_dep_files(ignored_dep_files
)
1697 repo
= sr
.Repository(root
=repo_uri
.get_pathname())
1701 xport
, xpub
, tmp_dir
= setup_transport(conf
, prefix
=pfx
,
1702 remote_prefix
=False,
1703 subcommand
=subcommand
)
1704 xpub
.transport
= xport
1705 found_pubs
.append(xpub
)
1707 for verify_tuple
in repo
.verify(pubs
=found_pubs
,
1708 allowed_checks
=allowed_checks
, force_dep_check
=force_dep_check
,
1709 ignored_dep_files
=ignored_dep_files
, progtrack
=progtrack
):
1710 report_error(verify_tuple
)
1717 def subcmd_fix(conf
, args
):
1718 """Fix the repository content (file and manifest content only)
1719 For index and catalog content corruption, a rebuild should be
1723 __load_verify_msgs()
1726 # Dependency verification. Note fix will not force dependency check.
1727 force_dep_check
= False
1728 ignored_dep_files
= []
1730 opts
, pargs
= getopt
.getopt(args
, "vp:s:")
1732 for opt
, arg
in opts
:
1734 conf
["repo_uri"] = parse_uri(arg
)
1738 if not misc
.valid_pub_prefix(arg
):
1739 error(_("Invalid publisher prefix '{0}'").format(
1740 arg
), cmd
=subcommand
)
1744 usage(_("command does not take operands"), cmd
=subcommand
)
1746 repo_uri
= conf
.get("repo_uri", None)
1748 usage(_("A package repository location must be provided "
1749 "using -s."), cmd
=subcommand
)
1751 if repo_uri
.scheme
!= "file":
1752 usage(_("Network repositories are not currently supported "
1753 "for this operation."), cmd
=subcommand
)
1755 xport
, xpub
, tmp_dir
= setup_transport(conf
, subcommand
=subcommand
)
1756 rval
, found
, pub_data
= _get_matching_pubs(subcommand
, pubs
, xport
,
1758 if rval
== EXIT_OOPS
:
1761 logger
.info("Initiating repository fix.")
1762 progtrack
= get_tracker()
1764 def verify_cb(tracker
, verify_tuple
):
1765 """A method passed to sr.Repository.fix(..) to emit verify
1766 messages if verbose mode is enabled."""
1769 formatted_message
, bad_fmri
= __fmt_verify(verify_tuple
)
1770 tracker
.repo_verify_yield_error(bad_fmri
, formatted_message
)
1772 repo
= sr
.Repository(root
=repo_uri
.get_pathname())
1774 broken_fmris
= set()
1775 failed_fix_paths
= set()
1776 progtrack
= get_tracker()
1777 __collect_default_ignore_dep_files(ignored_dep_files
)
1781 xport
, xpub
, tmp_dir
= setup_transport(conf
, prefix
=pfx
,
1782 remote_prefix
=False,
1783 subcommand
=subcommand
)
1784 xpub
.transport
= xport
1785 found_pubs
.append(xpub
)
1787 for status_code
, path
, message
, reason
in \
1788 repo
.fix(pubs
=found_pubs
, force_dep_check
=force_dep_check
,
1789 ignored_dep_files
=ignored_dep_files
,
1790 progtrack
=progtrack
,
1791 verify_callback
=verify_cb
):
1792 if status_code
== sr
.REPO_FIX_ITEM
:
1793 # When we can't get the FMRI, eg. in the case
1794 # of a corrupt manifest, use the path instead.
1795 fmri
= reason
["pkg"]
1798 broken_fmris
.add(fmri
)
1800 progtrack
.repo_fix_yield_info(fmri
,
1802 elif status_code
== sr
.REPO_VERIFY_DEPENDERROR
:
1803 bad_deps
.add(reason
["depend"])
1805 failed_fix_paths
.add(path
)
1811 logger
.info(_("Use pkgsend(1) or pkgrecv(1) to republish the\n"
1812 "following packages or paths which were quarantined:\n\n\t"
1814 "\n\t".join([str(f
) for f
in broken_fmris
])))
1815 if failed_fix_paths
:
1816 logger
.info(_("\npkgrepo could not repair the following paths "
1817 "in the repository:\n\n\t{0}").format(
1818 "\n\t".join([p
for p
in failed_fix_paths
])))
1820 logger
.info(_("\npkgrepo could not repair the following "
1821 "dependency issues in the repository:\n\n\t{0}").format(
1822 "\n\t".join([p
for p
in bad_deps
])))
1823 if not (broken_fmris
or failed_fix_paths
or bad_deps
):
1824 logger
.info(_("No repository fixes required."))
1826 logger
.info(_("Repository repairs completed."))
1828 if failed_fix_paths
or bad_deps
:
1834 global_settings
.client_name
= PKG_CLIENT_NAME
1837 opts
, pargs
= getopt
.getopt(sys
.argv
[1:], "s:D:?",
1839 except getopt
.GetoptError
as e
:
1840 usage(_("illegal global option -- {0}").format(e
.opt
))
1844 for opt
, arg
in opts
:
1846 conf
["repo_uri"] = parse_uri(arg
)
1847 elif opt
in ("--help", "-?"):
1849 elif opt
== "-D" or opt
== "--debug":
1851 key
, value
= arg
.split("=", 1)
1852 except (AttributeError, ValueError):
1853 usage(_("{opt} takes argument of form "
1854 "name=value, not {arg}").format(
1856 DebugValues
.set_value(key
, value
)
1863 subcommand
= pargs
.pop(0)
1864 if subcommand
== "help":
1868 usage(retcode
=0, full
=True)
1869 elif not subcommand
:
1870 usage(_("no subcommand specified"))
1872 subcommand
= subcommand
.replace("-", "_")
1873 func
= globals().get("subcmd_{0}".format(subcommand
), None)
1875 subcommand
= subcommand
.replace("_", "-")
1876 usage(_("unknown subcommand '{0}'").format(subcommand
))
1879 return func(conf
, pargs
)
1880 except getopt
.GetoptError
as e
:
1881 if e
.opt
in ("help", "?"):
1883 usage(_("illegal option -- {0}").format(e
.opt
), cmd
=subcommand
)
1887 # Establish a specific exit status which means: "python barfed an exception"
1888 # so that we can more easily detect these in testing of the CLI commands.
1890 def handle_errors(func
, *args
, **kwargs
):
1891 """Catch exceptions raised by the main program function and then print
1892 a message and/or exit with an appropriate return code.
1895 traceback_str
= misc
.get_traceback_message()
1898 # Out of memory errors can be raised as EnvironmentErrors with
1899 # an errno of ENOMEM, so in order to handle those exceptions
1900 # with other errnos, we nest this try block and have the outer
1901 # one handle the other instances.
1903 __ret
= func(*args
, **kwargs
)
1904 except (MemoryError, EnvironmentError) as __e
:
1905 if isinstance(__e
, EnvironmentError) and \
1906 __e
.errno
!= errno
.ENOMEM
:
1908 error("\n" + misc
.out_of_memory())
1910 except SystemExit as __e
:
1912 except (IOError, PipeError
, KeyboardInterrupt) as __e
:
1913 # Don't display any messages here to prevent possible further
1914 # broken pipe (EPIPE) errors.
1915 if isinstance(__e
, IOError) and __e
.errno
!= errno
.EPIPE
:
1918 except apx
.VersionException
as __e
:
1919 error(_("The pkgrepo command appears out of sync with the "
1920 "libraries provided\nby pkg:/package/pkg. The client "
1921 "version is {client} while the library\nAPI version is "
1922 "{api}.").format(client
=__e
.received_version
,
1923 api
=__e
.expected_version
))
1925 except apx
.BadRepositoryURI
as __e
:
1928 except apx
.InvalidOptionError
as __e
:
1929 error("{0} Supported formats: {1}".format(
1930 str(__e
), LISTING_FORMATS
))
1932 except (apx
.ApiException
, sr
.RepositoryError
) as __e
:
1936 traceback
.print_exc()
1937 error(traceback_str
)
1942 if __name__
== "__main__":
1943 misc
.setlocale(locale
.LC_ALL
, "", error
)
1944 gettext
.install("pkg", "/usr/share/locale",
1945 codeset
=locale
.getpreferredencoding())
1947 # Make all warnings be errors.
1948 warnings
.simplefilter('error')
1950 __retval
= handle_errors(main_func
)
1954 # Ignore python's spurious pipe problems.