api_errors.py revision 2126
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#!/usr/bin/python
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# CDDL HEADER START
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# The contents of this file are subject to the terms of the
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# Common Development and Distribution License (the "License").
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# You may not use this file except in compliance with the License.
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen# or http://www.opensolaris.org/os/licensing.
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# See the License for the specific language governing permissions
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# and limitations under the License.
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# When distributing Covered Code, include this CDDL HEADER in each
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# If applicable, add the following below this CDDL HEADER, with the
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# fields enclosed by brackets "[]" replaced with your own identifying
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# information: Portions Copyright [yyyy] [name of copyright owner]
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# CDDL HEADER END
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenimport errno
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenimport os
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenimport urlparse
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# EmptyI for argument defaults; can't import from misc due to circular
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen# dependency.
94a78eb438622fa53abef1e1726714dacad4b61cTimo SirainenEmptyI = tuple()
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
6c07b8ddc5e894feead4d422075b079451721241Timo Sirainenclass ApiException(Exception):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __init__(self, *args):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen Exception.__init__(self)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.__verbose_info = []
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __unicode__(self):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen # To workaround python issues 6108 and 2517, this provides a
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen # a standard wrapper for this class' exceptions so that they
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen # have a chance of being stringified correctly.
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return unicode(str(self))
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
6c07b8ddc5e894feead4d422075b079451721241Timo Sirainen def add_verbose_info(self, info):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.__verbose_info.extend(info)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen @property
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen def verbose_info(self):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return self.__verbose_info
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainenclass ImageLockedError(ApiException):
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen """Used to indicate that the image is currently locked by another thread
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen or process and cannot be modified."""
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __init__(self, hostname=None, pid=None, pid_name=None):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen ApiException.__init__(self)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.hostname = hostname
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.pid = pid
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.pid_name = pid_name
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen def __str__(self):
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen if self.pid is not None and self.pid_name is not None and \
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.hostname is not None:
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen return _("The image cannot be modified as it is "
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen "currently in use by another package client: "
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen "%(pid_name)s on %(host)s, pid %(pid)s.") % {
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen "pid_name": self.pid_name, "pid": self.pid,
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen "host": self.hostname }
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen if self.pid is not None and self.pid_name is not None:
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return _("The image cannot be modified as it is "
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen "currently in use by another package client: "
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen "%(pid_name)s on an unknown host, pid %(pid)s.") % {
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen "pid_name": self.pid_name, "pid": self.pid }
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen elif self.pid is not None:
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return _("The image cannot be modified as it is "
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen "currently in use by another package client: "
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen "pid %(pid)s on %(host)s.") % {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen "pid": self.pid, "host": self.hostname }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return _("The image cannot be modified as it is currently "
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen "in use by another package client.")
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenclass ImageNotFoundException(ApiException):
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen """Used when an image was not found"""
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __init__(self, user_specified, user_dir, root_dir):
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen ApiException.__init__(self)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.user_specified = user_specified
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen self.user_dir = user_dir
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.root_dir = root_dir
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainenclass VersionException(ApiException):
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen def __init__(self, expected_version, received_version):
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen ApiException.__init__(self)
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen self.expected_version = expected_version
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.received_version = received_version
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainenclass PlanExistsException(ApiException):
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen def __init__(self, plan_type):
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen ApiException.__init__(self)
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen self.plan_type = plan_type
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainenclass PlanPrepareException(ApiException):
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen """Base exception class for plan preparation errors."""
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen pass
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenclass InvalidPackageErrors(ApiException):
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen """Used to indicate that the requested operation could not be completed
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen as one or more packages contained invalid metadata."""
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __init__(self, errors):
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen """'errors' should be a list of exceptions or strings
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen indicating what packages had errors and why."""
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen ApiException.__init__(self)
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen self.errors = errors
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen def __str__(self):
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen return _("The requested operation cannot be completed due "
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen "to invalid package metadata. Details follow:\n\n"
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen "%s") % "\n".join(str(e) for e in self.errors)
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainenclass LicenseAcceptanceError(ApiException):
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen """Used to indicate that license-related errors occurred during
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen plan evaluation or execution."""
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __init__(self, pfmri, src=None, dest=None, accepted=None,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen displayed=None):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen ApiException.__init__(self)
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen self.fmri = pfmri
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.src = src
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen self.dest = dest
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.accepted = accepted
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen self.displayed = displayed
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenclass PkgLicenseErrors(PlanPrepareException):
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen """Used to indicate that plan evaluation or execution failed due
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen to license-related errors for a package."""
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen def __init__(self, errors):
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen """'errors' should be a list of LicenseAcceptanceError
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen exceptions."""
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen PlanPrepareException.__init__(self)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen self.__errors = errors
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen @property
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def errors(self):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen """A list of LicenseAcceptanceError exceptions."""
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen return self.__errors
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenclass PlanLicenseErrors(PlanPrepareException):
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen """Used to indicate that image plan evaluation or execution failed due
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen to license-related errors."""
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen def __init__(self, pp_errors):
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen """'errors' should be a list of PkgLicenseErrors exceptions."""
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen PlanPrepareException.__init__(self)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen self.__errors = pkgs = {}
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen for pp_err in pp_errors:
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen for e in pp_err.errors:
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen pkgs.setdefault(str(e.fmri), []).append(e)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen @property
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainen def errors(self):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen """Returns a dictionary indexed by package FMRI string of
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen lists of LicenseAcceptanceError exceptions."""
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return self.__errors
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen def __str__(self):
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen """Returns a string representation of the license errors."""
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen output = ""
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen for sfmri in self.__errors:
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen output += ("-" * 40) + "\n"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen output += _("Package: %s\n\n") % sfmri
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen for e in self.__errors[sfmri]:
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen lic_name = e.dest.attrs["license"]
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen output += _("License: %s\n") % lic_name
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if e.dest.must_accept and not e.accepted:
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen output += _(" License requires "
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen "acceptance.")
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen if e.dest.must_display and not e.displayed:
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen output += _(" License must be viewed.")
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen output += "\n"
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return output
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainenclass ActuatorException(ApiException):
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen def __init__(self, e):
ApiException.__init__(self)
self.exception = e
def __str__(self):
return str(self.exception)
class PrematureExecutionException(ApiException):
pass
class AlreadyPreparedException(PlanPrepareException):
pass
class AlreadyExecutedException(ApiException):
pass
class ImageplanStateException(ApiException):
def __init__(self, state):
ApiException.__init__(self)
self.state = state
class InvalidPlanError(ApiException):
"""Used to indicate that the image plan is no longer valid, likely as a
result of an image state change since the plan was created."""
def __str__(self):
return _("The plan for the current operation is no longer "
"valid. The image has likely been modified by another "
"process or client. Please try the operation again.")
class ImagePkgStateError(ApiException):
def __init__(self, fmri, states):
ApiException.__init__(self)
self.fmri = fmri
self.states = states
def __str__(self):
return _("Invalid package state change attempted '%(states)s' "
"for package '%(fmri)s'.") % { "states": self.states,
"fmri": self.fmri }
class IpkgOutOfDateException(ApiException):
pass
class ImageUpdateOnLiveImageException(ApiException):
pass
class RebootNeededOnLiveImageException(ApiException):
pass
class CanceledException(ApiException):
pass
class PlanMissingException(ApiException):
pass
class NoPackagesInstalledException(ApiException):
pass
class PermissionsException(ApiException):
def __init__(self, path):
ApiException.__init__(self)
self.path = path
def __str__(self):
if self.path:
return _("Could not operate on %s\nbecause of "
"insufficient permissions. Please try the "
"command again as a privileged user.") % \
self.path
else:
return _("""
Could not complete the operation because of insufficient permissions.
Please try the command again as a privileged user.
""")
class FileInUseException(PermissionsException):
def __init__(self, path):
PermissionsException.__init__(self, path)
assert path
def __str__(self):
return _("Could not operate on %s\nbecause the file is "
"in use. Please stop using the file and try the\n"
"operation again.") % self.path
class ReadOnlyFileSystemException(PermissionsException):
"""Used to indicate that the operation was attempted on a
read-only filesystem"""
def __init__(self, path):
ApiException.__init__(self)
self.path = path
def __str__(self):
if self.path:
return _("Could not complete the operation on %s: "
"read-only filesystem.") % self.path
return _("Could not complete the operation: read-only "
"filesystem.")
class PlanCreationException(ApiException):
def __init__(self, unmatched_fmris=EmptyI, multiple_matches=EmptyI,
missing_matches=EmptyI, illegal=EmptyI,
badarch=EmptyI, installed=EmptyI, multispec=EmptyI,
no_solution=False, no_version=EmptyI, missing_dependency=EmptyI,
wrong_publishers=EmptyI, obsolete=EmptyI):
ApiException.__init__(self)
self.unmatched_fmris = unmatched_fmris
self.multiple_matches = multiple_matches
self.missing_matches = missing_matches
self.illegal = illegal
self.badarch = badarch
self.installed = installed
self.multispec = multispec
self.obsolete = obsolete
self.no_solution = no_solution
self.no_version = no_version
self.missing_dependency = missing_dependency
self.wrong_publishers = wrong_publishers
def __str__(self):
res = []
if self.unmatched_fmris:
s = _("""\
The following pattern(s) did not match any packages in the current catalog.
Try relaxing the pattern, refreshing and/or examining the catalogs:""")
res += [s]
res += ["\t%s" % p for p in self.unmatched_fmris]
if self.wrong_publishers:
s = _("The following patterns only matched packages "
"that are from publishers other than that which "
"supplied the already installed version of this package")
res += [s]
res += ["\t%s: %s" % (p[0], ", ".join(p[1])) for p in self.wrong_publishers]
if self.multiple_matches:
s = _("'%s' matches multiple packages")
for p, lst in self.multiple_matches:
res.append(s % p)
for pfmri in lst:
res.append("\t%s" % pfmri)
if self.missing_matches:
s = _("'%s' matches no installed packages")
res += [ s % p for p in self.missing_matches ]
if self.illegal:
s = _("'%s' is an illegal fmri")
res += [ s % p for p in self.illegal ]
if self.badarch:
s = _("'%s' supports the following architectures: %s")
a = _("Image architecture is defined as: %s")
res += [ s % (self.badarch[0],
", ".join(self.badarch[1]))]
res += [ a % (self.badarch[2])]
s = _("'%(p)s' depends on obsolete package '%(op)s'")
res += [ s % {"p": p, "op": op} for p, op in self.obsolete ]
if self.installed:
s = _("The proposed operation can not be performed for "
"the following package(s) as they are already "
"installed: ")
res += [s]
res += ["\t%s" % p for p in self.installed]
if self.multispec:
s = _("The following different patterns specify the"
"same package(s):")
res += [s]
for t in self.multispec:
res += [
", ".join(
[t[i] for i in range(1, len(t))])
+ ": %s" % t[0]
]
if self.no_solution:
res += [_("No solution was found to satisfy constraints")]
if self.no_version:
res += self.no_version
if self.missing_dependency:
res += [_("Package %(pkg)s is missing a dependency: "
"%(dep)s") %
{"pkg": self.missing_dependency[0],
"dep": self.missing_dependency[1]}]
return "\n".join(res)
class ActionExecutionError(ApiException):
"""Used to indicate that action execution (such as install, remove,
etc.) failed even though the action is valid.
In particular, this exception indicates that something went wrong in the
application (or unapplication) of the action to the system, and is most
likely not an error in the pkg(5) code."""
def __init__(self, action, details=None, error=None, fmri=None,
use_errno=None):
"""'action' is the object for the action that failed during the
requested operation.
'details' is an optional message explaining what operation
failed, why it failed, and why it cannot continue. It should
also include a suggestion as to how to resolve the situation
if possible.
'error' is an optional exception object that may have been
raised when the operation failed.
'fmri' is an optional package FMRI indicating what package
was being operated on at the time the error occurred.
'use_errno' is an optional boolean value indicating whether
the strerror() text of the exception should be used. If
'details' is provided, the default value is False, otherwise
True."""
assert (details or error)
self.action = action
self.details = details
self.error = error
self.fmri = fmri
if use_errno == None:
# If details were provided, don't use errno unless
# explicitly requested.
use_errno = not details
self.use_errno = use_errno
def __str__(self):
errno = ""
if self.use_errno and self.error and \
hasattr(self.error, "errno"):
errno = "[errno %d: %s]" % (self.error.errno,
os.strerror(self.error.errno))
details = self.details or ""
# Fall back on the wrapped exception if we don't have anything
# useful.
if not errno and not details:
return str(self.error)
if errno and details:
details = "%s: %s" % (errno, details)
if details and not self.fmri:
details = _("Requested operation failed for action "
"%(action)s:\n%(details)s") % {
"action": self.action,
"details": details }
elif details:
details = _("Requested operation failed for package "
"%(fmri)s:\n%(details)s") % { "fmri": self.fmri,
"details": details }
# If we only have one of the two, no need for the colon.
return "%s%s" % (errno, details)
class CatalogRefreshException(ApiException):
def __init__(self, failed, total, succeeded, errmessage=None):
ApiException.__init__(self)
self.failed = failed
self.total = total
self.succeeded = succeeded
self.errmessage = errmessage
class CatalogError(ApiException):
"""Base exception class for all catalog exceptions."""
def __init__(self, *args, **kwargs):
ApiException.__init__(self)
if args:
self.data = args[0]
else:
self.data = None
self._args = kwargs
def __str__(self):
return str(self.data)
class AnarchicalCatalogFMRI(CatalogError):
"""Used to indicate that the specified FMRI is not valid for catalog
operations because it is missing publisher information."""
def __str__(self):
return _("The FMRI '%s' does not contain publisher information "
"and cannot be used for catalog operations.") % self.data
class BadCatalogMetaRoot(CatalogError):
"""Used to indicate an operation on the catalog's meta_root failed
because the meta_root is invalid."""
def __str__(self):
return _("Catalog meta_root '%(root)s' is invalid; unable "
"to complete operation: '%(op)s'.") % { "root": self.data,
"op": self._args.get("operation", None) }
class BadCatalogPermissions(CatalogError):
"""Used to indicate the server catalog files do not have the expected
permissions."""
def __init__(self, files):
"""files should contain a list object with each entry consisting
of a tuple of filename, expected_mode, received_mode."""
if not files:
files = []
CatalogError.__init__(self, files)
def __str__(self):
msg = _("The following catalog files have incorrect "
"permissions:\n")
for f in self._args:
fname, emode, fmode = f
msg += _("\t%(fname)s: expected mode: %(emode)s, found "
"mode: %(fmode)s\n") % { "fname": fname,
"emode": emode, "fmode": fmode }
return msg
class BadCatalogSignatures(CatalogError):
"""Used to indicate that the Catalog signatures are not valid."""
def __str__(self):
return _("The signature data for the '%s' catalog file is not "
"valid.") % self.data
class BadCatalogUpdateIdentity(CatalogError):
"""Used to indicate that the requested catalog updates could not be
applied as the new catalog data is significantly different such that
the old catalog cannot be updated to match it."""
def __str__(self):
return _("Unable to determine the updates needed for "
"the current catalog using the provided catalog "
"update data in '%s'.") % self.data
class DuplicateCatalogEntry(CatalogError):
"""Used to indicate that the specified catalog operation could not be
performed since it would result in a duplicate catalog entry."""
def __str__(self):
return _("Unable to perform '%(op)s' operation for catalog "
"%(name)s; completion would result in a duplicate entry "
"for package '%(fmri)s'.") % { "op": self._args.get(
"operation", None), "name": self._args.get("catalog_name",
None), "fmri": self.data }
class CatalogUpdateRequirements(CatalogError):
"""Used to indicate that an update request for the catalog could not
be performed because update requirements were not satisfied."""
def __str__(self):
return _("Catalog updates can only be applied to an on-disk "
"catalog.")
class InvalidCatalogFile(CatalogError):
"""Used to indicate a Catalog file could not be loaded."""
def __str__(self):
return _("Catalog file '%s' is invalid.") % self.data
class MismatchedCatalog(CatalogError):
"""Used to indicate that a Catalog's attributes and parts do not
match. This is likely the result of an attributes file being
retrieved which doesn't match the parts that were retrieved such
as in a misconfigured or stale cache case."""
def __str__(self):
return _("The content of the catalog for publisher '%s' "
"doesn't match the catalog's attributes. This is "
"likely the result of a mix of older and newer "
"catalog files being provided for the publisher.") % \
self.data
class ObsoleteCatalogUpdate(CatalogError):
"""Used to indicate that the specified catalog updates are for an older
version of the catalog and cannot be applied."""
def __str__(self):
return _("Unable to determine the updates needed for the "
"catalog using the provided catalog update data in '%s'. "
"The specified catalog updates are for an older version "
"of the catalog and cannot be used.") % self.data
class UnknownCatalogEntry(CatalogError):
"""Used to indicate that an entry for the specified package FMRI or
pattern could not be found in the catalog."""
def __str__(self):
return _("'%s' could not be found in the catalog.") % self.data
class UnknownUpdateType(CatalogError):
"""Used to indicate that the specified CatalogUpdate operation is
unknown."""
def __str__(self):
return _("Unknown catalog update type '%s'") % self.data
class UnrecognizedCatalogPart(CatalogError):
"""Raised when the catalog finds a CatalogPart that is unrecognized
or invalid."""
def __str__(self):
return _("Unrecognized, unknown, or invalid CatalogPart '%s'") \
% self.data
class InventoryException(ApiException):
"""Used to indicate that some of the specified patterns to a catalog
matching function did not match any catalog entries, or were invalid
patterns."""
def __init__(self, illegal=EmptyI, matcher=EmptyI, notfound=EmptyI,
publisher=EmptyI, version=EmptyI):
ApiException.__init__(self)
self.illegal = illegal
self.matcher = matcher
self.notfound = set(notfound)
self.publisher = publisher
self.version = version
self.notfound.update(matcher)
self.notfound.update(publisher)
self.notfound.update(version)
self.notfound = list(self.notfound)
assert self.illegal or self.notfound
def __str__(self):
outstr = ""
for x in self.illegal:
# Illegal FMRIs have their own __str__ method
outstr += "%s\n" % x
if self.matcher or self.publisher or self.version:
outstr += _("No matching package could be found for "
"the following FMRIs in any of the catalogs for "
"the current publishers:\n")
for x in self.matcher:
outstr += _("%s (pattern did not match)\n") % x
for x in self.publisher:
outstr += _("%s (publisher did not "
"match)\n") % x
for x in self.version:
outstr += _("%s (version did not match)\n") % x
return outstr
# SearchExceptions
class SearchException(ApiException):
"""Based class used for all search-related api exceptions."""
pass
class MalformedSearchRequest(SearchException):
"""Raised when the server cannot understand the format of the
search request."""
def __init__(self, url):
SearchException.__init__(self)
self.url = url
def __str__(self):
return str(self.url)
class NegativeSearchResult(SearchException):
"""Returned when the search cannot find any matches."""
def __init__(self, url):
SearchException.__init__(self)
self.url = url
def __str__(self):
return _("The search at url %s returned no results.") % self.url
class ProblematicSearchServers(SearchException):
"""This class wraps exceptions which could appear while trying to
do a search request."""
def __init__(self, failed=EmptyI, invalid=EmptyI, unsupported=EmptyI):
SearchException.__init__(self)
self.failed_servers = failed
self.invalid_servers = invalid
self.unsupported_servers = unsupported
def __str__(self):
s = _("Some repositories failed to respond appropriately:\n")
for pub, err in self.failed_servers:
s += _("%(o)s:\n%(msg)s\n") % \
{ "o": pub, "msg": err}
for pub in self.invalid_servers:
s += _("%s did not return a valid response.\n" \
% pub)
if len(self.unsupported_servers) > 0:
s += _("Some repositories don't support requested "
"search operation:\n")
for pub, err in self.unsupported_servers:
s += _("%(o)s:\n%(msg)s\n") % \
{ "o": pub, "msg": err}
return s
class SlowSearchUsed(SearchException):
"""This exception is thrown when a local search is performed without
an index. It's raised after all results have been yielded."""
def __str__(self):
return _("Search performance is degraded.\n"
"Run 'pkg rebuild-index' to improve search speed.")
class UnsupportedSearchError(SearchException):
"""Returned when a search protocol is not supported by the
remote server."""
def __init__(self, url=None, proto=None):
SearchException.__init__(self)
self.url = url
self.proto = proto
def __str__(self):
s = _("Search repository does not support the requested "
"protocol:")
if self.url:
s += "\nRepository URL: %s" % self.url
if self.proto:
s += "\nRequested operation: %s" % self.proto
return s
def __cmp__(self, other):
if not isinstance(other, UnsupportedSearchError):
return -1
r = cmp(self.url, other.url)
if r != 0:
return r
return cmp(self.proto, other.proto)
# IndexingExceptions.
class IndexingException(SearchException):
""" The base class for all exceptions that can occur while indexing. """
def __init__(self, private_exception):
SearchException.__init__(self)
self.cause = private_exception.cause
class CorruptedIndexException(IndexingException):
"""This is used when the index is not in a correct state."""
pass
class InconsistentIndexException(IndexingException):
"""This is used when the existing index is found to have inconsistent
versions."""
def __init__(self, e):
IndexingException.__init__(self, e)
self.exception = e
def __str__(self):
return str(self.exception)
class IndexLockedException(IndexingException):
"""This is used when an attempt to modify an index locked by another
process or thread is made."""
def __init__(self, e):
IndexingException.__init__(self, e)
self.exception = e
def __str__(self):
return str(self.exception)
class ProblematicPermissionsIndexException(IndexingException):
""" This is used when the indexer is unable to create, move, or remove
files or directories it should be able to. """
def __str__(self):
return "Could not remove or create " \
"%s because of incorrect " \
"permissions. Please correct this issue then " \
"rebuild the index." % self.cause
class WrapIndexingException(ApiException):
"""This exception is used to wrap an indexing exception during install,
uninstall, or update so that a more appropriate error message can be
displayed to the user."""
def __init__(self, e, tb, stack):
ApiException.__init__(self)
self.wrapped = e
self.tb = tb
self.stack = stack
def __str__(self):
tmp = self.tb.split("\n")
res = tmp[:1] + [s.rstrip("\n") for s in self.stack] + tmp[1:]
return "\n".join(res)
class WrapSuccessfulIndexingException(WrapIndexingException):
"""This exception is used to wrap an indexing exception during install,
uninstall, or update which was recovered from by performing a full
reindex."""
pass
# Query Parsing Exceptions
class BooleanQueryException(ApiException):
"""This exception is used when the children of a boolean operation
have different return types. The command 'pkg search foo AND <bar>'
is the simplest example of this."""
def __init__(self, e):
ApiException.__init__(self)
self.e = e
def __str__(self):
return str(self.e)
class ParseError(ApiException):
def __init__(self, e):
ApiException.__init__(self)
self.e = e
def __str__(self):
return str(self.e)
class NonLeafPackageException(ApiException):
"""Removal of a package which satisfies dependencies has been attempted.
The first argument to the constructor is the FMRI which we tried to
remove, and is available as the "fmri" member of the exception. The
second argument is the list of dependent packages that prevent the
removal of the package, and is available as the "dependents" member.
"""
def __init__(self, *args):
ApiException.__init__(self, *args)
self.fmri = args[0]
self.dependents = args[1]
class InvalidDepotResponseException(ApiException):
"""Raised when the depot doesn't have versions of operations
that the client needs to operate successfully."""
def __init__(self, url, data):
ApiException.__init__(self)
self.url = url
self.data = data
def __str__(self):
s = "Unable to contact valid package repository"
if self.url:
s += ": %s" % self.url
if self.data:
s += "\nEncountered the following error(s):\n%s" % \
self.data
return s
class DataError(ApiException):
"""Base exception class used for all data related errors."""
def __init__(self, *args, **kwargs):
ApiException.__init__(self, *args)
if args:
self.data = args[0]
else:
self.data = None
self._args = kwargs
class InvalidP5IFile(DataError):
"""Used to indicate that the specified location does not contain a
valid p5i-formatted file."""
def __str__(self):
if self.data:
return _("The provided p5i data is in an unrecognized "
"format or does not contain valid publisher "
"information: %s") % self.data
return _("The provided p5i data is in an unrecognized format "
"or does not contain valid publisher information.")
class UnsupportedP5IFile(DataError):
"""Used to indicate that an attempt to read an unsupported version
of pkg(5) info file was attempted."""
def __str__(self):
return _("Unsupported pkg(5) publisher information data "
"format.")
class TransportError(ApiException):
"""Abstract exception class for all transport exceptions.
Specific transport exceptions should be implemented in the
transport code. Callers wishing to catch transport exceptions
should use this class. Subclasses must implement all methods
defined here that raise NotImplementedError."""
def __str__(self):
raise NotImplementedError()
class RetrievalError(ApiException):
"""Used to indicate that a a requested resource could not be
retrieved."""
def __init__(self, data, location=None):
ApiException.__init__(self)
self.data = data
self.location = location
def __str__(self):
if self.location:
return _("Error encountered while retrieving data from "
"'%s':\n%s") % (self.location, self.data)
return _("Error encountered while retrieving data from: %s") % \
self.data
class InvalidResourceLocation(ApiException):
"""Used to indicate that an invalid transport location was provided."""
def __init__(self, data):
ApiException.__init__(self)
self.data = data
def __str__(self):
return _("'%s' is not a valid location.") % self.data
class BEException(ApiException):
def __init__(self):
ApiException.__init__(self)
class InvalidBENameException(BEException):
def __init__(self, be_name):
BEException.__init__(self)
self.be_name = be_name
def __str__(self):
return _("'%s' is not a valid boot environment name.") % \
self.be_name
class DuplicateBEName(BEException):
"""Used to indicate that there is an existing boot environment
with this name"""
def __init__(self, be_name):
BEException.__init__(self)
self.be_name = be_name
def __str__(self):
return _("The boot environment '%s' already exists.") % \
self.be_name
class BENamingNotSupported(BEException):
def __init__(self, be_name):
BEException.__init__(self)
self.be_name = be_name
def __str__(self):
return _("""\
Boot environment naming during package install is not supported on this
version of OpenSolaris. Please update without the --be-name option.""")
class UnableToCopyBE(BEException):
def __str__(self):
return _("Unable to clone the current boot environment.")
class UnableToRenameBE(BEException):
def __init__(self, orig, dest):
BEException.__init__(self)
self.original_name = orig
self.destination_name = dest
def __str__(self):
d = {
"orig": self.original_name,
"dest": self.destination_name
}
return _("""\
A problem occurred while attempting to rename the boot environment
currently named %(orig)s to %(dest)s.""") % d
class UnableToMountBE(BEException):
def __init__(self, be_name, be_dir):
BEException.__init__(self)
self.name = be_name
self.mountpoint = be_dir
def __str__(self):
return _("Unable to mount %(name)s at %(mt)s") % \
{"name": self.name, "mt": self.mountpoint}
class BENameGivenOnDeadBE(BEException):
def __init__(self, be_name):
BEException.__init__(self)
self.name = be_name
def __str__(self):
return _("""\
Naming a boot environment when operating on a non-live image is
not allowed.""")
class UnrecognizedOptionsToInfo(ApiException):
def __init__(self, opts):
ApiException.__init__(self)
self._opts = opts
def __str__(self):
s = _("Info does not recognize the following options:")
for o in self._opts:
s += _(" '") + str(o) + _("'")
return s
class IncorrectIndexFileHash(ApiException):
"""This is used when the index hash value doesn't match the hash of the
packages installed in the image."""
pass
class PublisherError(ApiException):
"""Base exception class for all publisher exceptions."""
def __init__(self, *args, **kwargs):
ApiException.__init__(self, *args)
if args:
self.data = args[0]
else:
self.data = None
self._args = kwargs
def __str__(self):
return str(self.data)
class BadPublisherMetaRoot(PublisherError):
"""Used to indicate an operation on the publisher's meta_root failed
because the meta_root is invalid."""
def __str__(self):
return _("Publisher meta_root '%(root)s' is invalid; unable "
"to complete operation: '%(op)s'.") % { "root": self.data,
"op": self._args.get("operation", None) }
class BadPublisherAlias(PublisherError):
"""Used to indicate that a publisher alias is not valid."""
def __str__(self):
return _("'%s' is not a valid publisher alias.") % self.data
class BadPublisherPrefix(PublisherError):
"""Used to indicate that a publisher name is not valid."""
def __str__(self):
return _("'%s' is not a valid publisher name.") % self.data
class BadRepositoryAttributeValue(PublisherError):
"""Used to indicate that the specified repository attribute value is
invalid."""
def __str__(self):
return _("'%(value)s' is not a valid value for repository "
"attribute '%(attribute)s'.") % {
"value": self._args["value"], "attribute": self.data }
class BadRepositoryCollectionType(PublisherError):
"""Used to indicate that the specified repository collection type is
invalid."""
def __init__(self, *args, **kwargs):
PublisherError.__init__(self, *args, **kwargs)
def __str__(self):
return _("'%s' is not a valid repository collection type.") % \
self.data
class BadRepositoryURI(PublisherError):
"""Used to indicate that a repository URI is not syntactically valid."""
def __str__(self):
return _("'%s' is not a valid URI.") % self.data
class BadRepositoryURIPriority(PublisherError):
"""Used to indicate that the priority specified for a repository URI is
not valid."""
def __str__(self):
return _("'%s' is not a valid URI priority; integer value "
"expected.") % self.data
class BadRepositoryURISortPolicy(PublisherError):
"""Used to indicate that the specified repository URI sort policy is
invalid."""
def __init__(self, *args, **kwargs):
PublisherError.__init__(self, *args, **kwargs)
def __str__(self):
return _("'%s' is not a valid repository URI sort policy.") % \
self.data
class DisabledPublisher(PublisherError):
"""Used to indicate that an attempt to use a disabled publisher occurred
during an operation."""
def __str__(self):
return _("Publisher '%s' is disabled and cannot be used for "
"packaging operations.") % self.data
class DuplicatePublisher(PublisherError):
"""Used to indicate that a publisher with the same name or alias already
exists for an image."""
def __str__(self):
return _("A publisher with the same name or alias as '%s' "
"already exists.") % self.data
class DuplicateRepository(PublisherError):
"""Used to indicate that a repository with the same origin uris
already exists for a publisher."""
def __str__(self):
return _("A repository with the same name or origin URIs "
"already exists for publisher '%s'.") % self.data
class DuplicateRepositoryMirror(PublisherError):
"""Used to indicate that a repository URI is already in use by another
repository mirror."""
def __str__(self):
return _("Mirror '%s' already exists for the specified "
"publisher.") % self.data
class DuplicateRepositoryOrigin(PublisherError):
"""Used to indicate that a repository URI is already in use by another
repository origin."""
def __str__(self):
return _("Origin '%s' already exists for the specified "
"publisher.") % self.data
class PublisherOriginRequired(PublisherError):
"""Used to indicate that the specified publisher must have at least one
repository with at least one origin URI."""
def __str__(self):
return _("At least one origin is required for %s. You must "
"add a new origin before attempting to remove the specified "
"origin(s).") % self.data
class RemovePreferredPublisher(PublisherError):
"""Used to indicate an attempt to remove the preferred publisher was
made."""
def __str__(self):
return _("The preferred publisher cannot be removed.")
class MoveRelativeToSelf(PublisherError):
"""Used to indicate an attempt to search a repo before or after itself"""
def __str__(self):
return _("Cannot search a repository before or after itself")
class SelectedRepositoryRemoval(PublisherError):
"""Used to indicate that an attempt to remove the selected repository
for a publisher was made."""
def __str__(self):
return _("Cannot remove the selected repository for a "
"publisher.")
class SetDisabledPublisherPreferred(PublisherError):
"""Used to indicate an attempt to set a disabled publisher as the
preferred publisher was made."""
def __str__(self):
return _("Publisher '%s' is disabled and cannot be set as the "
"preferred publisher.") % self.data
class SetPreferredPublisherDisabled(PublisherError):
"""Used to indicate that an attempt was made to set the preferred
publisher as disabled."""
def __str__(self):
return _("The preferred publisher may not be disabled."
" Another publisher must be set as the preferred "
"publisher before this publisher can be disabled.")
class UnknownLegalURI(PublisherError):
"""Used to indicate that no matching legal URI could be found using the
provided criteria."""
def __str__(self):
return _("Unknown legal URI '%s'.") % self.data
class UnknownPublisher(PublisherError):
"""Used to indicate that no matching publisher could be found using the
provided criteria."""
def __str__(self):
return _("Unknown publisher '%s'.") % self.data
class UnknownRepositoryPublishers(PublisherError):
"""Used to indicate that one or more publisher prefixes are unknown by
the specified repository."""
def __init__(self, known=EmptyI, unknown=EmptyI, location=None,
origins=EmptyI):
ApiException.__init__(self)
self.known = known
self.location = location
self.origins = origins
self.unknown = unknown
def __str__(self):
if self.location:
return _("The repository at %(location)s does not "
"contain package data for %(unknown)s; only "
"%(known)s.\n\nThis is either because the "
"repository location is not valid, or because the "
"provided publisher does not match those known by "
"the repository.") % {
"unknown": ", ".join(self.unknown),
"location": self.location,
"known": ", ".join(self.known) }
if self.origins:
return _("One or more of the repository origin(s) "
"listed below contains package data for "
"%(known)s; not %(unknown)s:\n\n%(origins)s\n\n"
"This is either because one of the repository "
"origins is not valid for this publisher, or "
"because the list of known publishers retrieved "
"from the repository origin does not match the "
"client.") % { "unknown": ", ".join(self.unknown),
"known": ", ".join(self.known),
"origins": "\n".join(str(o) for o in self.origins) }
return _("The specified publisher repository does not "
"contain any package data for %(unknown)s; only "
"%(known)s.") % { "unknown": ", ".join(self.unknown),
"known": ", ".join(self.known) }
class UnknownRelatedURI(PublisherError):
"""Used to indicate that no matching related URI could be found using
the provided criteria."""
def __str__(self):
return _("Unknown related URI '%s'.") % self.data
class UnknownRepository(PublisherError):
"""Used to indicate that no matching repository could be found using the
provided criteria."""
def __str__(self):
return _("Unknown repository '%s'.") % self.data
class UnknownRepositoryMirror(PublisherError):
"""Used to indicate that a repository URI could not be found in the
list of repository mirrors."""
def __str__(self):
return _("Unknown repository mirror '%s'.") % self.data
class UnsupportedRepositoryOperation(PublisherError):
"""The publisher has no active repositories that support the
requested operation."""
def __init__(self, pub, operation):
ApiException.__init__(self)
self.data = None
self.kwargs = None
self.pub = pub
self.op = operation
def __str__(self):
return _("Publisher '%s' has no repositories that support the"
" '%s' operation.") % (self.pub, self.op)
class RepoPubConfigUnavailable(PublisherError):
"""Used to indicate that the specified repository does not provide
publisher configuration information."""
def __init__(self, location=None, pub=None):
ApiException.__init__(self)
self.location = location
self.pub = pub
def __str__(self):
if not self.location and not self.pub:
return _("The specified package repository does not "
"provide publisher configuration information.")
if self.location:
return _("The package repository at %s does not "
"provide publisher configuration information or "
"the information provided is incomplete.") % \
self.location
return _("One of the package repository origins for %s does "
"not provide publisher configuration information or the "
"information provided is incomplete.") % self.pub
class UnknownRepositoryOrigin(PublisherError):
"""Used to indicate that a repository URI could not be found in the
list of repository origins."""
def __str__(self):
return _("Unknown repository origin '%s'") % self.data
class UnsupportedRepositoryURI(PublisherError):
"""Used to indicate that the specified repository URI uses an
unsupported scheme."""
def __str__(self):
if self.data:
scheme = urlparse.urlsplit(self.data,
allow_fragments=0)[0]
return _("The URI '%(uri)s' contains an unsupported "
"scheme '%(scheme)s'.") % { "uri": self.data,
"scheme": scheme }
return _("The specified URI contains an unsupported scheme.")
class UnsupportedRepositoryURIAttribute(PublisherError):
"""Used to indicate that the specified repository URI attribute is not
supported for the URI's scheme."""
def __str__(self):
return _("'%(attr)s' is not supported for '%(scheme)s'.") % {
"attr": self.data, "scheme": self._args["scheme"] }
class SigningException(ApiException):
"""The base class for exceptions related to manifest signing."""
def __init__(self, pfmri=None, sig=None):
self.pfmri = pfmri
self.sig = sig
# This string method is used by subclasses to fill in the details
# about the package and signature involved.
def __str__(self):
if self.pfmri:
if self.sig:
return _("The relevant signature action is "
"found in %(pfmri)s and has a hash of "
"%(hsh)s") % \
{"pfmri": self.pfmri, "hsh": self.sig.hash}
return _("The package involved is:%s") % self.pfmri
if self.sig:
return _("The relevant signature action's value "
"attribute is %s") % self.sig.attrs["value"]
return ""
class BadFileFormat(SigningException):
"""Exception used when a key, certificate or CRL file is not in a
recognized format."""
def __init__(self, txt):
self.txt = txt
def __str__(self):
return self.txt
class UnsupportedSignatureVersion(SigningException):
"""Exception used when a signature reports a version which this version
of pkg(5) doesn't support."""
def __init__(self, version, *args, **kwargs):
SigningException.__init__(self, *args, **kwargs)
self.version = version
def __str__(self):
return _("The signature action %(act)s was made using a "
"version (%(ver)s) this version of pkg(5) doesn't "
"understand.") % {"act":self.sig, "ver":self.version}
class CertificateException(SigningException):
"""Base class for exceptions encountered while establishing the chain
of trust."""
def __init__(self, cert, pfmri=None):
SigningException.__init__(self, pfmri)
self.cert = cert
class ModifiedCertificateException(CertificateException):
"""Exception used when a certificate does not match its expected hash
value."""
def __init__(self, cert, path, pfmri=None):
CertificateException.__init__(self, cert, pfmri)
self.path = path
def __str__(self):
return _("Certificate %s has been modified on disk. Its hash "
"value is not what was expected.") % self.path
class UntrustedSelfSignedCert(CertificateException):
"""Exception used when a chain of trust is rooted in an untrusted
self-signed certificate."""
def __str__(self):
return _("Chain was rooted in an untrusted self-signed "
"certificate.\n") + CertificateException.__str__(self)
class BrokenChain(CertificateException):
"""Exception used when a chain of trust can not be established between
the leaf certificate and a trust anchor."""
def __init__(self, cert, cert_exceptions, *args, **kwargs):
CertificateException.__init__(self, cert, *args, **kwargs)
self.ext_exs = cert_exceptions
def __str__(self):
s = ""
if self.ext_exs:
s = _("The following problems were encountered:\n") + \
"\n".join([str(e) for e in self.ext_exs])
return _("The certificate which issued this "
"certificate:%(subj)s could not be found. The issuer "
"is:%(issuer)s\n") % {"subj":self.cert.get_subject(),
"issuer":self.cert.get_issuer()} + s + \
CertificateException.__str__(self)
class RevokedCertificate(CertificateException):
"""Exception used when a chain of trust contains a revoked certificate.
"""
def __init__(self, cert, reason, *args, **kwargs):
CertificateException.__init__(self, cert, *args, **kwargs)
self.reason = reason
def __str__(self):
return _("This certificate was revoked:%(cert)s for this "
"reason:\n%(reason)s") % {"cert":self.cert.get_subject(),
"reason":self.reason} + CertificateException.__str__(self)
class UnverifiedSignature(SigningException):
"""Exception used when a signature could not be verified by the
expected certificate."""
def __init__(self, sig, reason, pfmri=None):
SigningException.__init__(self, pfmri)
self.sig = sig
self.reason = reason
def __str__(self):
if self.pfmri:
return _("A signature in %(pfmri)s could not be "
"verified for "
"this reason:\n%(reason)s\nThe signature's hash is "
"%(hash)s") % {"pfmri": self.pfmri,
"reason": self.reason,
"hash": self.sig.hash}
return _("The signature with this signature value:\n"
"%(sigval)s\n could not be verified for this reason:\n"
"%(reason)s\n") % {"reason": self.reason,
"sigval": self.sig.attrs["value"]}
class RequiredSignaturePolicyException(SigningException):
"""Exception used when signatures were required but none were found."""
def __init__(self, pub, pfmri=None):
SigningException.__init__(self, pfmri)
self.pub = pub
def __str__(self):
pub_str = self.pub.prefix
if self.pfmri:
return _("The policy for %(pub_str)s requires "
"signatures to be present but no signature was "
"found in %(fmri_str)s.") % \
{"pub_str": pub_str, "fmri_str": self.pfmri}
return _("The policy for %(pub_str)s requires signatures to be "
"present but no signature was found.") % {
"pub_str": pub_str}
class MissingRequiredNamesException(SigningException):
"""Exception used when a signature policy required names to be seen
which weren't seen."""
def __init__(self, pub, missing_names, pfmri=None):
SigningException.__init__(self, pfmri)
self.pub = pub
self.missing_names = missing_names
def __str__(self):
pub_str = self.pub.prefix
if self.pfmri:
return _("The policy for %(pub_str)s requires certain "
"CNs to be seen in a chain of trust. The following "
"required names couldn't be found for this "
"package:%(fmri_str)s.\n%(missing)s") % \
{"pub_str": pub_str, "fmri_str": self.pfmri,
"missing": "\n".join(self.missing_names)}
return _("The policy for %(pub_str)s requires certain CNs to "
"be seen in a chain of trust. The following required names "
"couldn't be found.\n%(missing)s") % {"pub_str": pub_str,
"missing": "\n".join(self.missing_names)}
class UnsupportedCriticalExtension(SigningException):
"""Exception used when a certificate in the chain of trust uses a
critical extension pkg5 doesn't understand."""
def __init__(self, cert, ext):
SigningException.__init__(self)
self.cert = cert
self.ext = ext
def __str__(self):
return _("The certificate whose subject is %(cert)s could not "
"be verified "
"because it uses a critical extension that pkg5 cannot "
"handle yet.\nExtension name:%(name)s\nExtension "
"value:%(val)s") % {"cert": self.cert.get_subject(),
"name":self.ext.get_name(), "val":self.ext.get_value()}
class InvalidPropertyValue(ApiException):
"""Exception used when a property was set to an invalid value."""
def __init__(self, s):
ApiException.__init__(self)
self.str = s
def __str__(self):
return self.str
class CertificateError(ApiException):
"""Base exception class for all certificate exceptions."""
def __init__(self, *args, **kwargs):
ApiException.__init__(self, *args)
if args:
self.data = args[0]
else:
self.data = None
self._args = kwargs
def __str__(self):
return str(self.data)
class ExpiredCertificate(CertificateError):
"""Used to indicate that a certificate has expired."""
def __str__(self):
publisher = self._args.get("publisher", None)
uri = self._args.get("uri", None)
if publisher:
if uri:
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s' needed to access '%(uri)s', "
"has expired. Please install a valid "
"certificate.") % { "cert": self.data,
"pub": publisher, "uri": uri }
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s', has expired. Please install a valid "
"certificate.") % { "cert": self.data,
"pub": publisher }
if uri:
return _("Certificate '%(cert)s', needed to access "
"'%(uri)s', has expired. Please install a valid "
"certificate.") % { "cert": self.data, "uri": uri }
return _("Certificate '%s' has expired. Please install a "
"valid certificate.") % self.data
class ExpiringCertificate(CertificateError):
"""Used to indicate that a certificate has expired."""
def __str__(self):
publisher = self._args.get("publisher", None)
uri = self._args.get("uri", None)
days = self._args.get("days", 0)
if publisher:
if uri:
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s', needed to access '%(uri)s', "
"will expire in '%(days)s' days.") % {
"cert": self.data, "pub": publisher,
"uri": uri, "days": days }
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s' will expire in '%(days)s' days.") % {
"cert": self.data, "pub": publisher, "days": days }
if uri:
return _("Certificate '%(cert)s', needed to access "
"'%(uri)s', will expire in '%(days)s' days.") % {
"cert": self.data, "uri": uri, "days": days }
return _("Certificate '%(cert)s' will expire in "
"'%(days)s' days.") % { "cert": self.data, "days": days }
class InvalidCertificate(CertificateError):
"""Used to indicate that a certificate is invalid."""
def __str__(self):
publisher = self._args.get("publisher", None)
uri = self._args.get("uri", None)
if publisher:
if uri:
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s', needed to access '%(uri)s', is "
"invalid.") % { "cert": self.data,
"pub": publisher, "uri": uri }
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s' is invalid.") % { "cert": self.data,
"pub": publisher }
if uri:
return _("Certificate '%(cert)s' needed to access "
"'%(uri)s' is invalid.") % { "cert": self.data,
"uri": uri }
return _("Invalid certificate '%s'.") % self.data
class NoSuchKey(CertificateError):
"""Used to indicate that a key could not be found."""
def __str__(self):
publisher = self._args.get("publisher", None)
uri = self._args.get("uri", None)
if publisher:
if uri:
return _("Unable to locate key '%(key)s' for "
"publisher '%(pub)s' needed to access "
"'%(uri)s'.") % { "key": self.data,
"pub": publisher, "uri": uri }
return _("Unable to locate key '%(key)s' for publisher "
"'%(pub)s'.") % { "key": self.data, "pub": publisher
}
if uri:
return _("Unable to locate key '%(key)s' needed to "
"access '%(uri)s'.") % { "key": self.data,
"uri": uri }
return _("Unable to locate key '%s'.") % self.data
class NoSuchCertificate(CertificateError):
"""Used to indicate that a certificate could not be found."""
def __str__(self):
publisher = self._args.get("publisher", None)
uri = self._args.get("uri", None)
if publisher:
if uri:
return _("Unable to locate certificate "
"'%(cert)s' for publisher '%(pub)s' needed "
"to access '%(uri)s'.") % {
"cert": self.data, "pub": publisher,
"uri": uri }
return _("Unable to locate certificate '%(cert)s' for "
"publisher '%(pub)s'.") % { "cert": self.data,
"pub": publisher }
if uri:
return _("Unable to locate certificate '%(cert)s' "
"needed to access '%(uri)s'.") % {
"cert": self.data, "uri": uri }
return _("Unable to locate certificate '%s'.") % self.data
class NotYetValidCertificate(CertificateError):
"""Used to indicate that a certificate is not yet valid (future
effective date)."""
def __str__(self):
publisher = self._args.get("publisher", None)
uri = self._args.get("uri", None)
if publisher:
if uri:
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s', needed to access '%(uri)s', "
"has a future effective date.") % {
"cert": self.data, "pub": publisher,
"uri": uri }
return _("Certificate '%(cert)s' for publisher "
"'%(pub)s' has a future effective date.") % {
"cert": self.data, "pub": publisher }
if uri:
return _("Certificate '%(cert)s' needed to access "
"'%(uri)s' has a future effective date.") % {
"cert": self.data, "uri": uri }
return _("Certificate '%s' has a future effective date.") % \
self.data
class ServerReturnError(ApiException):
"""This exception is used when the server returns a line which the
client cannot parse correctly."""
def __init__(self, line):
ApiException.__init__(self)
self.line = line
def __str__(self):
return _("Gave a bad response:%s") % self.line
class MissingFileArgumentException(ApiException):
"""This exception is used when a file was given as an argument but
no such file could be found."""
def __init__(self, path):
ApiException.__init__(self)
self.path = path
def __str__(self):
return _("Could not find %s") % self.path
class ManifestError(ApiException):
"""Base exception class for all manifest exceptions."""
def __init__(self, *args, **kwargs):
ApiException.__init__(self, *args, **kwargs)
if args:
self.data = args[0]
else:
self.data = None
self._args = kwargs
def __str__(self):
return str(self.data)
class BadManifestSignatures(ManifestError):
"""Used to indicate that the Manifest signatures are not valid."""
def __str__(self):
if self.data:
return _("The signature data for the manifest of the "
"'%s' package is not valid.") % self.data
return _("The signature data for the manifest is not valid.")
class UnknownErrors(ApiException):
"""Used to indicate that one or more exceptions were encountered.
This is intended for use with where multiple exceptions for multiple
files are encountered and the errors have been condensed into a
single exception and re-raised. One example case would be rmtree()
with shutil.Error."""
def __init__(self, msg):
ApiException.__init__(self)
self.__msg = msg
def __str__(self):
return self.__msg
# Image creation exceptions
class ImageCreationException(ApiException):
def __init__(self, path):
ApiException.__init__(self)
self.path = path
def __str__(self):
raise NotImplementedError()
class ImageAlreadyExists(ImageCreationException):
def __str__(self):
return _("there is already an image at: %s.\nTo override, use "
"the -f (force) option.") % self.path
class ImageCfgEmptyError(ApiException):
"""Used to indicate that the image configuration is invalid."""
def __str__(self):
return _("The configuration data for the image located at "
"'%s' is empty or missing.") % self.data
class CreatingImageInNonEmptyDir(ImageCreationException):
def __str__(self):
return _("the specified image path is not empty: %s.\nTo "
"override, use the -f (force) option.") % self.path
def _convert_error(e, ignored_errors=EmptyI):
"""Converts the provided exception into an ApiException equivalent if
possible. Returns a new exception object if converted or the original
if not.
'ignored_errors' is an optional list of errno values for which None
should be returned.
"""
if not hasattr(e, "errno"):
return e
if e.errno in ignored_errors:
return None
if e.errno in (errno.EACCES, errno.EPERM):
return PermissionsException(e.filename)
if e.errno == errno.EROFS:
return ReadOnlyFileSystemException(e.filename)
return e