Remove M2Crypto and replace with cryptography module
[unleashed-pkg5.git] / src / modules / client / api_errors.py
blobe0a3c2782c4c8a73a52c2c8dec689e559b9aef87
1 #!/usr/bin/python2.7
3 # CDDL HEADER START
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]
20 # CDDL HEADER END
24 # Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
27 import errno
28 import operator
29 import os
30 import xml.parsers.expat as expat
31 import urlparse
33 # pkg classes
34 import pkg.client.pkgdefs as pkgdefs
36 # EmptyI for argument defaults; can't import from misc due to circular
37 # dependency.
38 EmptyI = tuple()
40 class ApiException(Exception):
41 def __init__(self, *args):
42 Exception.__init__(self)
43 self.__verbose_info = []
45 def __unicode__(self):
46 # To workaround python issues 6108 and 2517, this provides a
47 # a standard wrapper for this class' exceptions so that they
48 # have a chance of being stringified correctly.
49 return unicode(str(self))
51 def add_verbose_info(self, info):
52 self.__verbose_info.extend(info)
54 @property
55 def verbose_info(self):
56 return self.__verbose_info
58 class SuidUnsupportedError(ApiException):
59 def __str__(self):
60 return _("""
61 The pkg client api module can not be invoked from an setuid executable.""")
64 class HistoryException(ApiException):
65 """Private base exception class for all History exceptions."""
67 def __init__(self, *args):
68 Exception.__init__(self, *args)
69 self.error = args[0]
71 def __str__(self):
72 return str(self.error)
75 class HistoryLoadException(HistoryException):
76 """Used to indicate that an unexpected error occurred while loading
77 History operation information.
79 The first argument should be an exception object related to the
80 error encountered.
81 """
82 def __init__(self, *args):
83 HistoryException.__init__(self, *args)
84 self.parse_failure = isinstance(self.error, expat.ExpatError)
87 class HistoryRequestException(HistoryException):
88 """Used to indicate that invalid time / range values were provided to
89 history API functions."""
90 pass
93 class HistoryStoreException(HistoryException):
94 """Used to indicate that an unexpected error occurred while storing
95 History operation information.
97 The first argument should be an exception object related to the
98 error encountered.
99 """
100 pass
103 class HistoryPurgeException(HistoryException):
104 """Used to indicate that an unexpected error occurred while purging
105 History operation information.
107 The first argument should be an exception object related to the
108 error encountered.
110 pass
113 class ImageLockedError(ApiException):
114 """Used to indicate that the image is currently locked by another thread
115 or process and cannot be modified."""
117 def __init__(self, hostname=None, pid=None, pid_name=None):
118 ApiException.__init__(self)
119 self.hostname = hostname
120 self.pid = pid
121 self.pid_name = pid_name
123 def __str__(self):
124 if self.pid is not None and self.pid_name is not None and \
125 self.hostname is not None:
126 return _("The image cannot be modified as it is "
127 "currently in use by another package client: "
128 "{pid_name} on {host}, pid {pid}.").format(
129 pid_name=self.pid_name, pid=self.pid,
130 host=self.hostname)
131 if self.pid is not None and self.pid_name is not None:
132 return _("The image cannot be modified as it is "
133 "currently in use by another package client: "
134 "{pid_name} on an unknown host, pid {pid}.").format(
135 pid_name=self.pid_name, pid=self.pid)
136 elif self.pid is not None:
137 return _("The image cannot be modified as it is "
138 "currently in use by another package client: "
139 "pid {pid} on {host}.").format(
140 pid=self.pid, host=self.hostname)
141 return _("The image cannot be modified as it is currently "
142 "in use by another package client.")
144 class ImageNotFoundException(ApiException):
145 """Used when an image was not found"""
146 def __init__(self, user_specified, user_dir, root_dir):
147 ApiException.__init__(self)
148 self.user_specified = user_specified
149 self.user_dir = user_dir
150 self.root_dir = root_dir
152 def __str__(self):
153 return _("No image rooted at '{0}'").format(self.user_dir)
156 class ImageFormatUpdateNeeded(ApiException):
157 """Used to indicate that an image cannot be used until its format is
158 updated."""
160 def __init__(self, path):
161 ApiException.__init__(self)
162 self.path = path
164 def __str__(self):
165 return _("The image rooted at {0} is written in an older format "
166 "and must be updated before the requested operation can be "
167 "performed.").format(self.path)
169 class ImageInsufficentSpace(ApiException):
170 """Used when insuffcient space exists for proposed operation"""
171 def __init__(self, needed, avail, use):
172 self.needed = needed
173 self.avail = avail
174 self.use = use
176 def __str__(self):
177 from pkg.misc import bytes_to_str
178 return _("Insufficient disk space available ({avail}) "
179 "for estimated need ({needed}) for {use}").format(
180 avail=bytes_to_str(self.avail),
181 needed=bytes_to_str(self.needed),
182 use=self.use
186 class VersionException(ApiException):
187 def __init__(self, expected_version, received_version):
188 ApiException.__init__(self)
189 self.expected_version = expected_version
190 self.received_version = received_version
193 class PlanExistsException(ApiException):
194 def __init__(self, plan_type):
195 ApiException.__init__(self)
196 self.plan_type = plan_type
199 class PlanPrepareException(ApiException):
200 """Base exception class for plan preparation errors."""
201 pass
204 class InvalidPackageErrors(ApiException):
205 """Used to indicate that the requested operation could not be completed
206 as one or more packages contained invalid metadata."""
208 def __init__(self, errors):
209 """'errors' should be a list of exceptions or strings
210 indicating what packages had errors and why."""
212 ApiException.__init__(self)
213 self.errors = errors
215 def __str__(self):
216 return _("The requested operation cannot be completed due "
217 "to invalid package metadata. Details follow:\n\n"
218 "{0}").format("\n".join(str(e) for e in self.errors))
221 class LicenseAcceptanceError(ApiException):
222 """Used to indicate that license-related errors occurred during
223 plan evaluation or execution."""
225 def __init__(self, pfmri, src=None, dest=None, accepted=None,
226 displayed=None):
227 ApiException.__init__(self)
228 self.fmri = pfmri
229 self.src = src
230 self.dest = dest
231 self.accepted = accepted
232 self.displayed = displayed
235 class PkgLicenseErrors(PlanPrepareException):
236 """Used to indicate that plan evaluation or execution failed due
237 to license-related errors for a package."""
239 def __init__(self, errors):
240 """'errors' should be a list of LicenseAcceptanceError
241 exceptions."""
243 PlanPrepareException.__init__(self)
244 self.__errors = errors
246 @property
247 def errors(self):
248 """A list of LicenseAcceptanceError exceptions."""
249 return self.__errors
252 class PlanLicenseErrors(PlanPrepareException):
253 """Used to indicate that image plan evaluation or execution failed due
254 to license-related errors."""
256 def __init__(self, pp_errors):
257 """'errors' should be a list of PkgLicenseErrors exceptions."""
259 PlanPrepareException.__init__(self)
260 self.__errors = pkgs = {}
261 for pp_err in pp_errors:
262 for e in pp_err.errors:
263 pkgs.setdefault(str(e.fmri), []).append(e)
265 @property
266 def errors(self):
267 """Returns a dictionary indexed by package FMRI string of
268 lists of LicenseAcceptanceError exceptions."""
270 return self.__errors
272 def __str__(self):
273 """Returns a string representation of the license errors."""
275 output = ""
276 for sfmri in self.__errors:
277 output += ("-" * 40) + "\n"
278 output += _("Package: {0}\n\n").format(sfmri)
279 for e in self.__errors[sfmri]:
280 lic_name = e.dest.attrs["license"]
281 output += _("License: {0}\n").format(lic_name)
282 if e.dest.must_accept and not e.accepted:
283 output += _(" License requires "
284 "acceptance.")
285 if e.dest.must_display and not e.displayed:
286 output += _(" License must be viewed.")
287 output += "\n"
288 return output
291 class ActuatorException(ApiException):
292 def __init__(self, e):
293 ApiException.__init__(self)
294 self.exception = e
296 def __str__(self):
297 return str(self.exception)
300 class PrematureExecutionException(ApiException):
301 pass
304 class AlreadyPreparedException(PlanPrepareException):
305 pass
308 class AlreadyExecutedException(ApiException):
309 pass
312 class ImageplanStateException(ApiException):
313 def __init__(self, state):
314 ApiException.__init__(self)
315 self.state = state
318 class InvalidPlanError(ApiException):
319 """Used to indicate that the image plan is no longer valid, likely as a
320 result of an image state change since the plan was created."""
322 def __str__(self):
323 return _("The plan for the current operation is no longer "
324 "valid. The image has likely been modified by another "
325 "process or client. Please try the operation again.")
328 class ImagePkgStateError(ApiException):
330 def __init__(self, fmri, states):
331 ApiException.__init__(self)
332 self.fmri = fmri
333 self.states = states
335 def __str__(self):
336 return _("Invalid package state change attempted '{states}' "
337 "for package '{fmri}'.").format(states=self.states,
338 fmri=self.fmri)
341 class IpkgOutOfDateException(ApiException):
342 def __str__(self):
343 return _("pkg(5) out of date")
346 class ImageUpdateOnLiveImageException(ApiException):
347 def __str__(self):
348 return _("Requested operation cannot be performed "
349 "in live image.")
352 class RebootNeededOnLiveImageException(ApiException):
353 def __str__(self):
354 return _("Requested operation cannot be performed "
355 "in live image.")
358 class CanceledException(ApiException):
359 pass
361 class PlanMissingException(ApiException):
362 pass
364 class NoPackagesInstalledException(ApiException):
365 pass
367 class PermissionsException(ApiException):
368 def __init__(self, path):
369 ApiException.__init__(self)
370 self.path = path
372 def __str__(self):
373 if self.path:
374 return _("Could not operate on {0}\nbecause of "
375 "insufficient permissions. Please try the "
376 "command again as a privileged user.").format(
377 self.path)
378 else:
379 return _("""
380 Could not complete the operation because of insufficient permissions.
381 Please try the command again as a privileged user.
382 """)
384 class FileInUseException(PermissionsException):
385 def __init__(self, path):
386 PermissionsException.__init__(self, path)
387 assert path
389 def __str__(self):
390 return _("Could not operate on {0}\nbecause the file is "
391 "in use. Please stop using the file and try the\n"
392 "operation again.").format(self.path)
395 class UnprivilegedUserError(PermissionsException):
396 def __init__(self, path):
397 PermissionsException.__init__(self, path)
399 def __str__(self):
400 return _("Insufficient access to complete the requested "
401 "operation.\nPlease try the operation again as a "
402 "privileged user.")
405 class ReadOnlyFileSystemException(PermissionsException):
406 """Used to indicate that the operation was attempted on a
407 read-only filesystem"""
409 def __init__(self, path):
410 ApiException.__init__(self)
411 self.path = path
413 def __str__(self):
414 if self.path:
415 return _("Could not complete the operation on {0}: "
416 "read-only filesystem.").format(self.path)
417 return _("Could not complete the operation: read-only "
418 "filesystem.")
421 class InvalidLockException(ApiException):
422 def __init__(self, path):
423 ApiException.__init__(self)
424 self.path = path
426 def __str__(self):
427 return _("Unable to obtain or operate on lock at {0}.\n"
428 "Please try the operation again as a privileged "
429 "user.").format(self.path)
432 class PackageMatchErrors(ApiException):
433 """Used to indicate which patterns were not matched or illegal during
434 a package name matching operation."""
436 def __init__(self, unmatched_fmris=EmptyI, multiple_matches=EmptyI,
437 illegal=EmptyI, multispec=EmptyI):
438 ApiException.__init__(self)
439 self.unmatched_fmris = unmatched_fmris
440 self.multiple_matches = multiple_matches
441 self.illegal = illegal
442 self.multispec = multispec
444 def __str__(self):
445 res = []
446 if self.unmatched_fmris:
447 s = _("The following pattern(s) did not match any "
448 "packages:")
450 res += [s]
451 res += ["\t{0}".format(p) for p in self.unmatched_fmris]
453 if self.multiple_matches:
454 s = _("'{0}' matches multiple packages")
455 for p, lst in self.multiple_matches:
456 res.append(s.format(p))
457 for pfmri in lst:
458 res.append("\t{0}".format(pfmri))
460 if self.illegal:
461 s = _("'{0}' is an illegal FMRI")
462 res += [ s.format(p) for p in self.illegal ]
464 if self.multispec:
465 s = _("The following different patterns specify the "
466 "same package(s):")
467 res += [s]
468 for t in self.multispec:
469 res += [
470 ", ".join([t[i] for i in range(1, len(t))])
471 + ": {0}".format(t[0])
474 return "\n".join(res)
477 class PlanExecutionError(InvalidPlanError):
478 """Used to indicate that the requested operation could not be executed
479 due to unexpected changes in image state after planning was completed.
482 def __init__(self, paths):
483 self.paths = paths
485 def __str__(self):
486 return _("The files listed below were modified after operation "
487 "planning was complete or were missing during plan "
488 "execution; this may indicate an administrative issue or "
489 "system configuration issue:\n{0}".format(
490 "\n".join(list(self.paths))))
493 class PlanCreationException(ApiException):
494 def __init__(self,
495 already_installed=EmptyI,
496 badarch=EmptyI,
497 illegal=EmptyI,
498 installed=EmptyI,
499 invalid_mediations=EmptyI,
500 linked_pub_error=EmptyI,
501 missing_dependency=EmptyI,
502 missing_matches=EmptyI,
503 multiple_matches=EmptyI,
504 multispec=EmptyI,
505 no_solution=False,
506 no_tmp_origins=False,
507 no_version=EmptyI,
508 not_avoided=EmptyI,
509 nofiles=EmptyI,
510 obsolete=EmptyI,
511 pkg_updates_required=EmptyI,
512 rejected_pats=EmptyI,
513 solver_errors=EmptyI,
514 no_repo_pubs=EmptyI,
515 unmatched_fmris=EmptyI,
516 would_install=EmptyI,
517 wrong_publishers=EmptyI,
518 wrong_variants=EmptyI):
520 ApiException.__init__(self)
521 self.already_installed = already_installed
522 self.badarch = badarch
523 self.illegal = illegal
524 self.installed = installed
525 self.invalid_mediations = invalid_mediations
526 self.linked_pub_error = linked_pub_error
527 self.missing_dependency = missing_dependency
528 self.missing_matches = missing_matches
529 self.multiple_matches = multiple_matches
530 self.multispec = multispec
531 self.no_solution = no_solution
532 self.no_tmp_origins = no_tmp_origins
533 self.no_version = no_version
534 self.not_avoided = not_avoided
535 self.nofiles = nofiles
536 self.obsolete = obsolete
537 self.pkg_updates_required = pkg_updates_required
538 self.rejected_pats = rejected_pats
539 self.solver_errors = solver_errors
540 self.unmatched_fmris = unmatched_fmris
541 self.no_repo_pubs = no_repo_pubs
542 self.would_install = would_install
543 self.wrong_publishers = wrong_publishers
544 self.wrong_variants = wrong_variants
546 def __str__(self):
547 res = []
548 if self.unmatched_fmris:
549 s = _("""\
550 The following pattern(s) did not match any allowable packages. Try
551 using a different matching pattern, or refreshing publisher information:
552 """)
553 res += [s]
554 res += ["\t{0}".format(p) for p in self.unmatched_fmris]
556 if self.rejected_pats:
557 s = _("""\
558 The following pattern(s) only matched packages rejected by user request. Try
559 using a different matching pattern, or refreshing publisher information:
560 """)
561 res += [s]
562 res += ["\t{0}".format(p) for p in self.rejected_pats]
564 if self.wrong_variants:
565 s = _("""\
566 The following pattern(s) only matched packages that are not available
567 for the current image's architecture, zone type, and/or other variant:""")
568 res += [s]
569 res += ["\t{0}".format(p) for p in self.wrong_variants]
571 if self.wrong_publishers:
572 s = _("The following patterns only matched packages "
573 "that are from publishers other than that which "
574 "supplied the already installed version of this package")
575 res += [s]
576 res += ["\t{0}: {1}".format(p[0], ", ".join(p[1])) for p in self.wrong_publishers]
578 if self.multiple_matches:
579 s = _("'{0}' matches multiple packages")
580 for p, lst in self.multiple_matches:
581 res.append(s.format(p))
582 for pfmri in lst:
583 res.append("\t{0}".format(pfmri))
585 if self.missing_matches:
586 s = _("'{0}' matches no installed packages")
587 res += [ s.format(p) for p in self.missing_matches ]
589 if self.illegal:
590 s = _("'{0}' is an illegal fmri")
591 res += [ s.format(p) for p in self.illegal ]
593 if self.badarch:
594 s = _("'{p}' supports the following architectures: "
595 "{archs}")
596 a = _("Image architecture is defined as: {0}")
597 res += [ s.format(p=self.badarch[0],
598 archs=", ".join(self.badarch[1]))]
599 res += [ a.format(self.badarch[2])]
601 s = _("'{p}' depends on obsolete package '{op}'")
602 res += [ s.format(p=p, op=op) for p, op in self.obsolete ]
604 if self.installed:
605 s = _("The proposed operation can not be performed for "
606 "the following package(s) as they are already "
607 "installed: ")
608 res += [s]
609 res += ["\t{0}".format(p) for p in self.installed]
611 if self.invalid_mediations:
612 s = _("The following mediations are not syntactically "
613 "valid:")
614 for m, entries in self.invalid_mediations.iteritems():
615 for value, error in entries.values():
616 res.append(error)
618 if self.multispec:
619 s = _("The following patterns specify different "
620 "versions of the same package(s):")
621 res += [s]
622 for t in self.multispec:
623 res += [
624 ", ".join(
625 [t[i] for i in range(1, len(t))])
626 + ": {0}".format(t[0])
628 if self.no_solution:
629 res += [_("No solution was found to satisfy constraints")]
630 if isinstance(self.no_solution, list):
631 res.extend(self.no_solution)
633 if self.pkg_updates_required:
634 s = _("""\
635 Syncing this linked image would require the following package updates:
636 """)
637 res += [s]
638 for (oldfmri, newfmri) in self.pkg_updates_required:
639 res += ["{oldfmri} -> {newfmri}\n".format(
640 oldfmri=oldfmri, newfmri=newfmri)]
642 if self.no_version:
643 res += self.no_version
645 if self.no_tmp_origins:
646 s = _("""
647 The proposed operation on this parent image can not be performed because
648 temporary origins were specified and this image has children. Please either
649 retry the operation again without specifying any temporary origins, or if
650 packages from additional origins are required, please configure those origins
651 persistently.""")
652 res = [s]
654 if self.missing_dependency:
655 res += [_("Package {pkg} is missing a dependency: "
656 "{dep}").format(
657 pkg=self.missing_dependency[0],
658 dep=self.missing_dependency[1])]
659 if self.nofiles:
660 res += [_("The following files are not packaged in this image:")]
661 res += ["\t{0}".format(f) for f in self.nofiles]
663 if self.solver_errors:
664 res += ["\n"]
665 res += [_("Solver dependency errors:")]
666 res.extend(self.solver_errors)
668 if self.already_installed:
669 res += [_("The following packages are already "
670 "installed in this image; use uninstall to "
671 "avoid these:")]
672 res += [ "\t{0}".format(s) for s in self.already_installed]
674 if self.would_install:
675 res += [_("The following packages are a target "
676 "of group dependencies; use install to unavoid "
677 "these:")]
678 res += [ "\t{0}".format(s) for s in self.would_install]
680 if self.not_avoided:
681 res += [_("The following packages are not on the "
682 "avoid list, so they\ncannot be removed from it.")]
683 res += [ "\t{0}".format(s) for s in sorted(self.not_avoided)]
685 def __format_li_pubs(pubs, res):
686 i = 0
687 for pub, sticky in pubs:
688 s = " {0} {1:d}: {2}".format(_("PUBLISHER"),
689 i, pub)
690 mod = []
691 if not sticky:
692 mod.append(_("non-sticky"))
693 if mod:
694 s += " ({0})".format(",".join(mod))
695 res.append(s)
696 i += 1
698 if self.linked_pub_error:
699 res = []
700 (pubs, parent_pubs) = self.linked_pub_error
702 res.append(_("""
703 Invalid child image publisher configuration. Child image publisher
704 configuration must be a superset of the parent image publisher configuration.
705 Please update the child publisher configuration to match the parent. If the
706 child image is a zone this can be done automatically by detaching and
707 attaching the zone.
709 The parent image has the following enabled publishers:"""))
710 __format_li_pubs(parent_pubs, res)
711 res.append(_("""
712 The child image has the following enabled publishers:"""))
713 __format_li_pubs(pubs, res)
715 if self.no_repo_pubs:
716 res += [_("The following publishers do not have any "
717 "configured package repositories and cannot be "
718 "used in package dehydration or rehydration "
719 "operations:\n")]
720 res += ["\t{0}".format(s) for s in sorted(
721 self.no_repo_pubs)]
723 return "\n".join(res)
726 class ConflictingActionError(ApiException):
727 """Used to indicate that the imageplan would result in one or more sets
728 of conflicting actions, meaning that more than one action would exist on
729 the system with the same key attribute value in the same namespace.
730 There are three categories, each with its own subclass:
732 - multiple files delivered to the same path or drivers, users, groups,
733 etc, delivered with the same key attribute;
735 - multiple objects delivered to the same path which aren't the same
736 type;
738 - multiple directories, links, or hardlinks delivered to the same path
739 but with conflicting attributes.
742 def __init__(self, data):
743 self._data = data
745 class ConflictingActionErrors(ApiException):
746 """A container for multiple ConflictingActionError exception objects
747 that can be raised as a single exception."""
749 def __init__(self, errors):
750 self.__errors = errors
752 def __str__(self):
753 return "\n\n".join((str(err) for err in self.__errors))
755 class DuplicateActionError(ConflictingActionError):
756 """Multiple actions of the same type have been delivered with the same
757 key attribute (when not allowed)."""
759 def __str__(self):
760 pfmris = set((a[1] for a in self._data))
761 kv = self._data[0][0].attrs[self._data[0][0].key_attr]
762 action = self._data[0][0].name
763 if len(pfmris) > 1:
764 s = _("The following packages all deliver {action} "
765 "actions to {kv}:\n").format(**locals())
766 for a, p in self._data:
767 s += "\n {0}".format(p)
768 s += _("\n\nThese packages may not be installed together. "
769 "Any non-conflicting set may\nbe, or the packages "
770 "must be corrected before they can be installed.")
771 else:
772 pfmri = pfmris.pop()
773 s = _("The package {pfmri} delivers multiple copies "
774 "of {action} {kv}").format(**locals())
775 s += _("\nThis package must be corrected before it "
776 "can be installed.")
778 return s
780 class InconsistentActionTypeError(ConflictingActionError):
781 """Multiple actions of different types have been delivered with the same
782 'path' attribute. While this exception could represent other action
783 groups which share a single namespace, none such exist."""
785 def __str__(self):
786 ad = {}
787 pfmris = set()
788 kv = self._data[0][0].attrs[self._data[0][0].key_attr]
789 for a, p in self._data:
790 ad.setdefault(a.name, []).append(p)
791 pfmris.add(p)
793 if len(pfmris) > 1:
794 s = _("The following packages deliver conflicting "
795 "action types to {0}:\n").format(kv)
796 for name, pl in ad.iteritems():
797 s += "\n {0}:".format(name)
798 s += "".join("\n {0}".format(p) for p in pl)
799 s += _("\n\nThese packages may not be installed together. "
800 "Any non-conflicting set may\nbe, or the packages "
801 "must be corrected before they can be installed.")
802 else:
803 pfmri = pfmris.pop()
804 types = list_to_lang(ad.keys())
805 s = _("The package {pfmri} delivers conflicting "
806 "action types ({types}) to {kv}").format(**locals())
807 s += _("\nThis package must be corrected before it "
808 "can be installed.")
809 return s
811 class InconsistentActionAttributeError(ConflictingActionError):
812 """Multiple actions of the same type representing the same object have
813 have been delivered, but with conflicting attributes, such as two
814 directories at /usr with groups 'root' and 'sys', or two 'root' users
815 with uids '0' and '7'."""
817 def __str__(self):
818 actions = self._data
819 keyattr = actions[0][0].attrs[actions[0][0].key_attr]
820 actname = actions[0][0].name
822 # Trim the action's attributes to only those required to be
823 # unique.
824 def ou(action):
825 ua = dict(
826 (k, v)
827 for k, v in action.attrs.iteritems()
828 if ((k in action.unique_attrs and
829 not (k == "preserve" and "overlay" in action.attrs)) or
830 ((action.name == "link" or action.name == "hardlink") and
831 k.startswith("mediator")))
833 action.attrs = ua
834 return action
836 d = {}
837 for a in actions:
838 if a[0].attrs.get("implicit", "false") == "false":
839 d.setdefault(str(ou(a[0])), set()).add(a[1])
840 l = sorted([
841 (len(pkglist), action, pkglist)
842 for action, pkglist in d.iteritems()
845 s = _("The requested change to the system attempts to install "
846 "multiple actions\nfor {a} '{k}' with conflicting "
847 "attributes:\n\n").format(a=actname, k=keyattr)
848 allpkgs = set()
849 for num, action, pkglist in l:
850 allpkgs.update(pkglist)
851 if num <= 5:
852 if num == 1:
853 t = _(" {n:d} package delivers '{a}':\n")
854 else:
855 t = _(" {n:d} packages deliver '{a}':\n")
856 s += t.format(n=num, a=action)
857 for pkg in sorted(pkglist):
858 s += _(" {0}\n").format(pkg)
859 else:
860 t = _(" {n:d} packages deliver '{a}', including:\n")
861 s += t.format(n=num, a=action)
862 for pkg in sorted(pkglist)[:5]:
863 s += _(" {0}\n").format(pkg)
865 if len(allpkgs) == 1:
866 s += _("\nThis package must be corrected before it "
867 "can be installed.")
868 else:
869 s += _("\nThese packages may not be installed together."
870 " Any non-conflicting set may\nbe, or the packages "
871 "must be corrected before they can be installed.")
873 return s
875 def list_to_lang(l):
876 """Takes a list of items and puts them into a string, with commas in
877 between items, and an "and" between the last two items. Special cases
878 for lists of two or fewer items, and uses the Oxford comma."""
880 if not l:
881 return ""
882 if len(l) == 1:
883 return l[0]
884 if len(l) == 2:
885 # Used for a two-element list
886 return _("{penultimate} and {ultimate}").format(
887 penultimate=l[0],
888 ultimate=l[1]
890 # In order to properly i18n this construct, we create two templates:
891 # one for each element save the last, and one that tacks on the last
892 # element.
893 # 'elementtemplate' is for each element through the penultimate
894 elementtemplate = _("{0}, ")
895 # 'listtemplate' concatenates the concatenation of non-ultimate elements
896 # and the ultimate element.
897 listtemplate = _("{list}and {tail}")
898 return listtemplate.format(
899 list="".join(elementtemplate.format(i) for i in l[:-1]),
900 tail=l[-1]
903 class ActionExecutionError(ApiException):
904 """Used to indicate that action execution (such as install, remove,
905 etc.) failed even though the action is valid.
907 In particular, this exception indicates that something went wrong in the
908 application (or unapplication) of the action to the system, and is most
909 likely not an error in the pkg(5) code."""
911 def __init__(self, action, details=None, error=None, fmri=None,
912 use_errno=None):
913 """'action' is the object for the action that failed during the
914 requested operation.
916 'details' is an optional message explaining what operation
917 failed, why it failed, and why it cannot continue. It should
918 also include a suggestion as to how to resolve the situation
919 if possible.
921 'error' is an optional exception object that may have been
922 raised when the operation failed.
924 'fmri' is an optional package FMRI indicating what package
925 was being operated on at the time the error occurred.
927 'use_errno' is an optional boolean value indicating whether
928 the strerror() text of the exception should be used. If
929 'details' is provided, the default value is False, otherwise
930 True."""
932 assert (details or error)
933 self.action = action
934 self.details = details
935 self.error = error
936 self.fmri = fmri
937 if use_errno == None:
938 # If details were provided, don't use errno unless
939 # explicitly requested.
940 use_errno = not details
941 self.use_errno = use_errno
943 def __str__(self):
944 errno = ""
945 if self.use_errno and self.error and \
946 hasattr(self.error, "errno"):
947 errno = "[errno {0:d}: {1}]".format(self.error.errno,
948 os.strerror(self.error.errno))
950 details = self.details or ""
952 # Fall back on the wrapped exception if we don't have anything
953 # useful.
954 if not errno and not details:
955 return str(self.error)
957 if errno and details:
958 details = "{0}: {1}".format(errno, details)
960 if details and not self.fmri:
961 details = _("Requested operation failed for action "
962 "{action}:\n{details}").format(
963 action=self.action,
964 details=details)
965 elif details:
966 details = _("Requested operation failed for package "
967 "{fmri}:\n{details}").format(fmri=self.fmri,
968 details=details)
970 # If we only have one of the two, no need for the colon.
971 return "{0}{1}".format(errno, details)
974 class CatalogRefreshException(ApiException):
975 def __init__(self, failed, total, succeeded, errmessage=None):
976 ApiException.__init__(self)
977 self.failed = failed
978 self.total = total
979 self.succeeded = succeeded
980 self.errmessage = errmessage
983 class CatalogError(ApiException):
984 """Base exception class for all catalog exceptions."""
986 def __init__(self, *args, **kwargs):
987 ApiException.__init__(self)
988 if args:
989 self.data = args[0]
990 else:
991 self.data = None
992 self._args = kwargs
994 def __str__(self):
995 return str(self.data)
998 class AnarchicalCatalogFMRI(CatalogError):
999 """Used to indicate that the specified FMRI is not valid for catalog
1000 operations because it is missing publisher information."""
1002 def __str__(self):
1003 return _("The FMRI '{0}' does not contain publisher information "
1004 "and cannot be used for catalog operations.").format(
1005 self.data)
1008 class BadCatalogMetaRoot(CatalogError):
1009 """Used to indicate an operation on the catalog's meta_root failed
1010 because the meta_root is invalid."""
1012 def __str__(self):
1013 return _("Catalog meta_root '{root}' is invalid; unable "
1014 "to complete operation: '{op}'.").format(root=self.data,
1015 op=self._args.get("operation", None))
1018 class BadCatalogPermissions(CatalogError):
1019 """Used to indicate the server catalog files do not have the expected
1020 permissions."""
1022 def __init__(self, files):
1023 """files should contain a list object with each entry consisting
1024 of a tuple of filename, expected_mode, received_mode."""
1025 if not files:
1026 files = []
1027 CatalogError.__init__(self, files)
1029 def __str__(self):
1030 msg = _("The following catalog files have incorrect "
1031 "permissions:\n")
1032 for f in self.data:
1033 fname, emode, fmode = f
1034 msg += _("\t{fname}: expected mode: {emode}, found "
1035 "mode: {fmode}\n").format(fname=fname,
1036 emode=emode, fmode=fmode)
1037 return msg
1040 class BadCatalogSignatures(CatalogError):
1041 """Used to indicate that the Catalog signatures are not valid."""
1043 def __str__(self):
1044 return _("The signature data for the '{0}' catalog file is not "
1045 "valid.").format(self.data)
1048 class BadCatalogUpdateIdentity(CatalogError):
1049 """Used to indicate that the requested catalog updates could not be
1050 applied as the new catalog data is significantly different such that
1051 the old catalog cannot be updated to match it."""
1053 def __str__(self):
1054 return _("Unable to determine the updates needed for "
1055 "the current catalog using the provided catalog "
1056 "update data in '{0}'.").format(self.data)
1059 class DuplicateCatalogEntry(CatalogError):
1060 """Used to indicate that the specified catalog operation could not be
1061 performed since it would result in a duplicate catalog entry."""
1063 def __str__(self):
1064 return _("Unable to perform '{op}' operation for catalog "
1065 "{name}; completion would result in a duplicate entry "
1066 "for package '{fmri}'.").format(op=self._args.get(
1067 "operation", None), name=self._args.get("catalog_name",
1068 None), fmri=self.data)
1071 class CatalogUpdateRequirements(CatalogError):
1072 """Used to indicate that an update request for the catalog could not
1073 be performed because update requirements were not satisfied."""
1075 def __str__(self):
1076 return _("Catalog updates can only be applied to an on-disk "
1077 "catalog.")
1080 class InvalidCatalogFile(CatalogError):
1081 """Used to indicate a Catalog file could not be loaded."""
1083 def __str__(self):
1084 return _("Catalog file '{0}' is invalid.\nUse 'pkgrepo rebuild' "
1085 "to create a new package catalog.").format(self.data)
1088 class MismatchedCatalog(CatalogError):
1089 """Used to indicate that a Catalog's attributes and parts do not
1090 match. This is likely the result of an attributes file being
1091 retrieved which doesn't match the parts that were retrieved such
1092 as in a misconfigured or stale cache case."""
1094 def __str__(self):
1095 return _("The content of the catalog for publisher '{0}' "
1096 "doesn't match the catalog's attributes. This is "
1097 "likely the result of a mix of older and newer "
1098 "catalog files being provided for the publisher.").format(
1099 self.data)
1102 class ObsoleteCatalogUpdate(CatalogError):
1103 """Used to indicate that the specified catalog updates are for an older
1104 version of the catalog and cannot be applied."""
1106 def __str__(self):
1107 return _("Unable to determine the updates needed for the "
1108 "catalog using the provided catalog update data in '{0}'. "
1109 "The specified catalog updates are for an older version "
1110 "of the catalog and cannot be used.").format(self.data)
1113 class UnknownCatalogEntry(CatalogError):
1114 """Used to indicate that an entry for the specified package FMRI or
1115 pattern could not be found in the catalog."""
1117 def __str__(self):
1118 return _("'{0}' could not be found in the catalog.").format(
1119 self.data)
1122 class UnknownUpdateType(CatalogError):
1123 """Used to indicate that the specified CatalogUpdate operation is
1124 unknown."""
1126 def __str__(self):
1127 return _("Unknown catalog update type '{0}'").format(self.data)
1130 class UnrecognizedCatalogPart(CatalogError):
1131 """Raised when the catalog finds a CatalogPart that is unrecognized
1132 or invalid."""
1134 def __str__(self):
1135 return _("Unrecognized, unknown, or invalid CatalogPart '{0}'").format(
1136 self.data)
1139 class InventoryException(ApiException):
1140 """Used to indicate that some of the specified patterns to a catalog
1141 matching function did not match any catalog entries, or were invalid
1142 patterns."""
1144 def __init__(self, illegal=EmptyI, matcher=EmptyI, notfound=EmptyI,
1145 publisher=EmptyI, version=EmptyI):
1146 ApiException.__init__(self)
1147 self.illegal = illegal
1148 self.matcher = matcher
1149 self.notfound = set(notfound)
1150 self.publisher = publisher
1151 self.version = version
1153 self.notfound.update(matcher)
1154 self.notfound.update(publisher)
1155 self.notfound.update(version)
1156 self.notfound = sorted(list(self.notfound))
1158 assert self.illegal or self.notfound
1160 def __str__(self):
1161 outstr = ""
1162 for x in self.illegal:
1163 # Illegal FMRIs have their own __str__ method
1164 outstr += "{0}\n".format(x)
1166 if self.matcher or self.publisher or self.version:
1167 outstr += _("No matching package could be found for "
1168 "the following FMRIs in any of the catalogs for "
1169 "the current publishers:\n")
1171 for x in self.matcher:
1172 outstr += \
1173 _("{0} (pattern did not match)\n").format(x)
1174 for x in self.publisher:
1175 outstr += _("{0} (publisher did not "
1176 "match)\n").format(x)
1177 for x in self.version:
1178 outstr += \
1179 _("{0} (version did not match)\n").format(x)
1180 return outstr
1183 # SearchExceptions
1185 class SearchException(ApiException):
1186 """Based class used for all search-related api exceptions."""
1187 pass
1190 class MalformedSearchRequest(SearchException):
1191 """Raised when the server cannot understand the format of the
1192 search request."""
1194 def __init__(self, url):
1195 SearchException.__init__(self)
1196 self.url = url
1198 def __str__(self):
1199 return str(self.url)
1202 class NegativeSearchResult(SearchException):
1203 """Returned when the search cannot find any matches."""
1205 def __init__(self, url):
1206 SearchException.__init__(self)
1207 self.url = url
1209 def __str__(self):
1210 return _("The search at url {0} returned no results.").format(
1211 self.url)
1214 class ProblematicSearchServers(SearchException):
1215 """This class wraps exceptions which could appear while trying to
1216 do a search request."""
1218 def __init__(self, failed=EmptyI, invalid=EmptyI, unsupported=EmptyI):
1219 SearchException.__init__(self)
1220 self.failed_servers = failed
1221 self.invalid_servers = invalid
1222 self.unsupported_servers = unsupported
1224 def __str__(self):
1225 s = _("Some repositories failed to respond appropriately:\n")
1226 for pub, err in self.failed_servers:
1227 s += _("{o}:\n{msg}\n").format(
1228 o=pub, msg=err)
1229 for pub in self.invalid_servers:
1230 s += _("{0} did not return a valid "
1231 "response.\n".format(pub))
1232 if len(self.unsupported_servers) > 0:
1233 s += _("Some repositories don't support requested "
1234 "search operation:\n")
1235 for pub, err in self.unsupported_servers:
1236 s += _("{o}:\n{msg}\n").format(
1237 o=pub, msg=err)
1239 return s
1242 class SlowSearchUsed(SearchException):
1243 """This exception is thrown when a local search is performed without
1244 an index. It's raised after all results have been yielded."""
1246 def __str__(self):
1247 return _("Search performance is degraded.\n"
1248 "Run 'pkg rebuild-index' to improve search speed.")
1251 class UnsupportedSearchError(SearchException):
1252 """Returned when a search protocol is not supported by the
1253 remote server."""
1255 def __init__(self, url=None, proto=None):
1256 SearchException.__init__(self)
1257 self.url = url
1258 self.proto = proto
1260 def __str__(self):
1261 s = _("Search repository does not support the requested "
1262 "protocol:")
1263 if self.url:
1264 s += "\nRepository URL: {0}".format(self.url)
1265 if self.proto:
1266 s += "\nRequested operation: {0}".format(self.proto)
1267 return s
1269 def __cmp__(self, other):
1270 if not isinstance(other, UnsupportedSearchError):
1271 return -1
1272 r = cmp(self.url, other.url)
1273 if r != 0:
1274 return r
1275 return cmp(self.proto, other.proto)
1278 # IndexingExceptions.
1280 class IndexingException(SearchException):
1281 """ The base class for all exceptions that can occur while indexing. """
1283 def __init__(self, private_exception):
1284 SearchException.__init__(self)
1285 self.cause = private_exception.cause
1288 class CorruptedIndexException(IndexingException):
1289 """This is used when the index is not in a correct state."""
1290 def __str__(self):
1291 return _("The search index appears corrupted.")
1294 class InconsistentIndexException(IndexingException):
1295 """This is used when the existing index is found to have inconsistent
1296 versions."""
1297 def __init__(self, e):
1298 IndexingException.__init__(self, e)
1299 self.exception = e
1301 def __str__(self):
1302 return str(self.exception)
1305 class IndexLockedException(IndexingException):
1306 """This is used when an attempt to modify an index locked by another
1307 process or thread is made."""
1309 def __init__(self, e):
1310 IndexingException.__init__(self, e)
1311 self.exception = e
1313 def __str__(self):
1314 return str(self.exception)
1317 class ProblematicPermissionsIndexException(IndexingException):
1318 """ This is used when the indexer is unable to create, move, or remove
1319 files or directories it should be able to. """
1320 def __str__(self):
1321 return "Could not remove or create " \
1322 "{0} because of incorrect " \
1323 "permissions. Please correct this issue then " \
1324 "rebuild the index.".format(self.cause)
1326 class WrapIndexingException(ApiException):
1327 """This exception is used to wrap an indexing exception during install,
1328 uninstall, or update so that a more appropriate error message can be
1329 displayed to the user."""
1331 def __init__(self, e, tb, stack):
1332 ApiException.__init__(self)
1333 self.wrapped = e
1334 self.tb = tb
1335 self.stack = stack
1337 def __str__(self):
1338 tmp = self.tb.split("\n")
1339 res = tmp[:1] + [s.rstrip("\n") for s in self.stack] + tmp[1:]
1340 return "\n".join(res)
1343 class WrapSuccessfulIndexingException(WrapIndexingException):
1344 """This exception is used to wrap an indexing exception during install,
1345 uninstall, or update which was recovered from by performing a full
1346 reindex."""
1347 pass
1350 # Query Parsing Exceptions
1351 class BooleanQueryException(ApiException):
1352 """This exception is used when the children of a boolean operation
1353 have different return types. The command 'pkg search foo AND <bar>'
1354 is the simplest example of this."""
1356 def __init__(self, e):
1357 ApiException.__init__(self)
1358 self.e = e
1360 def __str__(self):
1361 return str(self.e)
1364 class ParseError(ApiException):
1365 def __init__(self, e):
1366 ApiException.__init__(self)
1367 self.e = e
1369 def __str__(self):
1370 return str(self.e)
1373 class NonLeafPackageException(ApiException):
1374 """Removal of a package which satisfies dependencies has been attempted.
1376 The first argument to the constructor is the FMRI which we tried to
1377 remove, and is available as the "fmri" member of the exception. The
1378 second argument is the list of dependent packages that prevent the
1379 removal of the package, and is available as the "dependents" member.
1382 def __init__(self, *args):
1383 ApiException.__init__(self, *args)
1385 self.fmri = args[0]
1386 self.dependents = args[1]
1388 def __str__(self):
1389 s = _("Unable to remove '{0}' due to the following packages "
1390 "that depend on it:\n").format(self.fmri.get_short_fmri(
1391 anarchy=True, include_scheme=False))
1392 skey = operator.attrgetter('pkg_name')
1393 s += "\n".join(
1394 " {0}".format(f.get_short_fmri(anarchy=True,
1395 include_scheme=False))
1396 for f in sorted(self.dependents, key=skey)
1398 return s
1400 def _str_autofix(self):
1402 if getattr(self, "_autofix_pkgs", []):
1403 s = _("\nThis is happening because the following "
1404 "packages needed to be repaired as\npart of this "
1405 "operation:\n\n ")
1406 s += "\n ".join(str(f) for f in self._autofix_pkgs)
1407 s += _("\n\nYou will need to reestablish your access to the "
1408 "repository or remove the\npackages in the list above.")
1409 return s
1410 return ""
1412 class InvalidDepotResponseException(ApiException):
1413 """Raised when the depot doesn't have versions of operations
1414 that the client needs to operate successfully."""
1415 def __init__(self, url, data):
1416 ApiException.__init__(self)
1417 self.url = url
1418 self.data = data
1420 def __str__(self):
1421 s = _("Unable to contact valid package repository")
1422 if self.url:
1423 s += _(": {0}").format(self.url)
1424 if self.data:
1425 s += ("\nEncountered the following error(s):\n{0}").format(
1426 self.data)
1428 s += _str_autofix(self)
1430 return s
1432 class DataError(ApiException):
1433 """Base exception class used for all data related errors."""
1435 def __init__(self, *args, **kwargs):
1436 ApiException.__init__(self, *args)
1437 if args:
1438 self.data = args[0]
1439 else:
1440 self.data = None
1441 self._args = kwargs
1444 class InvalidP5IFile(DataError):
1445 """Used to indicate that the specified location does not contain a
1446 valid p5i-formatted file."""
1448 def __str__(self):
1449 if self.data:
1450 return _("The provided p5i data is in an unrecognized "
1451 "format or does not contain valid publisher "
1452 "information: {0}").format(self.data)
1453 return _("The provided p5i data is in an unrecognized format "
1454 "or does not contain valid publisher information.")
1457 class InvalidP5SFile(DataError):
1458 """Used to indicate that the specified location does not contain a
1459 valid p5i-formatted file."""
1461 def __str__(self):
1462 if self.data:
1463 return _("The provided p5s data is in an unrecognized "
1464 "format or does not contain valid publisher "
1465 "information: {0}").format(self.data)
1466 return _("The provided p5s data is in an unrecognized format "
1467 "or does not contain valid publisher information.")
1470 class UnsupportedP5IFile(DataError):
1471 """Used to indicate that an attempt to read an unsupported version
1472 of pkg(5) info file was attempted."""
1474 def __str__(self):
1475 return _("Unsupported pkg(5) publisher information data "
1476 "format.")
1479 class UnsupportedP5SFile(DataError):
1480 """Used to indicate that an attempt to read an unsupported version
1481 of pkg(5) info file was attempted."""
1483 def __str__(self):
1484 return _("Unsupported pkg(5) publisher and image information "
1485 "data format.")
1488 class UnsupportedP5SVersion(ApiException):
1489 """Used to indicate that an attempt to read an unsupported version
1490 of pkg(5) info file was attempted."""
1492 def __init__(self, v):
1493 self.version = v
1495 def __str__(self):
1496 return _("{0} is not a supported version for creating a "
1497 "syspub response.").format(self.version)
1500 class TransportError(ApiException):
1501 """Abstract exception class for all transport exceptions.
1502 Specific transport exceptions should be implemented in the
1503 transport code. Callers wishing to catch transport exceptions
1504 should use this class. Subclasses must implement all methods
1505 defined here that raise NotImplementedError."""
1507 def __str__(self):
1508 raise NotImplementedError()
1510 def _str_autofix(self):
1511 return _str_autofix(self)
1514 class RetrievalError(ApiException):
1515 """Used to indicate that a a requested resource could not be
1516 retrieved."""
1518 def __init__(self, data, location=None):
1519 ApiException.__init__(self)
1520 self.data = data
1521 self.location = location
1523 def __str__(self):
1524 if self.location:
1525 return _("Error encountered while retrieving data from "
1526 "'{location}':\n{data}").format(
1527 location=self.location, data=self.data)
1528 return _("Error encountered while retrieving data from: {0}").format(
1529 self.data)
1532 class InvalidResourceLocation(ApiException):
1533 """Used to indicate that an invalid transport location was provided."""
1535 def __init__(self, data):
1536 ApiException.__init__(self)
1537 self.data = data
1539 def __str__(self):
1540 return _("'{0}' is not a valid location.").format(self.data)
1542 class BEException(ApiException):
1543 def __init__(self):
1544 ApiException.__init__(self)
1546 class InvalidBENameException(BEException):
1547 def __init__(self, be_name):
1548 BEException.__init__(self)
1549 self.be_name = be_name
1551 def __str__(self):
1552 return _("'{0}' is not a valid boot environment name.").format(
1553 self.be_name)
1555 class DuplicateBEName(BEException):
1556 """Used to indicate that there is an existing boot environment
1557 with this name"""
1559 def __init__(self, be_name):
1560 BEException.__init__(self)
1561 self.be_name = be_name
1563 def __str__(self):
1564 return _("The boot environment '{0}' already exists.").format(
1565 self.be_name)
1567 class BENamingNotSupported(BEException):
1568 def __init__(self, be_name):
1569 BEException.__init__(self)
1570 self.be_name = be_name
1572 def __str__(self):
1573 return _("""\
1574 Boot environment naming during package install is not supported on this
1575 version of OpenSolaris. Please update without the --be-name option.""")
1577 class UnableToCopyBE(BEException):
1578 def __str__(self):
1579 return _("Unable to clone the current boot environment.")
1581 class UnableToRenameBE(BEException):
1582 def __init__(self, orig, dest):
1583 BEException.__init__(self)
1584 self.original_name = orig
1585 self.destination_name = dest
1587 def __str__(self):
1588 d = {
1589 "orig": self.original_name,
1590 "dest": self.destination_name
1592 return _("""\
1593 A problem occurred while attempting to rename the boot environment
1594 currently named {orig} to {dest}.""").format(**d)
1596 class UnableToMountBE(BEException):
1597 def __init__(self, be_name, be_dir):
1598 BEException.__init__(self)
1599 self.name = be_name
1600 self.mountpoint = be_dir
1602 def __str__(self):
1603 return _("Unable to mount {name} at {mt}").format(
1604 name=self.name, mt=self.mountpoint)
1606 class BENameGivenOnDeadBE(BEException):
1607 def __init__(self, be_name):
1608 BEException.__init__(self)
1609 self.name = be_name
1611 def __str__(self):
1612 return _("""\
1613 Naming a boot environment when operating on a non-live image is
1614 not allowed.""")
1617 class UnrecognizedOptionsToInfo(ApiException):
1618 def __init__(self, opts):
1619 ApiException.__init__(self)
1620 self._opts = opts
1622 def __str__(self):
1623 s = _("Info does not recognize the following options:")
1624 for o in self._opts:
1625 s += _(" '") + str(o) + _("'")
1626 return s
1628 class IncorrectIndexFileHash(ApiException):
1629 """This is used when the index hash value doesn't match the hash of the
1630 packages installed in the image."""
1631 pass
1634 class PublisherError(ApiException):
1635 """Base exception class for all publisher exceptions."""
1637 def __init__(self, *args, **kwargs):
1638 ApiException.__init__(self, *args)
1639 if args:
1640 self.data = args[0]
1641 else:
1642 self.data = None
1643 self._args = kwargs
1645 def __str__(self):
1646 return str(self.data)
1649 class BadPublisherMetaRoot(PublisherError):
1650 """Used to indicate an operation on the publisher's meta_root failed
1651 because the meta_root is invalid."""
1653 def __str__(self):
1654 return _("Publisher meta_root '{root}' is invalid; unable "
1655 "to complete operation: '{op}'.").format(root=self.data,
1656 op=self._args.get("operation", None))
1659 class BadPublisherAlias(PublisherError):
1660 """Used to indicate that a publisher alias is not valid."""
1662 def __str__(self):
1663 return _("'{0}' is not a valid publisher alias.").format(
1664 self.data)
1667 class BadPublisherPrefix(PublisherError):
1668 """Used to indicate that a publisher name is not valid."""
1670 def __str__(self):
1671 return _("'{0}' is not a valid publisher name.").format(
1672 self.data)
1675 class ReservedPublisherPrefix(PublisherError):
1676 """Used to indicate that a publisher name is not valid."""
1678 def __str__(self):
1679 fmri = self._args["fmri"]
1680 return _("'{pkg_pub}' is a reserved publisher and does not "
1681 "contain the requested package: pkg:/{pkg_name}").format(
1682 pkg_pub=fmri.publisher, pkg_name=fmri.pkg_name)
1685 class BadRepositoryAttributeValue(PublisherError):
1686 """Used to indicate that the specified repository attribute value is
1687 invalid."""
1689 def __str__(self):
1690 return _("'{value}' is not a valid value for repository "
1691 "attribute '{attribute}'.").format(
1692 value=self._args["value"], attribute=self.data)
1695 class BadRepositoryCollectionType(PublisherError):
1696 """Used to indicate that the specified repository collection type is
1697 invalid."""
1699 def __init__(self, *args, **kwargs):
1700 PublisherError.__init__(self, *args, **kwargs)
1702 def __str__(self):
1703 return _("'{0}' is not a valid repository collection type.").format(
1704 self.data)
1707 class BadRepositoryURI(PublisherError):
1708 """Used to indicate that a repository URI is not syntactically valid."""
1710 def __str__(self):
1711 return _("'{0}' is not a valid URI.").format(self.data)
1714 class BadRepositoryURIPriority(PublisherError):
1715 """Used to indicate that the priority specified for a repository URI is
1716 not valid."""
1718 def __str__(self):
1719 return _("'{0}' is not a valid URI priority; integer value "
1720 "expected.").format(self.data)
1723 class BadRepositoryURISortPolicy(PublisherError):
1724 """Used to indicate that the specified repository URI sort policy is
1725 invalid."""
1727 def __init__(self, *args, **kwargs):
1728 PublisherError.__init__(self, *args, **kwargs)
1730 def __str__(self):
1731 return _("'{0}' is not a valid repository URI sort policy.").format(
1732 self.data)
1735 class DisabledPublisher(PublisherError):
1736 """Used to indicate that an attempt to use a disabled publisher occurred
1737 during an operation."""
1739 def __str__(self):
1740 return _("Publisher '{0}' is disabled and cannot be used for "
1741 "packaging operations.").format(self.data)
1744 class DuplicatePublisher(PublisherError):
1745 """Used to indicate that a publisher with the same name or alias already
1746 exists for an image."""
1748 def __str__(self):
1749 return _("A publisher with the same name or alias as '{0}' "
1750 "already exists.").format(self.data)
1753 class DuplicateRepository(PublisherError):
1754 """Used to indicate that a repository with the same origin uris
1755 already exists for a publisher."""
1757 def __str__(self):
1758 return _("A repository with the same name or origin URIs "
1759 "already exists for publisher '{0}'.").format(self.data)
1762 class DuplicateRepositoryMirror(PublisherError):
1763 """Used to indicate that a repository URI is already in use by another
1764 repository mirror."""
1766 def __str__(self):
1767 return _("Mirror '{0}' already exists for the specified "
1768 "publisher.").format(self.data)
1771 class DuplicateSyspubMirror(PublisherError):
1772 """Used to indicate that a repository URI is already in use by the
1773 system publisher."""
1775 def __str__(self):
1776 return _("Mirror '{0}' is already accessible through the "
1777 "system repository.").format(self.data)
1780 class DuplicateRepositoryOrigin(PublisherError):
1781 """Used to indicate that a repository URI is already in use by another
1782 repository origin."""
1784 def __str__(self):
1785 return _("Origin '{0}' already exists for the specified "
1786 "publisher.").format(self.data)
1789 class DuplicateSyspubOrigin(PublisherError):
1790 """Used to indicate that a repository URI is already in use by the
1791 system publisher."""
1793 def __str__(self):
1794 return _("Origin '{0}' is already accessible through the "
1795 "system repository.").format(self.data)
1798 class RemoveSyspubOrigin(PublisherError):
1799 """Used to indicate that a system publisher origin may not be
1800 removed."""
1802 def __str__(self):
1803 return _("Unable to remove origin '{0}' since it is provided "
1804 "by the system repository.").format(self.data)
1806 class RemoveSyspubMirror(PublisherError):
1807 """Used to indicate that a system publisher mirror may not be
1808 removed."""
1810 def __str__(self):
1811 return _("Unable to remove mirror '{0}' since it is provided "
1812 "by the system repository.").format(self.data)
1815 class NoPublisherRepositories(TransportError):
1816 """Used to indicate that a Publisher has no repository information
1817 configured and so transport operations cannot be performed."""
1819 def __init__(self, prefix):
1820 TransportError.__init__(self)
1821 self.publisher = prefix
1823 def __str__(self):
1824 return _("""
1825 The requested operation requires that one or more package repositories are
1826 configured for publisher '{0}'.
1828 Use 'pkg set-publisher' to add new package repositories or restore previously
1829 configured package repositories for publisher '{0}'.""").format(self.publisher)
1832 class MoveRelativeToSelf(PublisherError):
1833 """Used to indicate an attempt to search a repo before or after itself"""
1835 def __str__(self):
1836 return _("Cannot search a repository before or after itself")
1839 class MoveRelativeToUnknown(PublisherError):
1840 """Used to indicate an attempt to order a publisher relative to an
1841 unknown publisher."""
1843 def __init__(self, unknown_pub):
1844 self.__unknown_pub = unknown_pub
1846 def __str__(self):
1847 return _("{0} is an unknown publisher; no other publishers can "
1848 "be ordered relative to it.").format(self.__unknown_pub)
1851 class SelectedRepositoryRemoval(PublisherError):
1852 """Used to indicate that an attempt to remove the selected repository
1853 for a publisher was made."""
1855 def __str__(self):
1856 return _("Cannot remove the selected repository for a "
1857 "publisher.")
1860 class UnknownLegalURI(PublisherError):
1861 """Used to indicate that no matching legal URI could be found using the
1862 provided criteria."""
1864 def __str__(self):
1865 return _("Unknown legal URI '{0}'.").format(self.data)
1868 class UnknownPublisher(PublisherError):
1869 """Used to indicate that no matching publisher could be found using the
1870 provided criteria."""
1872 def __str__(self):
1873 return _("Unknown publisher '{0}'.").format(self.data)
1876 class UnknownRepositoryPublishers(PublisherError):
1877 """Used to indicate that one or more publisher prefixes are unknown by
1878 the specified repository."""
1880 def __init__(self, known=EmptyI, unknown=EmptyI, location=None,
1881 origins=EmptyI):
1882 ApiException.__init__(self)
1883 self.known = known
1884 self.location = location
1885 self.origins = origins
1886 self.unknown = unknown
1888 def __str__(self):
1889 if self.location:
1890 return _("The repository at {location} does not "
1891 "contain package data for {unknown}; only "
1892 "{known}.\n\nThis is either because the "
1893 "repository location is not valid, or because the "
1894 "provided publisher does not match those known by "
1895 "the repository.").format(
1896 unknown=", ".join(self.unknown),
1897 location=self.location,
1898 known=", ".join(self.known))
1899 if self.origins:
1900 return _("One or more of the repository origin(s) "
1901 "listed below contains package data for "
1902 "{known}; not {unknown}:\n\n{origins}\n\n"
1903 "This is either because one of the repository "
1904 "origins is not valid for this publisher, or "
1905 "because the list of known publishers retrieved "
1906 "from the repository origin does not match the "
1907 "client.").format(unknown=", ".join(self.unknown),
1908 known=", ".join(self.known),
1909 origins="\n".join(str(o) for o in self.origins))
1910 return _("The specified publisher repository does not "
1911 "contain any package data for {unknown}; only "
1912 "{known}.").format(unknown=", ".join(self.unknown),
1913 known=", ".join(self.known))
1916 class UnknownRelatedURI(PublisherError):
1917 """Used to indicate that no matching related URI could be found using
1918 the provided criteria."""
1920 def __str__(self):
1921 return _("Unknown related URI '{0}'.").format(self.data)
1924 class UnknownRepository(PublisherError):
1925 """Used to indicate that no matching repository could be found using the
1926 provided criteria."""
1928 def __str__(self):
1929 return _("Unknown repository '{0}'.").format(self.data)
1932 class UnknownRepositoryMirror(PublisherError):
1933 """Used to indicate that a repository URI could not be found in the
1934 list of repository mirrors."""
1936 def __str__(self):
1937 return _("Unknown repository mirror '{0}'.").format(self.data)
1939 class UnsupportedRepositoryOperation(TransportError):
1940 """The publisher has no active repositories that support the
1941 requested operation."""
1943 def __init__(self, pub, operation):
1944 ApiException.__init__(self)
1945 self.data = None
1946 self.kwargs = None
1947 self.pub = pub
1948 self.op = operation
1950 def __str__(self):
1951 return _("Publisher '{pub}' has no repositories that support "
1952 "the '{op}' operation.").format(**self.__dict__)
1955 class RepoPubConfigUnavailable(PublisherError):
1956 """Used to indicate that the specified repository does not provide
1957 publisher configuration information."""
1959 def __init__(self, location=None, pub=None):
1960 ApiException.__init__(self)
1961 self.location = location
1962 self.pub = pub
1964 def __str__(self):
1965 if not self.location and not self.pub:
1966 return _("The specified package repository does not "
1967 "provide publisher configuration information.")
1968 if self.location:
1969 return _("The package repository at {0} does not "
1970 "provide publisher configuration information or "
1971 "the information provided is incomplete.").format(
1972 self.location)
1973 return _("One of the package repository origins for {0} does "
1974 "not provide publisher configuration information or the "
1975 "information provided is incomplete.").format(self.pub)
1978 class UnknownRepositoryOrigin(PublisherError):
1979 """Used to indicate that a repository URI could not be found in the
1980 list of repository origins."""
1982 def __str__(self):
1983 return _("Unknown repository origin '{0}'").format(self.data)
1986 class UnsupportedRepositoryURI(PublisherError):
1987 """Used to indicate that the specified repository URI uses an
1988 unsupported scheme."""
1990 def __init__(self, uris=[]):
1991 if isinstance(uris, basestring):
1992 uris = [uris]
1994 assert isinstance(uris, (list, tuple, set))
1996 self.uris = uris
1998 def __str__(self):
1999 illegals = []
2001 for u in self.uris:
2002 assert isinstance(u, basestring)
2003 scheme = urlparse.urlsplit(u,
2004 allow_fragments=0)[0]
2005 illegals.append((u, scheme))
2007 if len(illegals) > 1:
2008 msg = _("The follwing URIs use unsupported "
2009 "schemes. Supported schemes are "
2010 "file://, http://, and https://.")
2011 for i, s in illegals:
2012 msg += _("\n {uri} (scheme: "
2013 "{scheme})").format(uri=i, scheme=s)
2014 return msg
2015 elif len(illegals) == 1:
2016 i, s = illegals[0]
2017 return _("The URI '{uri}' uses the unsupported "
2018 "scheme '{scheme}'. Supported schemes are "
2019 "file://, http://, and https://.").format(
2020 uri=i, scheme=s)
2021 return _("The specified URI uses an unsupported scheme."
2022 " Supported schemes are: file://, http://, and "
2023 "https://.")
2026 class UnsupportedRepositoryURIAttribute(PublisherError):
2027 """Used to indicate that the specified repository URI attribute is not
2028 supported for the URI's scheme."""
2030 def __str__(self):
2031 return _("'{attr}' is not supported for '{scheme}'.").format(
2032 attr=self.data, scheme=self._args["scheme"])
2035 class UnsupportedProxyURI(PublisherError):
2036 """Used to indicate that the specified proxy URI is unsupported."""
2038 def __str__(self):
2039 if self.data:
2040 scheme = urlparse.urlsplit(self.data,
2041 allow_fragments=0)[0]
2042 return _("The proxy URI '{uri}' uses the unsupported "
2043 "scheme '{scheme}'. Supported schemes are "
2044 "http://, and https://.").format(
2045 uri=self.data, scheme=scheme)
2046 return _("The specified proxy URI uses an unsupported scheme."
2047 " Supported schemes are: http://, and https://.")
2049 class BadProxyURI(PublisherError):
2050 """Used to indicate that a proxy URI is not syntactically valid."""
2052 def __str__(self):
2053 return _("'{0}' is not a valid URI.").format(self.data)
2056 class UnknownSysrepoConfiguration(ApiException):
2057 """Used when a pkg client needs to communicate with the system
2058 repository but can't find the configuration for it."""
2060 def __str__(self):
2061 return _("""\
2062 pkg is configured to use the system repository (via the use-system-repo
2063 property) but it could not get the host and port from
2064 svc:/application/pkg/zones-proxy-client nor svc:/application/pkg/system-repository, and
2065 the PKG_SYSREPO_URL environment variable was not set. Please try enabling one
2066 of those services or setting the PKG_SYSREPO_URL environment variable.
2067 """)
2070 class ModifyingSyspubException(ApiException):
2071 """This exception is raised when a user attempts to modify a system
2072 publisher."""
2074 def __init__(self, s):
2075 self.s = s
2077 def __str__(self):
2078 return self.s
2081 class SigningException(ApiException):
2082 """The base class for exceptions related to manifest signing."""
2084 def __init__(self, pfmri=None, sig=None):
2085 self.pfmri = pfmri
2086 self.sig = sig
2088 # This string method is used by subclasses to fill in the details
2089 # about the package and signature involved.
2090 def __str__(self):
2091 if self.pfmri:
2092 if self.sig:
2093 return _("The relevant signature action is "
2094 "found in {pfmri} and has a hash of "
2095 "{hsh}").format(
2096 pfmri=self.pfmri, hsh=self.sig.hash)
2097 return _("The package involved is {0}").format(
2098 self.pfmri)
2099 if self.sig:
2100 return _("The relevant signature action's value "
2101 "attribute is {0}").format(self.sig.attrs["value"])
2102 return ""
2105 class BadFileFormat(SigningException):
2106 """Exception used when a key, certificate or CRL file is not in a
2107 recognized format."""
2109 def __init__(self, txt):
2110 self.txt = txt
2112 def __str__(self):
2113 return self.txt
2116 class UnsupportedSignatureVersion(SigningException):
2117 """Exception used when a signature reports a version which this version
2118 of pkg(5) doesn't support."""
2120 def __init__(self, version, *args, **kwargs):
2121 SigningException.__init__(self, *args, **kwargs)
2122 self.version = version
2124 def __str__(self):
2125 return _("The signature action {act} was made using a "
2126 "version ({ver}) this version of pkg(5) doesn't "
2127 "understand.").format(act=self.sig, ver=self.version)
2130 class CertificateException(SigningException):
2131 """Base class for exceptions encountered while establishing the chain
2132 of trust."""
2134 def __init__(self, cert, pfmri=None):
2135 SigningException.__init__(self, pfmri)
2136 self.cert = cert
2139 class ModifiedCertificateException(CertificateException):
2140 """Exception used when a certificate does not match its expected hash
2141 value."""
2143 def __init__(self, cert, path, pfmri=None):
2144 CertificateException.__init__(self, cert, pfmri)
2145 self.path = path
2147 def __str__(self):
2148 return _("Certificate {0} has been modified on disk. Its hash "
2149 "value is not what was expected.").format(self.path)
2152 class UntrustedSelfSignedCert(CertificateException):
2153 """Exception used when a chain of trust is rooted in an untrusted
2154 self-signed certificate."""
2156 def __str__(self):
2157 return _("Chain was rooted in an untrusted self-signed "
2158 "certificate.\n") + CertificateException.__str__(self)
2161 class BrokenChain(CertificateException):
2162 """Exception used when a chain of trust can not be established between
2163 the leaf certificate and a trust anchor."""
2165 def __init__(self, cert, cert_exceptions, *args, **kwargs):
2166 CertificateException.__init__(self, cert, *args, **kwargs)
2167 self.ext_exs = cert_exceptions
2169 def __str__(self):
2170 s = ""
2171 if self.ext_exs:
2172 s = _("The following problems were encountered:\n") + \
2173 "\n".join([str(e) for e in self.ext_exs])
2174 return _("The certificate which issued this "
2175 "certificate: {subj} could not be found. The issuer "
2176 "is: {issuer}\n").format(subj="/".join("{0}={1}".format(
2177 sub.oid._name, sub.value) for sub in self.cert.subject),
2178 issuer="/".join("{0}={1}".format(i.oid._name, i.value)
2179 for i in self.cert.issuer)) + s + "\n" + \
2180 CertificateException.__str__(self)
2183 class RevokedCertificate(CertificateException):
2184 """Exception used when a chain of trust contains a revoked certificate.
2187 def __init__(self, cert, reason, *args, **kwargs):
2188 CertificateException.__init__(self, cert, *args, **kwargs)
2189 self.reason = reason
2191 def __str__(self):
2192 return _("This certificate was revoked:{cert} for this "
2193 "reason:\n{reason}\n").format(cert="/".join("{0}={1}".format(
2194 s.oid._name, s.value) for s in self.cert.subject),
2195 reason=self.reason) + CertificateException.__str__(self)
2198 class UnverifiedSignature(SigningException):
2199 """Exception used when a signature could not be verified by the
2200 expected certificate."""
2202 def __init__(self, sig, reason, pfmri=None):
2203 SigningException.__init__(self, pfmri)
2204 self.sig = sig
2205 self.reason = reason
2207 def __str__(self):
2208 if self.pfmri:
2209 return _("A signature in {pfmri} could not be "
2210 "verified for "
2211 "this reason:\n{reason}\nThe signature's hash is "
2212 "{hash}").format(pfmri=self.pfmri,
2213 reason=self.reason,
2214 hash=self.sig.hash)
2215 return _("The signature with this signature value:\n"
2216 "{sigval}\n could not be verified for this reason:\n"
2217 "{reason}\n").format(reason=self.reason,
2218 sigval=self.sig.attrs["value"])
2221 class RequiredSignaturePolicyException(SigningException):
2222 """Exception used when signatures were required but none were found."""
2224 def __init__(self, pub, pfmri=None):
2225 SigningException.__init__(self, pfmri)
2226 self.pub = pub
2228 def __str__(self):
2229 pub_str = self.pub.prefix
2230 if self.pfmri:
2231 return _("The policy for {pub_str} requires "
2232 "signatures to be present but no signature was "
2233 "found in {fmri_str}.").format(
2234 pub_str=pub_str, fmri_str=self.pfmri)
2235 return _("The policy for {pub_str} requires signatures to be "
2236 "present but no signature was found.").format(
2237 pub_str=pub_str)
2240 class MissingRequiredNamesException(SigningException):
2241 """Exception used when a signature policy required names to be seen
2242 which weren't seen."""
2244 def __init__(self, pub, missing_names, pfmri=None):
2245 SigningException.__init__(self, pfmri)
2246 self.pub = pub
2247 self.missing_names = missing_names
2249 def __str__(self):
2250 pub_str = self.pub.prefix
2251 if self.pfmri:
2252 return _("The policy for {pub_str} requires certain "
2253 "CNs to be seen in a chain of trust. The following "
2254 "required names couldn't be found for this "
2255 "package:{fmri_str}.\n{missing}").format(
2256 pub_str=pub_str, fmri_str=self.pfmri,
2257 missing="\n".join(self.missing_names))
2258 return _("The policy for {pub_str} requires certain CNs to "
2259 "be seen in a chain of trust. The following required names "
2260 "couldn't be found.\n{missing}").format(pub_str=pub_str,
2261 missing="\n".join(self.missing_names))
2263 class UnsupportedCriticalExtension(SigningException):
2264 """Exception used when a certificate in the chain of trust uses a
2265 critical extension pkg doesn't understand."""
2267 def __init__(self, cert, ext):
2268 SigningException.__init__(self)
2269 self.cert = cert
2270 self.ext = ext
2272 def __str__(self):
2273 return _("The certificate whose subject is {cert} could not "
2274 "be verified because it uses an unsupported critical "
2275 "extension.\nExtension name: {name}\nExtension "
2276 "value: {val}").format(cert="/".join("{0}={1}".format(
2277 s.oid._name, s.value) for s in self.cert.subject),
2278 name=self.ext.oid._name, val=self.ext.value)
2280 class UnsupportedExtensionValue(SigningException):
2281 """Exception used when a certificate in the chain of trust has an
2282 extension with a value pkg doesn't understand."""
2284 def __init__(self, cert, ext, val, bad_val=None):
2285 SigningException.__init__(self)
2286 self.cert = cert
2287 self.ext = ext
2288 self.val = val
2289 self.bad_val = bad_val
2291 def __str__(self):
2292 s = _("The certificate whose subject is {cert} could not be "
2293 "verified because it has an extension with a value that "
2294 "pkg(5) does not understand."
2295 "\nExtension name: {name}\nExtension value: {val}").format(
2296 cert="/".join("{0}={1}".format(
2297 s.oid._name, s.value) for s in self.cert.subject),
2298 name=self.ext.oid._name, val=self.val)
2299 if self.bad_val:
2300 s += _("\nProblematic value: {0}").format(self.bad_val)
2301 return s
2303 class InvalidCertificateExtensions(SigningException):
2304 """Exception used when a certificate in the chain of trust has
2305 invalid extensions."""
2307 def __init__(self, cert, error):
2308 SigningException.__init__(self)
2309 self.cert = cert
2310 self.error = error
2312 def __str__(self):
2313 s = _("The certificate whose subject is {cert} could not be "
2314 "verified because it has invalid extensions:\n{error}"
2315 ).format(cert="/".join("{0}={1}".format(
2316 s.oid._name, s.value) for s in self.cert.subject),
2317 error=self.error)
2318 return s
2320 class InappropriateCertificateUse(SigningException):
2321 """Exception used when a certificate in the chain of trust has been used
2322 inappropriately. An example would be a certificate which was only
2323 supposed to be used to sign code being used to sign other certificates.
2326 def __init__(self, cert, ext, use, val):
2327 SigningException.__init__(self)
2328 self.cert = cert
2329 self.ext = ext
2330 self.use = use
2331 self.val = val
2333 def __str__(self):
2334 return _("The certificate whose subject is {cert} could not "
2335 "be verified because it has been used inappropriately. "
2336 "The way it is used means that the value for extension "
2337 "{name} must include '{use}' but the value was "
2338 "'{val}'.").format(cert="/".join("{0}={1}".format(
2339 s.oid._name, s.value) for s in self.cert.subject),
2340 use=self.use, name=self.ext.oid._name,
2341 val=self.val)
2343 class PathlenTooShort(InappropriateCertificateUse):
2344 """Exception used when a certificate in the chain of trust has been used
2345 inappropriately. An example would be a certificate which was only
2346 supposed to be used to sign code being used to sign other certificates.
2349 def __init__(self, cert, actual_length, cert_length):
2350 SigningException.__init__(self)
2351 self.cert = cert
2352 self.al = actual_length
2353 self.cl = cert_length
2355 def __str__(self):
2356 return _("The certificate whose subject is {cert} could not "
2357 "be verified because it has been used inappropriately. "
2358 "There can only be {cl} certificates between this "
2359 "certificate and the leaf certificate. There are {al} "
2360 "certificates between this certificate and the leaf in "
2361 "this chain.").format(
2362 cert="/".join("{0}={1}".format(
2363 s.oid._name, s.value) for s in self.cert.subject),
2364 al=self.al,
2365 cl=self.cl
2369 class AlmostIdentical(ApiException):
2370 """Exception used when a package already has a signature action which is
2371 nearly identical to the one being added but differs on some
2372 attributes."""
2374 def __init__(self, hsh, algorithm, version, pkg=None):
2375 self.hsh = hsh
2376 self.algorithm = algorithm
2377 self.version = version
2378 self.pkg = pkg
2380 def __str__(self):
2381 s = _("The signature to be added to the package has the same "
2382 "hash ({hash}), algorithm ({algorithm}), and "
2383 "version ({version}) as an existing signature, but "
2384 "doesn't match the signature exactly. For this signature "
2385 "to be added, the existing signature must be removed.").format(
2386 hash=self.hsh,
2387 algorithm=self.algorithm,
2388 version=self.version
2390 if self.pkg:
2391 s += _("The package being signed was {pkg}").format(
2392 pkg=self.pkg)
2393 return s
2396 class DuplicateSignaturesAlreadyExist(ApiException):
2397 """Exception used when a package already has a signature action which is
2398 nearly identical to the one being added but differs on some
2399 attributes."""
2401 def __init__(self, pfmri):
2402 self.pfmri = pfmri
2404 def __str__(self):
2405 return _("{0} could not be signed because it already has two "
2406 "copies of this signature in it. One of those signature "
2407 "actions must be removed before the package is given to "
2408 "users.").format(self.pfmri)
2411 class InvalidPropertyValue(ApiException):
2412 """Exception used when a property was set to an invalid value."""
2414 def __init__(self, s):
2415 ApiException.__init__(self)
2416 self.str = s
2418 def __str__(self):
2419 return self.str
2422 class CertificateError(ApiException):
2423 """Base exception class for all certificate exceptions."""
2425 def __init__(self, *args, **kwargs):
2426 ApiException.__init__(self, *args)
2427 if args:
2428 self.data = args[0]
2429 else:
2430 self.data = None
2431 self._args = kwargs
2433 def __str__(self):
2434 return str(self.data)
2437 class ExpiredCertificate(CertificateError):
2438 """Used to indicate that a certificate has expired."""
2440 def __init__(self, *args, **kwargs):
2441 CertificateError.__init__(self, *args, **kwargs)
2442 self.publisher = self._args.get("publisher", None)
2443 self.uri = self._args.get("uri", None)
2445 def __str__(self):
2446 if self.publisher:
2447 if self.uri:
2448 return _("Certificate '{cert}' for publisher "
2449 "'{pub}' needed to access '{uri}', "
2450 "has expired. Please install a valid "
2451 "certificate.").format(cert=self.data,
2452 pub=self.publisher, uri=self.uri)
2453 return _("Certificate '{cert}' for publisher "
2454 "'{pub}', has expired. Please install a valid "
2455 "certificate.").format(cert=self.data,
2456 pub=self.publisher)
2457 if self.uri:
2458 return _("Certificate '{cert}', needed to access "
2459 "'{uri}', has expired. Please install a valid "
2460 "certificate.").format(cert=self.data,
2461 uri=self.uri)
2462 return _("Certificate '{0}' has expired. Please install a "
2463 "valid certificate.").format(self.data)
2466 class ExpiredCertificates(CertificateError):
2467 """Used to collect ExpiredCertficate exceptions."""
2469 def __init__(self, errors):
2471 self.errors = []
2473 assert (isinstance(errors, (list, tuple,
2474 set, ExpiredCertificate)))
2476 if isinstance(errors, ExpiredCertificate):
2477 self.errors.append(errors)
2478 else:
2479 self.errors = errors
2481 def __str__(self):
2482 pdict = dict()
2483 for e in self.errors:
2484 if e.publisher in pdict:
2485 pdict[e.publisher].append(e.uri)
2486 else:
2487 pdict[e.publisher] = [e.uri]
2489 msg = ""
2490 for pub, uris in pdict.items():
2491 msg += "\n{0}:".format(_("Publisher"))
2492 msg += " {0}".format(pub)
2493 for uri in uris:
2494 msg += "\n {0}:\n".format(_("Origin URI"))
2495 msg += " {0}\n".format(uri)
2496 msg += " {0}:\n".format(_("Certificate"))
2497 msg += " {0}\n".format(uri.ssl_cert)
2498 msg += " {0}:\n".format(_("Key"))
2499 msg += " {0}\n".format(uri.ssl_key)
2500 return _("One or more client key and certificate files have "
2501 "expired. Please\nupdate the configuration for the "
2502 "publishers or origins listed below:\n {0}").format(msg)
2505 class ExpiringCertificate(CertificateError):
2506 """Used to indicate that a certificate has expired."""
2508 def __str__(self):
2509 publisher = self._args.get("publisher", None)
2510 uri = self._args.get("uri", None)
2511 days = self._args.get("days", 0)
2512 if publisher:
2513 if uri:
2514 return _("Certificate '{cert}' for publisher "
2515 "'{pub}', needed to access '{uri}', "
2516 "will expire in '{days}' days.").format(
2517 cert=self.data, pub=publisher,
2518 uri=uri, days=days)
2519 return _("Certificate '{cert}' for publisher "
2520 "'{pub}' will expire in '{days}' days.").format(
2521 cert=self.data, pub=publisher, days=days)
2522 if uri:
2523 return _("Certificate '{cert}', needed to access "
2524 "'{uri}', will expire in '{days}' days.").format(
2525 cert=self.data, uri=uri, days=days)
2526 return _("Certificate '{cert}' will expire in "
2527 "'{days}' days.").format(cert=self.data, days=days)
2530 class InvalidCertificate(CertificateError):
2531 """Used to indicate that a certificate is invalid."""
2533 def __str__(self):
2534 publisher = self._args.get("publisher", None)
2535 uri = self._args.get("uri", None)
2536 if publisher:
2537 if uri:
2538 return _("Certificate '{cert}' for publisher "
2539 "'{pub}', needed to access '{uri}', is "
2540 "invalid.").format(cert=self.data,
2541 pub=publisher, uri=uri)
2542 return _("Certificate '{cert}' for publisher "
2543 "'{pub}' is invalid.").format(cert=self.data,
2544 pub=publisher)
2545 if uri:
2546 return _("Certificate '{cert}' needed to access "
2547 "'{uri}' is invalid.").format(cert=self.data,
2548 uri=uri)
2549 return _("Invalid certificate '{0}'.").format(self.data)
2552 class NoSuchKey(CertificateError):
2553 """Used to indicate that a key could not be found."""
2555 def __str__(self):
2556 publisher = self._args.get("publisher", None)
2557 uri = self._args.get("uri", None)
2558 if publisher:
2559 if uri:
2560 return _("Unable to locate key '{key}' for "
2561 "publisher '{pub}' needed to access "
2562 "'{uri}'.").format(key=self.data,
2563 pub=publisher, uri=uri)
2564 return _("Unable to locate key '{key}' for publisher "
2565 "'{pub}'.").format(key=self.data, pub=publisher
2567 if uri:
2568 return _("Unable to locate key '{key}' needed to "
2569 "access '{uri}'.").format(key=self.data,
2570 uri=uri)
2571 return _("Unable to locate key '{0}'.").format(self.data)
2574 class NoSuchCertificate(CertificateError):
2575 """Used to indicate that a certificate could not be found."""
2577 def __str__(self):
2578 publisher = self._args.get("publisher", None)
2579 uri = self._args.get("uri", None)
2580 if publisher:
2581 if uri:
2582 return _("Unable to locate certificate "
2583 "'{cert}' for publisher '{pub}' needed "
2584 "to access '{uri}'.").format(
2585 cert=self.data, pub=publisher,
2586 uri=uri)
2587 return _("Unable to locate certificate '{cert}' for "
2588 "publisher '{pub}'.").format(cert=self.data,
2589 pub=publisher)
2590 if uri:
2591 return _("Unable to locate certificate '{cert}' "
2592 "needed to access '{uri}'.").format(
2593 cert=self.data, uri=uri)
2594 return _("Unable to locate certificate '{0}'.").format(
2595 self.data)
2598 class NotYetValidCertificate(CertificateError):
2599 """Used to indicate that a certificate is not yet valid (future
2600 effective date)."""
2602 def __str__(self):
2603 publisher = self._args.get("publisher", None)
2604 uri = self._args.get("uri", None)
2605 if publisher:
2606 if uri:
2607 return _("Certificate '{cert}' for publisher "
2608 "'{pub}', needed to access '{uri}', "
2609 "has a future effective date.").format(
2610 cert=self.data, pub=publisher,
2611 uri=uri)
2612 return _("Certificate '{cert}' for publisher "
2613 "'{pub}' has a future effective date.").format(
2614 cert=self.data, pub=publisher)
2615 if uri:
2616 return _("Certificate '{cert}' needed to access "
2617 "'{uri}' has a future effective date.").format(
2618 cert=self.data, uri=uri)
2619 return _("Certificate '{0}' has a future effective date.").format(
2620 self.data)
2623 class ServerReturnError(ApiException):
2624 """This exception is used when the server returns a line which the
2625 client cannot parse correctly."""
2627 def __init__(self, line):
2628 ApiException.__init__(self)
2629 self.line = line
2631 def __str__(self):
2632 return _("Gave a bad response:{0}").format(self.line)
2635 class MissingFileArgumentException(ApiException):
2636 """This exception is used when a file was given as an argument but
2637 no such file could be found."""
2638 def __init__(self, path):
2639 ApiException.__init__(self)
2640 self.path = path
2642 def __str__(self):
2643 return _("Could not find {0}").format(self.path)
2646 class ManifestError(ApiException):
2647 """Base exception class for all manifest exceptions."""
2649 def __init__(self, *args, **kwargs):
2650 ApiException.__init__(self, *args, **kwargs)
2651 if args:
2652 self.data = args[0]
2653 else:
2654 self.data = None
2655 self._args = kwargs
2657 def __str__(self):
2658 return str(self.data)
2661 class BadManifestSignatures(ManifestError):
2662 """Used to indicate that the Manifest signatures are not valid."""
2664 def __str__(self):
2665 if self.data:
2666 return _("The signature data for the manifest of the "
2667 "'{0}' package is not valid.").format(self.data)
2668 return _("The signature data for the manifest is not valid.")
2671 class UnknownErrors(ApiException):
2672 """Used to indicate that one or more exceptions were encountered.
2673 This is intended for use with where multiple exceptions for multiple
2674 files are encountered and the errors have been condensed into a
2675 single exception and re-raised. One example case would be rmtree()
2676 with shutil.Error."""
2678 def __init__(self, msg):
2679 ApiException.__init__(self)
2680 self.__msg = msg
2682 def __str__(self):
2683 return self.__msg
2686 # Image creation exceptions
2687 class ImageCreationException(ApiException):
2688 def __init__(self, path):
2689 ApiException.__init__(self)
2690 self.path = path
2692 def __str__(self):
2693 raise NotImplementedError()
2696 class ImageAlreadyExists(ImageCreationException):
2697 def __str__(self):
2698 return _("there is already an image at: {0}.\nTo override, use "
2699 "the -f (force) option.").format(self.path)
2702 class ImageCfgEmptyError(ApiException):
2703 """Used to indicate that the image configuration is invalid."""
2705 def __str__(self):
2706 return _("The configuration data for the image rooted at "
2707 "{0} is empty or missing.").format(self.data)
2710 class UnsupportedImageError(ApiException):
2711 """Used to indicate that the image at a specific location is in a format
2712 not supported by this version of the pkg(5) API."""
2714 def __init__(self, path):
2715 ApiException.__init__(self)
2716 self.path = path
2718 def __str__(self):
2719 return _("The image rooted at {0} is invalid or is not "
2720 "supported by this version of the packaging system.").format(
2721 self.path)
2724 class CreatingImageInNonEmptyDir(ImageCreationException):
2725 def __str__(self):
2726 return _("the specified image path is not empty: {0}.\nTo "
2727 "override, use the -f (force) option.").format(self.path)
2730 def _convert_error(e, ignored_errors=EmptyI):
2731 """Converts the provided exception into an ApiException equivalent if
2732 possible. Returns a new exception object if converted or the original
2733 if not.
2735 'ignored_errors' is an optional list of errno values for which None
2736 should be returned.
2739 if not hasattr(e, "errno"):
2740 return e
2741 if e.errno in ignored_errors:
2742 return None
2743 if e.errno in (errno.EACCES, errno.EPERM):
2744 return PermissionsException(e.filename)
2745 if e.errno == errno.EROFS:
2746 return ReadOnlyFileSystemException(e.filename)
2747 return e
2749 class LinkedImageException(ApiException):
2751 def __init__(self, bundle=None, lin=None, exitrv=None,
2752 attach_bad_prop=None,
2753 attach_bad_prop_value=None,
2754 attach_child_notsup=None,
2755 attach_parent_notsup=None,
2756 attach_root_as_child=None,
2757 attach_with_curpath=None,
2758 child_bad_img=None,
2759 child_diverged=None,
2760 child_dup=None,
2761 child_not_in_altroot=None,
2762 child_not_nested=None,
2763 child_op_failed=None,
2764 child_path_notabs=None,
2765 child_unknown=None,
2766 cmd_failed=None,
2767 cmd_output_invalid=None,
2768 detach_child_notsup=None,
2769 detach_from_parent=None,
2770 detach_parent_notsup=None,
2771 img_linked=None,
2772 intermediate_image=None,
2773 lin_malformed=None,
2774 link_to_self=None,
2775 parent_bad_img=None,
2776 parent_bad_notabs=None,
2777 parent_bad_path=None,
2778 parent_nested=None,
2779 parent_not_in_altroot=None,
2780 pkg_op_failed=None,
2781 self_linked=None,
2782 self_not_child=None,
2783 unparsable_output=None):
2785 self.attach_bad_prop = attach_bad_prop
2786 self.attach_bad_prop_value = attach_bad_prop_value
2787 self.attach_child_notsup = attach_child_notsup
2788 self.attach_parent_notsup = attach_parent_notsup
2789 self.attach_root_as_child = attach_root_as_child
2790 self.attach_with_curpath = attach_with_curpath
2791 self.child_bad_img = child_bad_img
2792 self.child_diverged = child_diverged
2793 self.child_dup = child_dup
2794 self.child_not_in_altroot = child_not_in_altroot
2795 self.child_not_nested = child_not_nested
2796 self.child_op_failed = child_op_failed
2797 self.child_path_notabs = child_path_notabs
2798 self.child_unknown = child_unknown
2799 self.cmd_failed = cmd_failed
2800 self.cmd_output_invalid = cmd_output_invalid
2801 self.detach_child_notsup = detach_child_notsup
2802 self.detach_from_parent = detach_from_parent
2803 self.detach_parent_notsup = detach_parent_notsup
2804 self.img_linked = img_linked
2805 self.intermediate_image = intermediate_image
2806 self.lin_malformed = lin_malformed
2807 self.link_to_self = link_to_self
2808 self.parent_bad_img = parent_bad_img
2809 self.parent_bad_notabs = parent_bad_notabs
2810 self.parent_bad_path = parent_bad_path
2811 self.parent_nested = parent_nested
2812 self.parent_not_in_altroot = parent_not_in_altroot
2813 self.pkg_op_failed = pkg_op_failed
2814 self.self_linked = self_linked
2815 self.self_not_child = self_not_child
2816 self.unparsable_output = unparsable_output
2818 # first deal with an error bundle
2819 if bundle:
2820 assert type(bundle) in [tuple, list, set]
2821 for e in bundle:
2822 assert isinstance(e, LinkedImageException)
2824 # set default error return value
2825 if exitrv == None:
2826 exitrv = pkgdefs.EXIT_OOPS
2828 self.lix_err = None
2829 self.lix_bundle = bundle
2830 self.lix_exitrv = exitrv
2831 return
2833 err = None
2835 if attach_bad_prop is not None:
2836 err = _("Invalid linked image attach property: {0}").format(
2837 attach_bad_prop)
2839 if attach_bad_prop_value is not None:
2840 assert type(attach_bad_prop_value) in [tuple, list]
2841 assert len(attach_bad_prop_value) == 2
2842 err = _("Invalid linked image attach property "
2843 "value: {0}").format(
2844 "=".join(attach_bad_prop_value))
2846 if attach_child_notsup is not None:
2847 err = _("Linked image type does not support child "
2848 "attach: {0}").format(attach_child_notsup)
2850 if attach_parent_notsup is not None:
2851 err = _("Linked image type does not support parent "
2852 "attach: {0}").format(attach_parent_notsup)
2854 if attach_root_as_child is not None:
2855 err = _("Cannot attach root image as child: {0}".format(
2856 attach_root_as_child))
2858 if attach_with_curpath is not None:
2859 path, curpath = attach_with_curpath
2860 err = _("Cannot link images when an image is not at "
2861 "its default location. The image currently "
2862 "located at:\n {curpath}\n"
2863 "is normally located at:\n {path}\n").format(
2864 path=path,
2865 curpath=curpath,
2868 if child_bad_img is not None:
2869 if exitrv == None:
2870 exitrv = pkgdefs.EXIT_EACCESS
2871 if lin:
2872 err = _("Can't initialize child image "
2873 "({lin}) at path: {path}").format(
2874 lin=lin,
2875 path=child_bad_img
2877 else:
2878 err = _("Can't initialize child image "
2879 "at path: {0}").format(child_bad_img)
2881 if child_diverged is not None:
2882 if exitrv == None:
2883 exitrv = pkgdefs.EXIT_DIVERGED
2884 err = _("Linked image is diverged: {0}").format(
2885 child_diverged)
2887 if child_dup is not None:
2888 err = _("A linked child image with this name "
2889 "already exists: {0}").format(child_dup)
2891 if child_not_in_altroot is not None:
2892 path, altroot = child_not_in_altroot
2893 err = _("Child image '{path}' is not located "
2894 "within the parent's altroot '{altroot}'").format(
2895 path=path,
2896 altroot=altroot
2899 if child_not_nested is not None:
2900 cpath, ppath = child_not_nested
2901 err = _("Child image '{cpath}' is not nested "
2902 "within the parent image '{ppath}'").format(
2903 cpath=cpath,
2904 ppath=ppath,
2907 if child_op_failed is not None:
2908 op, cpath, e = child_op_failed
2909 if exitrv == None:
2910 exitrv = pkgdefs.EXIT_EACCESS
2911 if lin:
2912 err = _("Failed '{op}' for child image "
2913 "({lin}) at path: {path}: "
2914 "{strerror}").format(
2915 op=op,
2916 lin=lin,
2917 path=cpath,
2918 strerror=e,
2920 else:
2921 err = _("Failed '{op}' for child image "
2922 "at path: {path}: {strerror}").format(
2923 op=op,
2924 path=cpath,
2925 strerror=e,
2928 if child_path_notabs is not None:
2929 err = _("Child path not absolute: {0}").format(
2930 child_path_notabs)
2932 if child_unknown is not None:
2933 err = _("Unknown child linked image: {0}").format(
2934 child_unknown)
2936 if cmd_failed is not None:
2937 (rv, cmd, errout) = cmd_failed
2938 err = _("The following subprocess returned an "
2939 "unexpected exit code of {rv:d}:\n {cmd}").format(
2940 rv=rv, cmd=cmd)
2941 if not errout:
2942 return
2943 err += _("\nAnd generated the following error "
2944 "message:\n{errout}".format(errout=errout))
2946 if cmd_output_invalid is not None:
2947 (cmd, output) = cmd_output_invalid
2948 err = _(
2949 "The following subprocess:\n"
2950 " {cmd}\n"
2951 "Generated the following unexpected output:\n"
2952 "{output}\n".format(
2953 cmd=" ".join(cmd), output="\n".join(output)))
2955 if detach_child_notsup is not None:
2956 err = _("Linked image type does not support "
2957 "child detach: {0}").format(detach_child_notsup)
2959 if detach_from_parent is not None:
2960 if exitrv == None:
2961 exitrv = pkgdefs.EXIT_PARENTOP
2962 err = _("Parent linked to child, can not detach "
2963 "child: {0}").format(detach_from_parent)
2965 if detach_parent_notsup is not None:
2966 err = _("Linked image type does not support "
2967 "parent detach: {0}").format(detach_parent_notsup)
2969 if img_linked is not None:
2970 err = _("Image already a linked child: {0}").format(
2971 img_linked)
2973 if intermediate_image is not None:
2974 ppath, cpath, ipath = intermediate_image
2975 err = _(
2976 "Intermediate image '{ipath}' found between "
2977 "child '{cpath}' and "
2978 "parent '{ppath}'").format(
2979 ppath=ppath,
2980 cpath=cpath,
2981 ipath=ipath,
2984 if lin_malformed is not None:
2985 err = _("Invalid linked image name '{0}'. "
2986 "Linked image names have the following format "
2987 "'<linked_image plugin>:<linked_image name>'").format(
2988 lin_malformed)
2990 if link_to_self is not None:
2991 err = _("Can't link image to itself: {0}")
2993 if parent_bad_img is not None:
2994 if exitrv == None:
2995 exitrv = pkgdefs.EXIT_EACCESS
2996 err = _("Can't initialize parent image at path: {0}").format(
2997 parent_bad_img)
2999 if parent_bad_notabs is not None:
3000 err = _("Parent path not absolute: {0}").format(
3001 parent_bad_notabs)
3003 if parent_bad_path is not None:
3004 if exitrv == None:
3005 exitrv = pkgdefs.EXIT_EACCESS
3006 err = _("Can't access parent image at path: {0}").format(
3007 parent_bad_path)
3009 if parent_nested is not None:
3010 ppath, cpath = parent_nested
3011 err = _("A parent image '{ppath}' can not be nested "
3012 "within a child image '{cpath}'").format(
3013 ppath=ppath,
3014 cpath=cpath,
3017 if parent_not_in_altroot is not None:
3018 path, altroot = parent_not_in_altroot
3019 err = _("Parent image '{path}' is not located "
3020 "within the child's altroot '{altroot}'").format(
3021 path=path,
3022 altroot=altroot
3025 if pkg_op_failed is not None:
3026 assert lin
3027 (op, exitrv, errout, e) = pkg_op_failed
3028 assert op is not None
3030 if e is None:
3031 err = _("""
3032 A '{op}' operation failed for child '{lin}' with an unexpected
3033 return value of {exitrv:d} and generated the following output:
3034 {errout}
3037 ).format(
3038 lin=lin,
3039 op=op,
3040 exitrv=exitrv,
3041 errout=errout,
3043 else:
3044 err = _("""
3045 A '{op}' operation failed for child '{lin}' with an unexpected
3046 exception:
3049 The child generated the following output:
3050 {errout}
3053 ).format(
3054 lin=lin,
3055 op=op,
3056 errout=errout,
3057 e=e,
3060 if self_linked is not None:
3061 err = _("Current image already a linked child: {0}").format(
3062 self_linked)
3064 if self_not_child is not None:
3065 if exitrv == None:
3066 exitrv = pkgdefs.EXIT_NOPARENT
3067 err = _("Current image is not a linked child: {0}").format(
3068 self_not_child)
3070 if unparsable_output is not None:
3071 (op, errout, e) = unparsable_output
3072 err = _("""
3073 A '{op}' operation for child '{lin}' generated non-json output.
3074 The json parser failed with the following error:
3077 The child generated the following output:
3078 {errout}
3081 ).format(
3082 lin=lin,
3083 op=op,
3084 e=e,
3085 errout=errout,
3088 # set default error return value
3089 if exitrv == None:
3090 exitrv = pkgdefs.EXIT_OOPS
3092 self.lix_err = err
3093 self.lix_bundle = None
3094 self.lix_exitrv = exitrv
3096 def __str__(self):
3097 assert self.lix_err or self.lix_bundle
3098 assert not (self.lix_err and self.lix_bundle), \
3099 "self.lix_err = {0}, self.lix_bundle = {1}".format(
3100 str(self.lix_err), str(self.lix_bundle))
3102 # single error
3103 if self.lix_err:
3104 return self.lix_err
3106 # concatenate multiple errors
3107 bundle_str = []
3108 for e in self.lix_bundle:
3109 bundle_str.append(str(e))
3110 return "\n".join(bundle_str)
3113 class FreezePkgsException(ApiException):
3114 """Used if an argument to pkg freeze isn't valid."""
3116 def __init__(self, multiversions=None, unmatched_wildcards=None,
3117 version_mismatch=None, versionless_uninstalled=None):
3118 ApiException.__init__(self)
3119 self.multiversions = multiversions
3120 self.unmatched_wildcards = unmatched_wildcards
3121 self.version_mismatch = version_mismatch
3122 self.versionless_uninstalled = versionless_uninstalled
3124 def __str__(self):
3125 res = []
3126 if self.multiversions:
3127 s = _("""\
3128 The following packages were frozen at two different versions by
3129 the patterns provided. The package stem and the versions it was frozen at are
3130 provided:""")
3131 res += [s]
3132 res += ["\t{0}\t{1}".format(stem, " ".join([
3133 str(v) for v in versions]))
3134 for stem, versions in sorted(self.multiversions)]
3136 if self.unmatched_wildcards:
3137 s = _("""\
3138 The following patterns contained wildcards but matched no
3139 installed packages.""")
3140 res += [s]
3141 res += ["\t{0}".format(pat) for pat in sorted(
3142 self.unmatched_wildcards)]
3144 if self.version_mismatch:
3145 s = _("""\
3146 The following patterns attempted to freeze the listed packages
3147 at a version different from the version at which the packages are installed.""")
3148 res += [s]
3149 for pat in sorted(self.version_mismatch):
3150 res += ["\t{0}".format(pat)]
3151 if len(self.version_mismatch[pat]) > 1:
3152 res += [
3153 "\t\t{0}".format(stem)
3154 for stem
3155 in self.version_mismatch[pat]
3158 if self.versionless_uninstalled:
3159 s = _("""\
3160 The following patterns don't match installed packages and
3161 contain no version information. Uninstalled packages can only be frozen by
3162 providing a version at which to freeze them.""")
3163 res += [s]
3164 res += ["\t{0}".format(p) for p in sorted(
3165 self.versionless_uninstalled)]
3166 return "\n".join(res)
3168 class InvalidFreezeFile(ApiException):
3169 """Used to indicate the freeze state file could not be loaded."""
3171 def __str__(self):
3172 return _("The freeze state file '{0}' is invalid.").format(
3173 self.data)
3175 class UnknownFreezeFileVersion(ApiException):
3176 """Used when the version on the freeze state file isn't the version
3177 that's expected."""
3179 def __init__(self, found_ver, expected_ver, location):
3180 self.found = found_ver
3181 self.expected = expected_ver
3182 self.loc = location
3184 def __str__(self):
3185 return _("The freeze state file '{loc}' was expected to have "
3186 "a version of {exp}, but its version was {found}").format(
3187 exp=self.expected,
3188 found=self.found,
3189 loc=self.loc,
3192 class InvalidOptionError(ApiException):
3193 """Used to indicate an issue with verifying options passed to a certain
3194 operation."""
3196 GENERIC = "generic" # generic option violation
3197 OPT_REPEAT = "opt_repeat" # option repetition is not allowed
3198 ARG_REPEAT = "arg_repeat" # argument repetition is not allowed
3199 ARG_INVALID = "arg_invalid" # argument is invalid
3200 INCOMPAT = "incompat" # option 'a' can not be specified with option 'b'
3201 REQUIRED = "required" # option 'a' requires option 'b'
3202 REQUIRED_ANY = "required_any" # option 'a' requires option 'b', 'c' or more
3203 XOR = "xor" # either option 'a' or option 'b' must be specified
3205 def __init__(self, err_type=GENERIC, options=[], msg=None,
3206 valid_args=[]):
3208 self.err_type = err_type
3209 self.options = options
3210 self.msg = msg
3211 self.valid_args = valid_args
3213 def __str__(self):
3215 # In case the user provided a custom message we just take it and
3216 # append the according options.
3217 if self.msg is not None:
3218 if self.options:
3219 self.msg += ": "
3220 self.msg += " ".join(self.options)
3221 return self.msg
3223 if self.err_type == self.OPT_REPEAT:
3224 assert len(self.options) == 1
3225 return _("Option '{option}' may not be repeated.").format(
3226 option=self.options[0])
3227 elif self.err_type == self.ARG_REPEAT:
3228 assert len(self.options) == 2
3229 return _("Argument '{op1}' for option '{op2}' may "
3230 "not be repeated.").format(op1=self.options[0],
3231 op2=self.options[1])
3232 elif self.err_type == self.ARG_INVALID:
3233 assert len(self.options) == 2
3234 s = _("Argument '{op1}' for option '{op2}' is "
3235 "invalid.").format(op1=self.options[0],
3236 op2=self.options[1])
3237 if self.valid_args:
3238 s += _("\nSupported: {0}").format(", ".join(
3239 self.valid_args))
3240 return s
3241 elif self.err_type == self.INCOMPAT:
3242 assert len(self.options) == 2
3243 return _("The '{op1}' and '{op2}' option may "
3244 "not be combined.").format(op1=self.options[0],
3245 op2=self.options[1])
3246 elif self.err_type == self.REQUIRED:
3247 assert len(self.options) == 2
3248 return _("'{op1}' may only be used with "
3249 "'{op2}'.").format(op1=self.options[0],
3250 op2=self.options[1])
3251 elif self.err_type == self.REQUIRED_ANY:
3252 assert len(self.options) > 2
3253 return _("'{op1}' may only be used with "
3254 "'{op2}' or {op3}.").format(op1=self.options[0],
3255 op2=", ".join(self.options[1:-1]),
3256 op3=self.options[-1])
3257 elif self.err_type == self.XOR:
3258 assert len(self.options) == 2
3259 return _("Either '{op1}' or '{op2}' must be "
3260 "specified").format(op1=self.options[0],
3261 op2=self.options[1])
3262 else:
3263 return _("invalid option(s): ") + " ".join(self.options)
3265 class InvalidOptionErrors(ApiException):
3267 def __init__(self, errors):
3269 self.errors = []
3271 assert (isinstance(errors, list) or isinstance(errors, tuple) or
3272 isinstance(errors, set) or
3273 isinstance(errors, InvalidOptionError))
3275 if isinstance(errors, InvalidOptionError):
3276 self.errors.append(errors)
3277 else:
3278 self.errors = errors
3280 def __str__(self):
3281 msgs = []
3282 for e in self.errors:
3283 msgs.append(str(e))
3284 return "\n".join(msgs)
3286 class UnexpectedLinkError(ApiException):
3287 """Used to indicate that an image state file has been replaced
3288 with a symlink."""
3290 def __init__(self, path, filename, errno):
3291 self.path = path
3292 self.filename = filename
3293 self.errno = errno
3295 def __str__(self):
3296 return _("Cannot update file: '{file}' at path "
3297 "'{path}', contains a symlink. "
3298 "[Error '{errno:d}': '{error}']").format(
3299 error=os.strerror(self.errno),
3300 errno=self.errno,
3301 path=self.path,
3302 file=self.filename,
3306 class InvalidConfigFile(ApiException):
3307 """Used to indicate that a configuration file is invalid
3308 or broken"""
3310 def __init__(self, path):
3311 self.path = path
3313 def __str__(self):
3314 return _("Cannot parse configuration file "
3315 "{path}'.").format(path=self.path)