--- a/src/modules/client/image.py Mon Mar 09 13:16:54 2009 +0000
+++ b/src/modules/client/image.py Mon Mar 09 16:09:13 2009 -0500
@@ -20,12 +20,13 @@
# CDDL HEADER END
#
+#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+#
import cPickle
-import calendar
-import datetime
+import copy
import errno
import httplib
import os
@@ -37,12 +38,6 @@
import urllib
import urllib2
-import OpenSSL.crypto as osc
-
-from pkg.misc import msg, emsg
-
-# import uuid # XXX interesting 2.5 module
-
import pkg.Uuid25
import pkg.catalog as catalog
import pkg.client.api_errors as api_errors
@@ -56,26 +51,26 @@
import pkg.client.progress as progress
import pkg.client.query_engine as query_e
import pkg.client.retrieve as retrieve
+import pkg.client.publisher as publisher
import pkg.client.variant as variant
import pkg.fmri
import pkg.manifest as manifest
import pkg.misc as misc
import pkg.portable as portable
import pkg.search_errors as search_errors
-import pkg.updatelog as updatelog
import pkg.version
-from pkg.misc import versioned_urlopen
-from pkg.misc import EmptyI, EmptyDict
-from pkg.misc import TransportException
-from pkg.misc import TransferTimedOutException
-from pkg.misc import TransportFailures
-from pkg.misc import CLIENT_DEFAULT_MEM_USE_KB
-from pkg.misc import CfgCacheError
from pkg.actions import MalformedActionError
from pkg.client import global_settings
from pkg.client.api_errors import InvalidDepotResponseException
-from pkg.client.imagetypes import *
+from pkg.client.imagetypes import IMG_USER, IMG_ENTIRE
+from pkg.misc import CLIENT_DEFAULT_MEM_USE_KB
+from pkg.misc import CfgCacheError
+from pkg.misc import EmptyI, EmptyDict
+from pkg.misc import msg, emsg
+from pkg.misc import versioned_urlopen
+from pkg.misc import TransportException
+from pkg.misc import TransportFailures
img_user_prefix = ".org.opensolaris,pkg"
img_root_prefix = "var/pkg"
@@ -83,9 +78,6 @@
PKG_STATE_INSTALLED = "installed"
PKG_STATE_KNOWN = "known"
-# Minimum number of days to issue warning before a certificate expires
-MIN_WARN_DAYS = datetime.timedelta(days=30)
-
class Image(object):
"""An Image object is a directory tree containing the laid-down contents
of a self-consistent graph of Packages.
@@ -199,7 +191,7 @@
# right now we don't explicitly set dir/file modes everywhere;
# set umask to proper value to prevent problems w/ overly
- # locked down umask.
+ # locked down umask.
os.umask(0022)
def _check_subdirs(self, sub_d, prefix):
@@ -222,7 +214,7 @@
self._check_subdirs(d, img_root_prefix):
rv = IMG_ENTIRE
return rv
-
+
def find_root(self, d, exact_match=False):
# Ascend from the given directory d to find first
# encountered image. If exact_match is true, if the
@@ -288,18 +280,22 @@
# make sure we define architecture variant; upgrade config
# file if possible.
if "variant.arch" not in self.cfg_cache.variants:
- self.cfg_cache.variants["variant.arch"] = platform.processor()
+ self.cfg_cache.variants["variant.arch"] = \
+ platform.processor()
try:
self.save_config()
except api_errors.PermissionsException:
pass
# make sure we define zone variant; upgrade config if possible
if "variant.opensolaris.zone" not in self.cfg_cache.variants:
- zone = self.cfg_cache.filters.get("opensolaris.zone", "")
+ zone = self.cfg_cache.filters.get("opensolaris.zone",
+ "")
if zone == "nonglobal":
- self.cfg_cache.variants["variant.opensolaris.zone"] = "nonglobal"
+ self.cfg_cache.variants[
+ "variant.opensolaris.zone"] = "nonglobal"
else:
- self.cfg_cache.variants["variant.opensolaris.zone"] = "global"
+ self.cfg_cache.variants[
+ "variant.opensolaris.zone"] = "global"
try:
self.save_config()
except api_errors.PermissionsException:
@@ -335,12 +331,14 @@
self.dl_cache_incoming = os.path.normpath(os.path.join(
self.dl_cache_dir, "incoming-%d" % os.getpid()))
- def set_attrs(self, type, root, is_zone, auth_name, auth_url,
- ssl_key=None, ssl_cert=None, variants=EmptyDict, refresh_allowed=True):
-
- self.__set_dirs(imgtype=type, root=root)
-
- if not os.path.exists(os.path.join(self.imgdir, imageconfig.CFG_FILE)):
+ def set_attrs(self, imgtype, root, is_zone, prefix, pub_url,
+ ssl_key=None, ssl_cert=None, variants=EmptyDict,
+ refresh_allowed=True):
+
+ self.__set_dirs(imgtype=imgtype, root=root)
+
+ if not os.path.exists(os.path.join(self.imgdir,
+ imageconfig.CFG_FILE)):
self.history.operation_name = "image-create"
else:
self.history.operation_name = "image-set-attributes"
@@ -351,29 +349,25 @@
if is_zone:
self.cfg_cache.filters["opensolaris.zone"] = "nonglobal"
- self.cfg_cache.variants["variant.opensolaris.zone"] = "nonglobal"
+ self.cfg_cache.variants[
+ "variant.opensolaris.zone"] = "nonglobal"
else:
- self.cfg_cache.variants["variant.opensolaris.zone"] = "global"
-
- newauth = {}
-
- newauth["prefix"] = auth_name
- newauth["origin"] = misc.url_affix_trailing_slash(auth_url)
- newauth["disabled"] = False
- newauth["mirrors"] = []
- newauth["ssl_key"] = ssl_key
- newauth["ssl_cert"] = ssl_cert
- newauth["uuid"] = pkg.Uuid25.uuid1()
+ self.cfg_cache.variants[
+ "variant.opensolaris.zone"] = "global"
+
+ repo = publisher.Repository()
+ repo.add_origin(pub_url, ssl_cert=ssl_cert, ssl_key=ssl_key)
+ newpub = publisher.Publisher(prefix, repositories=[repo])
# Refresh catalog for new server, if allowed.
if refresh_allowed:
self.retrieve_catalogs(full_refresh=True,
- auths=[newauth])
+ pubs=[newpub])
# If we reached this point, the refresh succeeded. Add the
- # authority to the cfg_cache and save the config.
- self.cfg_cache.authorities[auth_name] = newauth
- self.cfg_cache.preferred_authority = auth_name
+ # publisher to the cfg_cache and save the config.
+ self.cfg_cache.publishers[prefix] = newpub
+ self.cfg_cache.preferred_publisher = prefix
self.cfg_cache.variants["variant.arch"] = \
variants.get("variant.arch", platform.processor())
@@ -386,7 +380,8 @@
return self.root == "/"
def is_zone(self):
- return self.cfg_cache.variants["variant.opensolaris.zone"] == "nonglobal"
+ return self.cfg_cache.variants[
+ "variant.opensolaris.zone"] == "nonglobal"
def get_arch(self):
return self.cfg_cache.variants["variant.arch"]
@@ -394,67 +389,67 @@
def get_root(self):
return self.root
- def gen_authorities(self, inc_disabled = False):
+ def gen_publishers(self, inc_disabled=False):
if not self.cfg_cache:
raise CfgCacheError, "empty ImageConfig"
- if not self.cfg_cache.authorities:
- raise CfgCacheError, "no defined authorities"
- for a in self.cfg_cache.authorities:
- auth = self.cfg_cache.authorities[a]
- if inc_disabled or not auth["disabled"]:
- yield self.cfg_cache.authorities[a]
-
- def get_url_by_authority(self, authority = None):
- """Return the URL prefix associated with the given authority.
+ if not self.cfg_cache.publishers:
+ raise CfgCacheError, "no defined publishers"
+ for p in self.cfg_cache.publishers:
+ pub = self.cfg_cache.publishers[p]
+ if inc_disabled or not pub.disabled:
+ yield self.cfg_cache.publishers[p]
+
+ def get_url_by_publisher(self, prefix=None):
+ """Return the URL prefix associated with the given prefix.
For the undefined case, represented by None, return the
- preferred authority."""
+ preferred publisher."""
# XXX This function is a possible location to insert one or more
# policies regarding use of mirror responses, etc.
- if authority == None:
- authority = self.cfg_cache.preferred_authority
+ if prefix is None:
+ prefix = self.cfg_cache.preferred_publisher
try:
- o = self.cfg_cache.authorities[authority]["origin"]
+ o = self.cfg_cache.publishers[prefix]["origin"]
except KeyError:
- # If the authority that we're trying to get no longer
- # exists, fall back to preferred authority.
- authority = self.cfg_cache.preferred_authority
- o = self.cfg_cache.authorities[authority]["origin"]
+ # If the publisher that we're trying to get no longer
+ # exists, fall back to preferred publisher.
+ prefix = self.cfg_cache.preferred_publisher
+ o = self.cfg_cache.publishers[prefix]["origin"]
return o.rstrip("/")
def gen_depot_status(self):
- """Walk all authorities and return all depot status
- objects for both mirrors and primary authorities."""
-
- auths = self.cfg_cache.authorities
- # return depot status objects in authority order
- for auth in auths.keys():
- # first yield authority origin
- yield self.cfg_cache.authority_status[auth]
+ """Walk all publishers and return all depot status
+ objects for both mirrors and primary publishers."""
+
+ pubs = self.cfg_cache.publishers
+ # return depot status objects in publisher order
+ for pub in pubs.keys():
+ # first yield publisher origin
+ yield self.cfg_cache.publisher_status[pub]
# then return mirrors
- for ds in self.cfg_cache.mirror_status[auth]:
+ for ds in self.cfg_cache.mirror_status[pub]:
yield ds
- def num_mirrors(self, auth):
+ def num_mirrors(self, pub):
"""Return the number of mirrors configured for the
- given authority."""
-
- if auth == None:
- auth = self.cfg_cache.preferred_authority
+ given publisher."""
+
+ if pub == None:
+ pub = self.cfg_cache.preferred_publisher
try:
- num = len(self.cfg_cache.mirror_status[auth])
+ num = len(self.cfg_cache.mirror_status[pub])
except KeyError:
- # Auth isn't in the list of mirrors, return 0
+ # pub isn't in the list of mirrors, return 0
num = 0
return num
- def select_mirror(self, auth = None, chosen_set = None):
- """For the given authority, look through the status of
+ def select_mirror(self, pub = None, chosen_set = None):
+ """For the given publisher, look through the status of
the mirrors. Pick the best one. This method returns
a DepotStatus object or None. The chosen_set argument
contains a set object that lists the mirrors that were
@@ -462,19 +457,19 @@
by depot status statistics and ensures we don't
always pick the same depot."""
- if auth == None:
- auth = self.cfg_cache.preferred_authority
+ if pub == None:
+ pub = self.cfg_cache.preferred_publisher
try:
- slst = self.cfg_cache.mirror_status[auth]
+ slst = self.cfg_cache.mirror_status[pub]
except KeyError:
- # If the authority that we're trying to get no longer
- # exists, fall back to preferred authority.
- auth = self.cfg_cache.preferred_authority
- slst = self.cfg_cache.mirror_status[auth]
+ # If the publisher that we're trying to get no longer
+ # exists, fall back to preferred publisher.
+ pub = self.cfg_cache.preferred_publisher
+ slst = self.cfg_cache.mirror_status[pub]
if len(slst) == 0:
- if auth in self.cfg_cache.authority_status:
- return self.cfg_cache.authority_status[auth]
+ if pub in self.cfg_cache.publisher_status:
+ return self.cfg_cache.publisher_status[pub]
else:
return None
@@ -493,11 +488,11 @@
slst.sort(cmp = cmp_depotstatus)
# All mirrors in the chosen_set have already been
- # selected. Try the authority origin instead.
+ # selected. Try the publisher origin instead.
# Empty chosen_set, next time we start over.
if chosen_set and len(chosen_set) == len(slst):
chosen_set.clear()
- return self.cfg_cache.authority_status[auth]
+ return self.cfg_cache.publisher_status[pub]
if chosen_set and slst[0] in chosen_set:
for ds in slst:
@@ -506,270 +501,179 @@
return slst[0]
- def get_ssl_credentials(self, authority=None, origin=None,
- authent=None):
- """Return a tuple containing (ssl_key, ssl_cert) for the
- specified authority prefix. If the authority isn't specified,
- attempt to determine the authority by the given origin. If
- neither is specified, use the preferred authority. Authent
- is a dictionary argument that contains the authority
- information.
- """
-
- # If authent supplied, don't bother with any other
- # fancy processing.
- if authent:
- return authent["ssl_key"], authent["ssl_cert"]
-
- if authority is None:
+ def get_ssl_credentials(self, prefix=None, origin=None,
+ pubent=None):
+ """Deprecated; this function will be removed in a future
+ release. This information should be retrieved directly from a
+ repository origin or mirror object.
+
+ Return a tuple containing (ssl_key, ssl_cert) for the
+ specified publisher prefix. If the publisher isn't specified,
+ attempt to determine the publisher by the given origin. If
+ neither is specified, use the preferred publisher. pubent
+ is a dictionary argument that contains the publisher
+ information."""
+
+ if not pubent and prefix is None:
if origin is None:
- authority = self.cfg_cache.preferred_authority
+ prefix = self.cfg_cache.preferred_publisher
else:
- auths = self.cfg_cache.authorities
- for pfx, auth in auths.iteritems():
- if auth["origin"] == origin:
- authority = pfx
+ pubs = self.cfg_cache.publishers
+ for pfx, pub in pubs.iteritems():
+ repo = pub.selected_repository
+ if repo.has_origin(origin):
+ prefix = pfx
break
else:
return None
- try:
- authent = self.cfg_cache.authorities[authority]
- except KeyError:
- authority = self.cfg_cache.preferred_authority
- authent = self.cfg_cache.authorities[authority]
-
- return (authent["ssl_key"], authent["ssl_cert"])
-
- @staticmethod
- def build_cert(path):
- """Take the file given in path, open it, and use it to create
- an X509 certificate object."""
-
- cf = file(path, "rb")
- certdata = cf.read()
- cf.close()
- cert = osc.load_certificate(osc.FILETYPE_PEM, certdata)
-
- return cert
+
+ # One of these should be defined at this point unless the
+ # caller didn't provide anything.
+ assert prefix or origin or pubent
+
+ if not pubent:
+ try:
+ pubent = self.cfg_cache.publishers[prefix]
+ except KeyError:
+ prefix = self.cfg_cache.preferred_publisher
+ pubent = self.cfg_cache.publishers[prefix]
+
+ repo = pubent.selected_repository
+ origin = repo.origins[0]
+ return (origin.ssl_key, origin.ssl_cert)
def check_cert_validity(self):
- """Look through the authorities defined for the image. Print
+ """Look through the publishers defined for the image. Print
a message and exit with an error if one of the certificates
has expired. If certificates are getting close to expiration,
print a warning instead."""
- for a in self.gen_authorities():
- pfx, url, ssl_key, ssl_cert, dt, mir = \
- self.split_authority(a)
-
- if not ssl_cert:
- continue
-
- try:
- cert = self.build_cert(ssl_cert)
- except IOError, e:
- if e.errno == errno.ENOENT:
- emsg(_("Certificate for authority %s" \
- " not found") % pfx)
- emsg(_("File was supposed to exist at" \
- " path %s") % ssl_cert)
- return False
- else:
- raise
- # OpenSSL.crypto.Error
- except osc.Error, e:
- emsg(_("Certificate for authority %(pfx)s at" \
- " %(ssl_cert)s has an invalid format.") % \
- vars())
- return False
-
- if cert.has_expired():
- emsg(_("Certificate for authority %s" \
- " has expired") % pfx)
- emsg(_("Please install a valid certificate"))
- return False
-
- now = datetime.datetime.utcnow()
- nb = cert.get_notBefore()
- t = time.strptime(nb, "%Y%m%d%H%M%SZ")
- nbdt = datetime.datetime.utcfromtimestamp(
- calendar.timegm(t))
-
- # PyOpenSSL's has_expired() doesn't validate the notBefore
- # time on the certificate. Don't ask me why.
-
- if nbdt > now:
- emsg(_("Certificate for authority %s is" \
- " invalid") % pfx)
- emsg(_("Certificate effective date is in" \
- " the future"))
- return False
-
- na = cert.get_notAfter()
- t = time.strptime(na, "%Y%m%d%H%M%SZ")
- nadt = datetime.datetime.utcfromtimestamp(
- calendar.timegm(t))
-
- diff = nadt - now
-
- if diff <= MIN_WARN_DAYS:
- emsg(_("Certificate for authority %s will" \
- " expire in %d days" % (pfx, diff.days)))
-
+ for p in self.gen_publishers():
+ for r in p.repositories:
+ for uri in r.origins:
+ if uri.ssl_cert:
+ misc.validate_ssl_cert(
+ uri.ssl_cert,
+ prefix=p.prefix, uri=uri)
return True
- def get_uuid(self, authority):
- """Return the UUID for the specified authority prefix. If the
- policy for sending the UUID is set to false, return None.
- """
+ def get_uuid(self, prefix):
+ """Deprecated; this function will be removed in a future
+ release. This information should be retrieved directly from a
+ publisher object.
+
+ Return the UUID for the specified publisher prefix. If the
+ policy for sending the UUID is set to false, return None."""
+
if not self.cfg_cache.get_policy(imageconfig.SEND_UUID):
return None
try:
- return self.cfg_cache.authorities[authority]["uuid"]
+ return self.cfg_cache.publishers[prefix].client_uuid
except KeyError:
return None
-
- def get_default_authority(self):
- return self.cfg_cache.preferred_authority
-
- def has_authority(self, auth_name):
- return auth_name in self.cfg_cache.authorities
-
- def delete_authority(self, auth_name):
- self.history.operation_name = "delete-authority"
- if not self.has_authority(auth_name):
- error = "no such authority '%s'" % auth_name
- self.history.operation_errors.append(error)
- self.history.operation_result = \
- history.RESULT_FAILED_UNKNOWN
- raise KeyError, error
- self.cfg_cache.delete_authority(auth_name)
+
+ def has_publisher(self, prefix=None, alias=None):
+ for pub in self.gen_publishers():
+ if prefix == pub.prefix or (alias and
+ alias == pub.alias):
+ return True
+ return False
+
+ def remove_publisher(self, prefix=None, alias=None):
+ self.history.log_operation_start("remove-publisher")
+ try:
+ pub = self.get_publisher(prefix=prefix,
+ alias=alias)
+ except api_errors.ApiException, e:
+ self.history.log_operation_end(e)
+ raise e
+
+ if pub.prefix == self.cfg_cache.preferred_publisher:
+ e = api_errors.RemovePreferredPublisher()
+ self.history.log_operation_end(error=e)
+ raise e
+
+ self.cfg_cache.remove_publisher(prefix)
self.save_config()
- self.destroy_catalog(auth_name)
+ self.destroy_catalog(prefix)
self.cache_catalogs()
- self.history.operation_result = history.RESULT_SUCCEEDED
-
- def get_authority(self, auth_name):
- if not self.has_authority(auth_name):
- raise KeyError, "no such authority '%s'" % auth_name
-
- return self.cfg_cache.authorities[auth_name]
-
- def split_authority(self, auth):
- prefix = auth["prefix"]
+ self.history.log_operation_end()
+
+ def get_publishers(self):
+ return self.cfg_cache.publishers
+
+ def get_publisher(self, prefix=None, alias=None, origin=None):
+ publishers = [p for p in self.get_publishers().values()]
+ for pub in publishers:
+ if prefix and prefix == pub.prefix:
+ return pub
+ elif alias and alias == pub.alias:
+ return pub
+ elif origin and \
+ pub.selected_repository.has_origin(origin):
+ return pub
+ raise api_errors.UnknownPublisher(max(prefix, alias, origin))
+
+ def get_publisher_last_update_time(self, prefix, cached=True):
+ """Returns a datetime object (or 'None') representing the last
+ time the catalog for a publisher was updated.
+
+ If the catalog has already been loaded, this reflects the
+ in-memory state of the catalog.
+
+ If the catalog has not already been loaded or 'cached' is False,
+ then the catalog will be temporarily loaded and the most recent
+ information returned."""
+
+ if not cached:
+ try:
+ cat = self.catalogs[prefix]
+ except KeyError:
+ pass
+ else:
+ update_dt = cat.last_modified()
+ if update_dt:
+ update_dt = catalog.ts_to_datetime(update_dt)
+ return update_dt
+
+ # Temporarily retrieve the catalog object, but don't
+ # cache it as that would interfere with load_catalogs.
+ try:
+ croot = "%s/catalog/%s" % (self.imgdir, prefix)
+ cat = catalog.Catalog(croot, publisher=prefix)
+ except (EnvironmentError, catalog.CatalogException):
+ cat = None
+
update_dt = None
-
- try:
- cat = self.catalogs[prefix]
- except KeyError:
- cat = None
-
if cat:
update_dt = cat.last_modified()
if update_dt:
update_dt = catalog.ts_to_datetime(update_dt)
-
- return (prefix, auth["origin"], auth["ssl_key"],
- auth["ssl_cert"], update_dt, auth["mirrors"])
-
- def set_preferred_authority(self, auth_name):
- self.history.operation_name = "set-preferred-authority"
- if not self.has_authority(auth_name):
- error = "no such authority '%s'" % auth_name
- self.history.operation_errors.append(error)
- self.history.operation_result = \
- history.RESULT_FAILED_UNKNOWN
- raise KeyError, error
- if self.get_authority(auth_name)["disabled"]:
- error = "authority '%s' is disabled" % auth_name
- self.history.operation_errors.append(error)
- self.history.operation_result = \
- history.RESULT_FAILED_BAD_REQUEST
- raise KeyError, error
- self.cfg_cache.preferred_authority = auth_name
+ return update_dt
+
+ def get_preferred_publisher(self):
+ """Returns the prefix of the preferred publisher."""
+ return self.cfg_cache.preferred_publisher
+
+ def set_preferred_publisher(self, prefix=None, alias=None):
+ self.history.log_operation_start("set-preferred-publisher")
+ try:
+ pub = self.get_publisher(prefix=prefix, alias=alias)
+ except api_errors.UnknownPublisher, e:
+ self.history.log_operation_end(error=e)
+ raise e
+
+ if pub.disabled:
+ e = api_errors.SetPreferredPublisherDisabled(pub)
+ self.history.log_operation_end(error=e)
+ raise e
+ self.cfg_cache.preferred_publisher = pub.prefix
self.save_config()
- self.history.operation_result = history.RESULT_SUCCEEDED
-
- def set_authority(self, auth_name, origin_url = None, ssl_key = None,
- ssl_cert = None, refresh_allowed = True, uuid = None,
- disabled = None):
- self.history.operation_name = "set-authority"
- auths = self.cfg_cache.authorities
-
- refresh_catalog = False
- purge_catalog = False
-
- if auth_name in auths:
- # Copy old authority information to new entry.
- oldauth = auths[auth_name]
- newauth = oldauth.copy()
-
- # Update any fields that have changed.
- if origin_url:
- newauth["origin"] = \
- misc.url_affix_trailing_slash(origin_url)
- refresh_catalog = True
- if ssl_key:
- newauth["ssl_key"] = ssl_key
- if ssl_cert:
- newauth["ssl_cert"] = ssl_cert
- if uuid:
- newauth["uuid"] = uuid
- if disabled != None:
- # don't make the preferred authority disabled
- # the caller is responsible for checking this
- assert(not disabled or \
- auth_name != self.get_default_authority())
- newauth["disabled"] = disabled
- if disabled:
- purge_catalog = True
- else:
- refresh_catalog = True
-
- else:
- newauth = {}
- newauth["prefix"] = auth_name
- newauth["origin"] = \
- misc.url_affix_trailing_slash(origin_url)
- newauth["mirrors"] = []
- newauth["ssl_key"] = ssl_key
- newauth["ssl_cert"] = ssl_cert
- if not uuid:
- uuid = pkg.Uuid25.uuid1()
- newauth["uuid"] = uuid
- if disabled is None:
- disabled = False
- newauth["disabled"] = disabled
- if not newauth["disabled"]:
- refresh_catalog = True
-
- if refresh_catalog or purge_catalog:
- try:
- self.destroy_catalog(auth_name)
- self.destroy_catalog_cache()
- except EnvironmentError, e:
- if e.errno == errno.EACCES:
- raise api_errors.PermissionsException(
- e.filename)
- raise
-
- if purge_catalog:
- self.cache_catalogs()
- elif refresh_allowed:
- self.retrieve_catalogs(full_refresh=True,
- auths=[newauth])
-
- # If the code got here, it successfully refreshed
- # the authority, and passed any sanity checks. Save
- # the configuration.
- auths[auth_name] = newauth
- self.save_config()
-
- self.history.operation_result = history.RESULT_SUCCEEDED
+ self.history.log_operation_end()
def set_property(self, prop_name, prop_value):
- assert prop_name != "preferred-authority"
+ assert prop_name != "preferred-publisher"
self.cfg_cache.properties[prop_name] = prop_value
self.save_config()
@@ -780,7 +684,7 @@
return prop_name in self.cfg_cache.properties
def delete_property(self, prop_name):
- assert prop_name != "preferred-authority"
+ assert prop_name != "preferred-publisher"
del self.cfg_cache.properties[prop_name]
self.save_config()
@@ -788,37 +692,40 @@
for p in self.cfg_cache.properties:
yield p
- def add_mirror(self, auth_name, mirror):
- """Add the mirror URL contained in mirror to
- auth_name's list of mirrors."""
- self.history.operation_name = "add-mirror"
- auths = self.cfg_cache.authorities
- auths[auth_name]["mirrors"].append(mirror)
+ def add_publisher(self, pub, refresh_allowed=True):
+ """Adds the provided publisher object to the image
+ configuration."""
+ self.history.log_operation_start("add-publisher")
+ for p in self.cfg_cache.publishers.values():
+ if pub == p or (pub.alias and pub.alias == p.alias):
+ error = api_errors.DuplicatePublisher(pub)
+ self.history.log_operation_end(error=error)
+ raise error
+
+ try:
+ self.destroy_catalog(pub.prefix)
+ self.destroy_catalog_cache()
+ except EnvironmentError, e:
+ if e.errno == errno.EACCES:
+ raise api_errors.PermissionsException(
+ e.filename)
+ raise
+
+ if refresh_allowed:
+ self.retrieve_catalogs(full_refresh=True, pubs=[pub])
+
+ # Only after success should the new publisher be added to the
+ # configuration.
+ self.cfg_cache.publishers[pub.prefix] = pub
self.save_config()
- self.history.operation_result = history.RESULT_SUCCEEDED
-
- def has_mirror(self, auth_name, url):
- """Returns true if url is in auth_name's list of mirrors."""
-
- return url in self.cfg_cache.authorities[auth_name]["mirrors"]
-
- def del_mirror(self, auth_name, mirror):
- """Remove the mirror URL contained in mirror from
- auth_name's list of mirrors."""
-
- self.history.operation_name = "delete-mirror"
- auths = self.cfg_cache.authorities
-
- if mirror in self.cfg_cache.authorities[auth_name]["mirrors"]:
- auths[auth_name]["mirrors"].remove(mirror)
- self.save_config()
- self.history.operation_result = history.RESULT_SUCCEEDED
+ self.history.log_operation_end()
def verify(self, fmri, progresstracker, **args):
"""generator that returns any errors in installed pkgs
as tuple of action, list of errors"""
- for act in self.get_manifest(fmri).gen_actions(self.list_excludes()):
+ for act in self.get_manifest(fmri).gen_actions(
+ self.list_excludes()):
errors = act.verify(self, pkg_fmri=fmri, **args)
progresstracker.verify_add_progress(fmri)
actname = act.distinguished_name()
@@ -880,8 +787,8 @@
raise failures
except MalformedActionError, e:
retry_count -= 1
- auth = fmri.get_authority()
- url = self.cfg_cache.authorities[auth]["origin"]
+ pub = fmri.get_publisher()
+ url = self.cfg_cache.publishers[pub]["origin"]
te = misc.TransferContentException(url=url,
reason=str(e))
failures.append(te)
@@ -891,7 +798,7 @@
return m
- def __get_touched_manifest(self, fmri):
+ def __get_touched_manifest(self, fmri, intent):
"""Returns whether intent information has been provided for the
given fmri."""
@@ -912,9 +819,14 @@
# fmri for the current operation.
return False
+ if intent not in self.__touched_manifests[op][f]:
+ # No intent information has been provided for this
+ # fmri for the current operation and reason.
+ return False
+
return True
- def __set_touched_manifest(self, fmri):
+ def __set_touched_manifest(self, fmri, intent):
"""Records that intent information has been provided for the
given fmri's manifest."""
@@ -933,36 +845,39 @@
if f not in self.__touched_manifests[op]:
# No intent information has yet been provided for this
# fmri for the current operation.
- self.__touched_manifests[op][f] = None
+ self.__touched_manifests[op][f] = { intent: None }
+ else:
+ # No intent information has yet been provided for this
+ # fmri for the current operation and reason.
+ self.__touched_manifests[op][f][intent] = None
def __touch_manifest(self, fmri):
"""Perform steps necessary to 'touch' a manifest to provide
intent information. Ignores most exceptions as this operation
is only for informational purposes."""
- if not self.__get_touched_manifest(fmri):
+ # What is the client currently processing?
+ target, intent = self.state.get_target()
+
+ # Ignore dry-runs of operations or operations which do not have
+ # a set target.
+ if not target or intent == imagestate.INTENT_EVALUATE:
+ return
+
+ if not self.__get_touched_manifest(fmri, intent):
# If the manifest for this fmri hasn't been "seen"
# before, determine if intent information needs to be
# provided.
- # What is the client currently processing?
- target, intent = self.state.get_target()
-
- if target and intent != imagestate.INTENT_EVALUATE:
- # If the client is currently performing an
- # image-modifying operation, not just an
- # an evaluation, then perform further checks.
-
- # Ignore the authority for comparison.
- na_target = target.get_fmri(anarchy=True)
- na_fmri = target.get_fmri(anarchy=True)
-
- if na_target == na_fmri:
- # If the client is currently processing
- # the given fmri (for an install, etc.)
- # then intent information is needed.
- retrieve.touch_manifest(self, fmri)
- self.__set_touched_manifest(fmri)
+ # Ignore the publisher for comparison.
+ np_target = target.get_fmri(anarchy=True)
+ np_fmri = fmri.get_fmri(anarchy=True)
+ if np_target == np_fmri:
+ # If the client is currently processing
+ # the given fmri (for an install, etc.)
+ # then intent information is needed.
+ retrieve.touch_manifest(self, fmri)
+ self.__set_touched_manifest(fmri, intent)
def __fetch_manifest(self, fmri, excludes=EmptyI):
"""Perform steps necessary to get manifest from remote host
@@ -985,14 +900,14 @@
mcontent = retrieve.get_manifest(self, fmri)
m.set_content(mcontent)
- # Write the originating authority into the manifest.
+ # Write the originating publisher into the manifest.
# Manifests prior to this change won't contain this information.
# In that case, the client attempts to re-download the manifest
# from the depot.
- if not fmri.has_authority():
- m["authority"] = self.get_default_authority()
+ if not fmri.has_publisher():
+ m["publisher"] = self.get_preferred_publisher()
else:
- m["authority"] = fmri.get_authority()
+ m["publisher"] = fmri.get_publisher()
try:
m.store(mpath)
@@ -1000,28 +915,42 @@
if e.errno not in (errno.EROFS, errno.EACCES):
raise
- self.__set_touched_manifest(fmri)
-
- # if we were passed actual excludes, reset
+ # What is the client currently processing?
+ targets = self.state.get_targets()
+
+ intent = None
+ for entry in targets:
+ target, reason = entry
+
+ # Ignore the publisher for comparison.
+ np_target = target.get_fmri(anarchy=True)
+ np_fmri = fmri.get_fmri(anarchy=True)
+ if np_target == np_fmri:
+ intent = reason
+
+ # If no intent could be found, assume INTENT_INFO.
+ self.__set_touched_manifest(fmri, max(intent,
+ imagestate.INTENT_INFO))
+
+ # if we were passed actual excludes, reset in-memory
# in-memory content to reflect that.
if excludes:
m.set_content(mcontent, excludes)
return m
- def _valid_manifest(self, fmri, manifest):
- """Check authority attached to manifest. Make sure
- it matches authority specified in FMRI."""
-
- authority = fmri.get_authority()
- if not authority:
- authority = self.get_default_authority()
-
- if not "authority" in manifest:
+ def _valid_manifest(self, fmri, m):
+ """Check publisher attached to manifest. Make sure
+ it matches publisher specified in FMRI."""
+
+ pub = fmri.get_publisher()
+ if not pub:
+ pub = self.get_preferred_publisher()
+
+ try:
+ if m["publisher"] != pub:
+ return False
+ except KeyError:
return False
-
- if manifest["authority"] != authority:
- return False
-
return True
def get_manifest_path(self, fmri):
@@ -1047,10 +976,10 @@
try:
# If the manifest didn't already exist, or isn't from
- # the correct authority, or no authority is attached
+ # the correct publisher, or no publisher is attached
# to the manifest, attempt to download a new one.
if not m or not self._valid_manifest(fmri, m):
- m = self.__fetch_manifest_with_retries(fmri,
+ m = self.__fetch_manifest_with_retries(fmri,
excludes)
except (retrieve.ManifestRetrievalError,
retrieve.DatastreamRetrievalError):
@@ -1089,9 +1018,9 @@
self.__touch_manifest(fmri)
return m
- def installed_file_authority(self, filepath):
+ def installed_file_publisher(self, filepath):
"""Find the pkg's installed file named by filepath.
- Return the authority that installed this package."""
+ Return the publisher that installed this package."""
read_only = False
@@ -1106,47 +1035,48 @@
f = file(filepath, "r")
flines = f.readlines()
- newauth = None
+ newpub = None
try:
- version, auth = flines
+ version, pub = flines
version = version.strip()
- auth = auth.strip()
+ pub = pub.strip()
except ValueError:
# If we get a ValueError, we've encoutered an
# installed file of a previous format. If we want
# upgrade to work in this situation, it's necessary
# to assume that the package was installed from
- # the preferred authority. Here, we set up
- # the authority to record that.
+ # the preferred publisher. Here, we set up
+ # the publisher to record that.
if flines:
- auth = flines[0]
- auth = auth.strip()
- newauth = "%s_%s" % (pkg.fmri.PREF_AUTH_PFX,
- auth)
+ pub = flines[0]
+ pub = pub.strip()
+ newpub = "%s_%s" % (pkg.fmri.PREF_PUB_PFX,
+ pub)
else:
- newauth = "%s_%s" % (pkg.fmri.PREF_AUTH_PFX,
- self.get_default_authority())
+ newpub = "%s_%s" % (pkg.fmri.PREF_PUB_PFX,
+ self.get_preferred_publisher())
# Exception handler is only part of this code that
- # sets newauth
- auth = newauth
-
- if newauth and not read_only:
+ # sets newpub
+ pub = newpub
+
+ if newpub and not read_only:
# This is where we actually update the installed
- # file with the new authority.
+ # file with the new publisher.
f.seek(0)
- f.writelines(["VERSION_1\n", newauth, "\n"])
+ f.writelines(["VERSION_1\n", newpub, "\n"])
f.close()
- assert auth
-
- return auth
+ assert pub
+
+ return pub
def _install_file(self, fmri):
"""Returns the path to the "installed" file for a given fmri."""
- return "%s/pkg/%s/installed" % (self.imgdir, fmri.get_dir_path())
+ return "%s/pkg/%s/installed" % (self.imgdir,
+ fmri.get_dir_path())
def install_file_present(self, fmri):
"""Returns true if the package named by the fmri is installed
@@ -1177,7 +1107,7 @@
raise
f = file(self._install_file(fmri), "w")
- f.writelines(["VERSION_1\n", fmri.get_authority_str(), "\n"])
+ f.writelines(["VERSION_1\n", fmri.get_publisher_str(), "\n"])
f.close()
fi = file("%s/state/installed/%s" % (self.imgdir,
@@ -1223,7 +1153,7 @@
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
return
elif e.errno == errno.EACCES:
- # The directory may exist and be non-empty
+ # The directory may exist and be non-empty
# even though we got EACCES. Try
# to determine its emptiness another way.
try:
@@ -1231,10 +1161,11 @@
len(os.listdir(statedir)) > 0:
return
except EnvironmentError:
- # ignore this error, pass on the original
- # access error
+ # ignore this error, pass on the
+ # original access error
pass
- raise api_errors.PermissionsException(e.filename)
+ raise api_errors.PermissionsException(
+ e.filename)
elif e.errno != errno.ENOENT:
raise
@@ -1249,7 +1180,8 @@
os.makedirs(tmpdir)
except OSError, e:
if e.errno == errno.EACCES:
- raise api_errors.PermissionsException(e.filename)
+ raise api_errors.PermissionsException(
+ e.filename)
if e.errno != errno.EEXIST or \
not os.path.isdir(tmpdir):
raise
@@ -1267,8 +1199,8 @@
continue
fmristr = urllib.unquote("%s@%s" % (pd, vd))
- auth = self.installed_file_authority(path)
- f = pkg.fmri.PkgFmri(fmristr, authority = auth)
+ pub = self.installed_file_publisher(path)
+ f = pkg.fmri.PkgFmri(fmristr, publisher = pub)
fi = file(os.path.join(tmpdir, f.get_link_path()), "w")
fi.close()
@@ -1295,45 +1227,45 @@
return self.pkg_states.get(pfmri.get_fmri(anarchy = True)[5:],
(PKG_STATE_KNOWN, None))[0]
- def get_pkg_auth_by_fmri(self, pfmri):
- """Return the authority from which 'pfmri' was installed."""
+ def get_pkg_pub_by_fmri(self, pfmri):
+ """Return the publisher from which 'pfmri' was installed."""
f = self.pkg_states.get(pfmri.get_fmri(anarchy = True)[5:],
(PKG_STATE_KNOWN, None))[1]
if f:
# Return the non-preferred-prefixed name
- return f.get_authority()
+ return f.get_publisher()
return None
- def fmri_set_default_authority(self, fmri):
+ def fmri_set_default_publisher(self, fmri):
"""If the FMRI supplied as an argument does not have
- an authority, set it to the image's preferred authority."""
-
- if fmri.has_authority():
+ a publisher, set it to the image's preferred publisher."""
+
+ if fmri.has_publisher():
return
- fmri.set_authority(self.get_default_authority(), True)
+ fmri.set_publisher(self.get_preferred_publisher(), True)
def get_catalog(self, fmri, exception = False):
- """Given a FMRI, look at the authority and return the
+ """Given a FMRI, look at the publisher and return the
correct catalog for this image."""
- # If FMRI has no authority, or is default authority,
- # then return the catalog for the preferred authority
- if not fmri.has_authority() or fmri.preferred_authority():
- cat = self.catalogs[self.get_default_authority()]
+ # If FMRI has no publisher, or is default publisher,
+ # then return the catalog for the preferred publisher
+ if not fmri.has_publisher() or fmri.preferred_publisher():
+ cat = self.catalogs[self.get_preferred_publisher()]
else:
try:
- cat = self.catalogs[fmri.get_authority()]
+ cat = self.catalogs[fmri.get_publisher()]
except KeyError:
- # If the authority that installed this package
- # has vanished, pick the default authority
+ # If the publisher that installed this package
+ # has vanished, pick the default publisher
# instead.
if exception:
raise
else:
cat = self.catalogs[\
- self.get_default_authority()]
+ self.get_preferred_publisher()]
return cat
@@ -1343,10 +1275,10 @@
v = self.get_version_installed(fmri)
- if v and not fmri.has_authority():
- fmri.set_authority(v.get_authority_str())
- elif not fmri.has_authority():
- fmri.set_authority(self.get_default_authority(), True)
+ if v and not fmri.has_publisher():
+ fmri.set_publisher(v.get_publisher_str())
+ elif not fmri.has_publisher():
+ fmri.set_publisher(self.get_preferred_publisher(), True)
if v and self.fmri_is_successor(v, fmri):
return True
@@ -1378,7 +1310,7 @@
v = self.get_version_installed(fmri)
- assert fmri.has_authority()
+ assert fmri.has_publisher()
if v:
return v
@@ -1397,8 +1329,8 @@
"""Check that the exact version given in the FMRI is installed
in the current image."""
- # All FMRIs passed to is_installed shall have an authority
- assert fmri.has_authority()
+ # All FMRIs passed to is_installed shall have a publisher
+ assert fmri.has_publisher()
v = self.get_version_installed(fmri)
if not v:
@@ -1407,11 +1339,12 @@
return v == fmri
def list_excludes(self, new_variants=None):
- """Generate a list of callables that each return True if an action
- is to be included in the image using the currently defined
- variants for the image, or an updated set if new_variants are
- specified. The callables take a single action argument.
- Variants, facets and filters will be handled in this fashion."""
+ """Generate a list of callables that each return True if an
+ action is to be included in the image using the currently
+ defined variants for the image, or an updated set if
+ new_variants are specified. The callables take a single action
+ argument. Variants, facets and filters will be handled in
+ this fashion."""
# XXX simple for now; facets and filters need impl.
if new_variants:
new_vars = self.cfg_cache.variants.copy()
@@ -1429,7 +1362,7 @@
progtrack.evaluate_progress(fmri)
mfst = self.get_manifest(fmri)
- for dep in mfst.gen_actions_by_type("depend",
+ for dep in mfst.gen_actions_by_type("depend",
self.list_excludes()):
if dep.attrs["type"] != "require":
continue
@@ -1456,17 +1389,17 @@
dependents.extend(self.__req_dependents[f])
return dependents
- def __do_get_versions(self, auth):
+ def __do_get_versions(self, pub):
"""An internal method that is a wrapper around get_catalog.
This handles retryable exceptions and timeouts."""
retry_count = global_settings.PKG_TIMEOUT_MAX
failures = TransportFailures()
versdict = None
-
+
while not versdict:
try:
- versdict = retrieve.get_versions(self, auth)
+ versdict = retrieve.get_versions(self, pub)
except TransportException, e:
retry_count -= 1
failures.append(e)
@@ -1476,64 +1409,65 @@
return versdict
- def valid_authority_test(self, auth):
- """Test that the authority supplied in auth actually
+ def valid_publisher_test(self, pub):
+ """Test that the publisher supplied in pub actually
points to a valid packaging server."""
try:
- vd = self.__do_get_versions(auth)
+ vd = self.__do_get_versions(pub)
except (retrieve.VersionRetrievalError,
TransportFailures), e:
# Failure when contacting server. Report
# this as an error.
- raise InvalidDepotResponseException(auth["origin"],
+ raise InvalidDepotResponseException(pub["origin"],
"Transport errors encountered when trying to "
"contact depot server. Reported the following "
"errors:\n%s" % e)
if not self._valid_versions_test(vd):
- raise InvalidDepotResponseException(auth["origin"],
+ raise InvalidDepotResponseException(pub["origin"],
"Invalid or unparseable version information.")
return True
def captive_portal_test(self):
"""A captive portal forces a HTTP client on a network
- to see a special web page, usually for authentication
+ to see a special web page, usually for pubentication
purposes. (http://en.wikipedia.org/wiki/Captive_portal)."""
vd = None
- for auth in self.gen_authorities():
+ for pub in self.gen_publishers():
try:
- vd = self.__do_get_versions(auth)
+ vd = self.__do_get_versions(pub)
except (retrieve.VersionRetrievalError,
TransportFailures):
# Encountered a transport error while
- # trying to contact this authority.
- # Pick another authority instead.
+ # trying to contact this publisher.
+ # Pick another publisher instead.
continue
if self._valid_versions_test(vd):
return
else:
- raise InvalidDepotResponseException(auth["origin"],
- "This server is not a valid package depot.")
+ raise InvalidDepotResponseException(
+ pub["origin"], _("This server is not a "
+ "valid package depot."))
if not vd:
- # We got all the way through the list of authorites but
+ # We got all the way through the list of puborites but
# encountered transport errors in every case. This is
# likely a network configuration problem. Report our
# inability to contact a server.
raise InvalidDepotResponseException(None,
- "Unable to contact any configured authorities. "
+ "Unable to contact any configured publishers. "
"This is likely a network configuration problem.")
def _valid_versions_test(self, versdict):
"""Check that the versions information contained in
versdict contains valid version specifications.
- In order to test for this condition, pick an authority
- from the list of active authorities. Check to see if
+ In order to test for this condition, pick a publisher
+ from the list of active publishers. Check to see if
we can connect to it. If so, test to see if it supports
the versions/0 operations. If versions/0 is not found,
we get an unparseable response, or the response does
@@ -1565,7 +1499,7 @@
# Some other error encountered. Fail
return False
- def _do_get_catalog(self, auth, hdr, ts):
+ def _do_get_catalog(self, pub, hdr, ts):
"""An internal method that is a wrapper around get_catalog.
This handles retryable exceptions and timeouts."""
@@ -1575,7 +1509,7 @@
while not success:
try:
- success = retrieve.get_catalog(self, auth,
+ success = retrieve.get_catalog(self, pub,
hdr, ts)
except TransportException, e:
retry_count -= 1
@@ -1585,76 +1519,76 @@
raise failures
def retrieve_catalogs(self, full_refresh = False,
- auths = None, progtrack = None):
+ pubs = None, progtrack = None):
failed = []
total = 0
succeeded = 0
cat = None
ts = 0
- # XXX The authority validation checks depend upon the
- # assumption that callers who pass a list of authorties
- # have newly created the authorities in question and want
+ # XXX The publisher validation checks depend upon the
+ # assumption that callers who pass a list of puborties
+ # have newly created the publishers in question and want
# to specifically refresh their catalogs and verify that they
# are indeed reachable.
- if not auths:
- # If no auths were passed into this routine, we're
+ if not pubs:
+ # If no pubs were passed into this routine, we're
# performing a refresh of all catalogs. In that case,
# it's best to simply check that we're
# not connected to a captive portal.
self.captive_portal_test()
- authlist = list(self.gen_authorities())
+ publist = list(self.gen_publishers())
else:
# The code that's calling us has instantiated
- # new authorities. Verify that each auth is
+ # new publishers. Verify that each pub is
# valid and reachable. This is a more strict
# check than the captive_portal_test()
- authlist = [
- auth
- for auth in auths
- if self.valid_authority_test(auth)
+ publist = [
+ pub
+ for pub in pubs
+ if self.valid_publisher_test(pub)
]
if progtrack:
- progtrack.refresh_start(len(authlist))
-
- for auth in authlist:
- if auth["disabled"]:
+ progtrack.refresh_start(len(publist))
+
+ for pub in publist:
+ if pub.disabled:
continue
-
+
total += 1
if progtrack:
- progtrack.refresh_progress(auth["prefix"])
-
- full_refresh_this_auth = False
-
- if auth["prefix"] in self.catalogs:
- cat = self.catalogs[auth["prefix"]]
+ progtrack.refresh_progress(pub.prefix)
+
+ full_refresh_this_pub = False
+
+ if pub.prefix in self.catalogs:
+ cat = self.catalogs[pub.prefix]
ts = cat.last_modified()
# Although we may have a catalog with a
# timestamp, the user may have changed the
- # origin URL for the authority. If this has
+ # origin URL for the publisher. If this has
# occurred, we need to perform a full refresh.
- if cat.origin() != auth["origin"]:
- full_refresh_this_auth = True
+ if cat.origin() != pub["origin"]:
+ full_refresh_this_pub = True
if ts and not full_refresh and \
- not full_refresh_this_auth:
+ not full_refresh_this_pub:
hdr = {'If-Modified-Since': ts}
else:
hdr = {}
try:
- self._do_get_catalog(auth, hdr, ts)
+ self._do_get_catalog(pub, hdr, ts)
except retrieve.CatalogRetrievalError, e:
- failed.append((auth, e))
+ failed.append((pub, e))
except TransportFailures, e:
- failed.append((auth, e))
+ failed.append((pub, e))
else:
succeeded += 1
- self.cache_catalogs(auths)
+ self.cache_catalogs(pubs)
self.update_installed_pkgs()
if progtrack:
@@ -1664,33 +1598,33 @@
raise api_errors.CatalogRefreshException(failed, total,
succeeded)
- CATALOG_CACHE_VERSION = 1
-
- def cache_catalogs(self, auths=None):
+ CATALOG_CACHE_VERSION = 2
+
+ def cache_catalogs(self, pubs=None):
"""Read in all the catalogs and cache the data."""
cache = {}
- authlist = []
+ publist = []
try:
- authlist = list(self.gen_authorities())
+ publist = list(self.gen_publishers())
except CfgCacheError:
- # No authorities defined. If the caller hasn't
- # supplied authorities to cache, raise the error
- if not auths:
+ # No publishers defined. If the caller hasn't
+ # supplied publishers to cache, raise the error
+ if not pubs:
raise
- if auths:
- # If caller passed authorities, include this in
- # the list of authorities to cache.
- authlist.extend(auths)
-
- for auth in authlist:
- croot = "%s/catalog/%s" % (self.imgdir, auth["prefix"])
+ if pubs:
+ # If caller passed publishers, include this in
+ # the list of publishers to cache.
+ publist.extend(pubs)
+
+ for pub in publist:
+ croot = "%s/catalog/%s" % (self.imgdir, pub.prefix)
# XXX Should I be removing pkg_names.pkl now that we're
# not using it anymore?
try:
catalog.Catalog.read_catalog(cache,
- croot, auth = auth["prefix"])
+ croot, pub = pub.prefix)
except EnvironmentError, e:
# If a catalog file is just missing, ignore it.
# If there's a worse error, make sure the user
@@ -1744,35 +1678,36 @@
try:
version, self._catalog = \
cPickle.load(file(cache_file, "rb"))
- except (cPickle.PickleError, EnvironmentError, EOFError):
+ except (cPickle.PickleError, EnvironmentError,
+ EOFError):
self._catalog = {}
self._catalog_cache_mod_time = None
raise RuntimeError
self._catalog_cache_mod_time = mod_time
-
+
# If we don't recognize the version, complain.
if version != self.CATALOG_CACHE_VERSION:
raise RuntimeError
def load_catalogs(self, progresstracker):
- for auth in self.gen_authorities():
- croot = "%s/catalog/%s" % (self.imgdir, auth["prefix"])
- progresstracker.catalog_start(auth["prefix"])
- if auth["prefix"] == self.cfg_cache.preferred_authority:
- authpfx = "%s_%s" % (pkg.fmri.PREF_AUTH_PFX,
- auth["prefix"])
+ for pub in self.gen_publishers():
+ croot = "%s/catalog/%s" % (self.imgdir, pub.prefix)
+ progresstracker.catalog_start(pub.prefix)
+ if pub.prefix == self.cfg_cache.preferred_publisher:
+ pubpfx = "%s_%s" % (pkg.fmri.PREF_PUB_PFX,
+ pub.prefix)
c = catalog.Catalog(croot,
- authority=authpfx)
+ publisher=pubpfx)
else:
c = catalog.Catalog(croot,
- authority = auth["prefix"])
- self.catalogs[auth["prefix"]] = c
+ publisher=pub.prefix)
+ self.catalogs[pub.prefix] = c
progresstracker.catalog_done()
# Try to load the catalog cache file. If that fails, load the
# data from the canonical text copies of the catalogs from each
- # authority. Try to save it, to spare the time in the future.
+ # publisher. Try to save it, to spare the time in the future.
# XXX Given that this is a read operation, should we be writing?
try:
self.load_catalog_cache()
@@ -1785,12 +1720,12 @@
for state, f in self.pkg_states.values():
if state != PKG_STATE_INSTALLED:
continue
- auth, name, vers = f.tuple()
+ pub, name, vers = f.tuple()
if name not in self._catalog or \
vers not in self._catalog[name]["versions"]:
catalog.Catalog.cache_fmri(self._catalog, f,
- f.get_authority())
+ f.get_publisher())
def destroy_catalog_cache(self):
pickle_file = os.path.join(self.imgdir, "catalog/catalog.pkl")
@@ -1800,10 +1735,14 @@
if e.errno != errno.ENOENT:
raise
- def destroy_catalog(self, auth_name):
+ def has_catalog(self, prefix):
+ return os.path.exists(os.path.join(self.imgdir, "catalog",
+ prefix, "catalog"))
+
+ def destroy_catalog(self, prefix):
try:
shutil.rmtree("%s/catalog/%s" %
- (self.imgdir, auth_name))
+ (self.imgdir, prefix))
except OSError, e:
if e.errno not in (errno.ENOENT, errno.ESRCH):
raise
@@ -1820,7 +1759,7 @@
if cfmri.is_same_pkg(pfmri):
return True
- # Get the catalog for the correct authority
+ # Get the catalog for the correct publisher
cat = self.get_catalog(cfmri)
return cat.rename_is_same_pkg(cfmri, pfmri)
@@ -1829,12 +1768,12 @@
"""Since the catalog keeps track of renames, it's no longer
sufficient to rely on the FMRI class to determine whether a
package is a successor. This routine takes two FMRIs, and
- if they have the same authority, checks if they've been
+ if they have the same publisher, checks if they've been
renamed. If a rename has occurred, this runs the is_successor
routine from the catalog. Otherwise, this runs the standard
fmri.is_successor() code."""
- # Get the catalog for the correct authority
+ # Get the catalog for the correct publisher
cat = self.get_catalog(cfmri)
# If the catalog has a rename record that names fmri as a
@@ -1885,7 +1824,7 @@
"""Build up the package state dictionary.
This dictionary maps the full fmri string to a tuple of the
- state, the prefix of the authority from which it's installed,
+ state, the prefix of the publisher from which it's installed,
and the fmri object.
Note that this dictionary only maps installed packages. Use
@@ -1910,8 +1849,8 @@
fmristr = urllib.unquote(pl)
tmpf = pkg.fmri.PkgFmri(fmristr)
path = self._install_file(tmpf)
- auth = self.installed_file_authority(path)
- f = pkg.fmri.PkgFmri(fmristr, authority = auth)
+ pub = self.installed_file_publisher(path)
+ f = pkg.fmri.PkgFmri(fmristr, publisher = pub)
self.pkg_states[fmristr] = \
(PKG_STATE_INSTALLED, f)
@@ -1928,8 +1867,8 @@
continue
fmristr = urllib.unquote("%s@%s" % (pd, vd))
- auth = self.installed_file_authority(path)
- f = pkg.fmri.PkgFmri(fmristr, authority = auth)
+ pub = self.installed_file_publisher(path)
+ f = pkg.fmri.PkgFmri(fmristr, publisher = pub)
self.pkg_states[fmristr] = \
(PKG_STATE_INSTALLED, f)
@@ -1942,7 +1881,8 @@
return pkg.fmri.PkgFmri(myfmri, self.attrs["Build-Release"])
def strtomatchingfmri(self, myfmri):
- return pkg.fmri.MatchingPkgFmri(myfmri, self.attrs["Build-Release"])
+ return pkg.fmri.MatchingPkgFmri(myfmri,
+ self.attrs["Build-Release"])
def load_constraints(self, progtrack):
"""Load constraints for all install pkgs"""
@@ -1951,16 +1891,17 @@
# skip loading if already done
if self.constraints.start_loading(fmri):
mfst = self.get_manifest(fmri)
- for dep in mfst.gen_actions_by_type("depend",
+ for dep in mfst.gen_actions_by_type("depend",
self.list_excludes()):
progtrack.evaluate_progress()
- f, constraint = dep.parse(self, fmri.get_name())
- self.constraints.update_constraints(constraint)
+ f, con = dep.parse(self,
+ fmri.get_name())
+ self.constraints.update_constraints(con)
self.constraints.finish_loading(fmri)
def get_installed_unbound_inc_list(self):
- """Returns list of packages containing incorporation dependencies
- on which no other pkgs depend."""
+ """Returns list of packages containing incorporation
+ dependencies on which no other pkgs depend."""
inc_tuples = []
dependents = set()
@@ -1968,19 +1909,20 @@
for fmri in self.gen_installed_pkgs():
fmri_name = fmri.get_pkg_stem()
mfst = self.get_manifest(fmri)
- for dep in mfst.gen_actions_by_type("depend", self.list_excludes()):
+ for dep in mfst.gen_actions_by_type("depend",
+ self.list_excludes()):
con_fmri = dep.get_constrained_fmri(self)
if con_fmri:
con_name = con_fmri.get_pkg_stem()
dependents.add(con_name)
inc_tuples.append((fmri_name, con_name))
- # remove those incorporations which are depended on by other
+ # remove those incorporations which are depended on by other
# incorporations.
deletions = 0
for i, a in enumerate(inc_tuples[:]):
if a[0] in dependents:
del inc_tuples[i - deletions]
-
+
return list(set([ a[0] for a in inc_tuples ]))
def get_user_by_name(self, name):
@@ -2017,17 +1959,17 @@
"""Applies a matcher to a name across a list of patterns.
Returns all tuples of patterns which match the name. Each tuple
contains the index into the original list, the pattern itself,
- the package version, the authority, and the raw authority
+ the package version, the publisher, and the raw publisher
string."""
return [
(i, pat, pat.tuple()[2],
- pat.get_authority(), pat.get_authority_str())
+ pat.get_publisher(), pat.get_publisher_str())
for i, pat in enumerate(patterns)
if matcher(name, pat.tuple()[1])
]
- def __inventory(self, patterns = None, all_known = False, matcher = None,
- constraint = pkg.version.CONSTRAINT_AUTO):
+ def __inventory(self, patterns=None, all_known=False, matcher=None,
+ constraint=pkg.version.CONSTRAINT_AUTO):
"""Private method providing the back-end for inventory()."""
if not matcher:
@@ -2048,8 +1990,8 @@
if "*" in pat or "?" in pat:
matcher = pkg.fmri.glob_match
patterns[i] = \
- pkg.fmri.MatchingPkgFmri(pat,
- "5.11")
+ pkg.fmri.MatchingPkgFmri(
+ pat, "5.11")
else:
patterns[i] = \
pkg.fmri.PkgFmri(pat,
@@ -2060,13 +2002,13 @@
if illegals:
raise api_errors.InventoryException(illegal=illegals)
- pauth = self.cfg_cache.preferred_authority
+ ppub = self.cfg_cache.preferred_publisher
# matchingpats is the set of all the patterns which matched a
# package in the catalog. This allows us to return partial
# failure if some patterns match and some don't.
# XXX It would be nice to keep track of why some patterns failed
- # to match -- based on name, version, or authority.
+ # to match -- based on name, version, or publisher.
matchingpats = set()
# XXX Perhaps we shouldn't sort here, but in the caller, to save
@@ -2107,42 +2049,42 @@
continue
# Like the version skipping above, do the same
- # for authorities.
- authlist = set(self._catalog[name][str(ver)][1])
+ # for publishers.
+ publist = set(self._catalog[name][str(ver)][1])
nomatch = []
for i, match in enumerate(vmatches):
if match[3] and \
- match[3] not in authlist:
+ match[3] not in publist:
nomatch.append(i)
- amatches = [
+ pmatches = [
vmatches[i]
for i, match in enumerate(vmatches)
if i not in nomatch
]
- if vmatches and not amatches:
+ if vmatches and not pmatches:
continue
# If no patterns were specified or any still-
- # matching pattern specified no authority, we
- # use the entire authlist for this version.
- # Otherwise, we use the intersection of authlist
- # and the auths in the patterns.
- aset = set(i[3] for i in amatches)
+ # matching pattern specified no publisher, we
+ # use the entire publist for this version.
+ # Otherwise, we use the intersection of publist
+ # and the pubs in the patterns.
+ aset = set(i[3] for i in pmatches)
if aset and None not in aset:
- authlist = set(
+ publist = set(
m[3:5]
- for m in amatches
- if m[3] in authlist
+ for m in pmatches
+ if m[3] in publist
)
else:
- authlist = zip(authlist, authlist)
+ publist = zip(publist, publist)
pfmri = self._catalog[name][str(ver)][0]
inst_state = self.get_pkg_state_by_fmri(pfmri)
- inst_auth = self.get_pkg_auth_by_fmri(pfmri)
+ inst_pub = self.get_pkg_pub_by_fmri(pfmri)
state = {
"upgradable": ver != newest,
"frozen": False,
@@ -2151,19 +2093,19 @@
}
# We yield copies of the fmri objects in the
- # catalog because we add the authorities in, and
+ # catalog because we add the publishers in, and
# don't want to mess up the canonical catalog.
- # If a pattern had specified an authority as
+ # If a pattern had specified a publisher as
# preferred, be sure to emit an fmri that way,
# too.
yielded = False
if all_known:
- for auth, rauth in authlist:
+ for pub, rpub in publist:
nfmri = pfmri.copy()
- nfmri.set_authority(rauth,
- auth == pauth)
+ nfmri.set_publisher(rpub,
+ pub == ppub)
st = state.copy()
- if auth == inst_auth:
+ if pub == inst_pub:
st["state"] = \
PKG_STATE_INSTALLED
else:
@@ -2173,19 +2115,21 @@
yielded = True
elif inst_state == PKG_STATE_INSTALLED:
nfmri = pfmri.copy()
- nfmri.set_authority(inst_auth,
- inst_auth == pauth)
+ nfmri.set_publisher(inst_pub,
+ inst_pub == ppub)
state["state"] = inst_state
yield nfmri, state
yielded = True
if yielded:
- matchingpats |= set(i[:2] for i in amatches)
+ matchingpats |= set(
+ i[:2] for i in pmatches)
nonmatchingpats = [
opatterns[i]
for i, f in set(enumerate(patterns)) - matchingpats
]
+
if nonmatchingpats:
raise api_errors.InventoryException(
notfound=nonmatchingpats)
@@ -2213,14 +2157,14 @@
# "preferred" and "first_only" are private arguments that are
# currently only used in evaluate_fmri(), but could be made more
# generally useful. "preferred" ensures that all potential
- # matches from the preferred authority are generated before
- # those from non-preferred authorities. In the current
+ # matches from the preferred publisher are generated before
+ # those from non-preferred publishers. In the current
# implementation, this consumes more memory. "first_only"
# signals us to return only the first match, which allows us to
# save all the memory that "preferred" currently eats up.
preferred = kwargs.pop("preferred", False)
first_only = kwargs.pop("first_only", False)
- pauth = self.cfg_cache.preferred_authority
+ ppub = self.cfg_cache.preferred_publisher
if not preferred:
for f in self.__inventory(*args, **kwargs):
@@ -2229,7 +2173,7 @@
nplist = []
firstnp = None
for f in self.__inventory(*args, **kwargs):
- if f[0].get_authority() == pauth:
+ if f[0].get_publisher() == ppub:
yield f
if first_only:
return
@@ -2289,28 +2233,40 @@
failed = []
if not servers:
- servers = self.gen_authorities()
-
- for auth in servers:
- ssl_tuple = self.get_ssl_credentials(
- authority = auth.get("prefix", None),
- origin = auth["origin"])
- try:
- uuid = self.get_uuid(auth["prefix"])
- except KeyError:
- uuid = None
+ servers = self.gen_publishers()
+
+ for pub in servers:
+ if not isinstance(pub, publisher.Publisher):
+ origin = pub["origin"]
+ try:
+ pub = self.get_publisher(
+ origin=origin)
+ except api_errors.UnknownPublisher:
+ pass
+ else:
+ repo = pub.selected_repository
+ origin = repo.get_origin(origin)
+ else:
+ origin = pub.selected_repository.origins[0]
+
+ uuid = None
+ ssl_tuple = (None, None)
+ if isinstance(origin, publisher.RepositoryURI):
+ ssl_tuple = (origin.ssl_key, origin.ssl_cert)
+ uuid = self.get_uuid(pub.prefix)
+ origin = origin.uri
try:
- res, v = versioned_urlopen(auth["origin"],
- "search", [0], urllib.quote(args[0], ""),
+ res, v = versioned_urlopen(origin, "search",
+ [0], urllib.quote(args[0], ""),
ssl_creds=ssl_tuple, imgtype=self.type,
uuid=uuid)
except urllib2.HTTPError, e:
if e.code != httplib.NOT_FOUND:
- failed.append((auth, e))
+ failed.append((pub, e))
continue
except urllib2.URLError, e:
- failed.append((auth, e))
+ failed.append((pub, e))
continue
try:
@@ -2322,7 +2278,7 @@
else:
yield fields[:4]
except socket.timeout, e:
- failed.append((auth, e))
+ failed.append((pub, e))
continue
if failed:
@@ -2360,9 +2316,9 @@
shutil.rmtree(self.dl_cache_dir, True)
def salvagedir(self, path):
- """Called when directory contains something and it's not supposed
- to because it's being deleted. XXX Need to work out a better error
- passback mechanism. Path is rooted in /...."""
+ """Called when directory contains something and it's not
+ supposed to because it's being deleted. XXX Need to work out a
+ better error passback mechanism. Path is rooted in /...."""
salvagedir = os.path.normpath(
os.path.join(self.imgdir, "lost+found",
@@ -2371,13 +2327,15 @@
parent = os.path.dirname(salvagedir)
if not os.path.exists(parent):
os.makedirs(parent)
- shutil.move(os.path.normpath(os.path.join(self.root, path)), salvagedir)
+ shutil.move(os.path.normpath(os.path.join(self.root, path)),
+ salvagedir)
# XXX need a better way to do this.
emsg("\nWarning - directory %s not empty - contents preserved "
"in %s" % (path, salvagedir))
def temporary_file(self):
- """ create a temp file under image directory for various purposes"""
+ """create a temp file under image directory for various
+ purposes"""
tempdir = os.path.normpath(os.path.join(self.imgdir, "tmp"))
if not os.path.exists(tempdir):
os.makedirs(tempdir)
@@ -2385,7 +2343,8 @@
os.close(fd)
return name
- def expanddirs(self, dirs):
+ @staticmethod
+ def expanddirs(dirs):
"""given a set of directories, return expanded set that includes
all components"""
out = set()
@@ -2403,16 +2362,16 @@
to assemble an appropriate image plan. This is a helper
routine for some common operations in the client.
- This method checks all authorities for a package match;
- however, it defaults to choosing the preferred authority
+ This method checks all publishers for a package match;
+ however, it defaults to choosing the preferred publisher
when an ambiguous package name is specified. If the user
- wishes to install a package from a non-preferred authority,
- the full FMRI that contains an authority should be used
+ wishes to install a package from a non-preferred publisher,
+ the full FMRI that contains a publisher should be used
to name the package."""
if filters is None:
filters = []
-
+
error = 0
ip = imageplan.ImagePlan(self, progtrack, check_cancelation,
filters=filters, noexecute=noexecute)
@@ -2429,7 +2388,7 @@
# done first
inc_list = self.get_installed_unbound_inc_list()
-
+
head = []
tail = []
@@ -2449,19 +2408,22 @@
for p in pkg_list:
progtrack.evaluate_progress()
try:
- conp = pkg.fmri.PkgFmri(p, self.attrs["Build-Release"])
+ conp = pkg.fmri.PkgFmri(p,
+ self.attrs["Build-Release"])
except pkg.fmri.IllegalFmri:
illegal_fmris.append(p)
error = 1
continue
try:
- conp = self.constraints.apply_constraints_to_fmri(conp)
+ conp = \
+ self.constraints.apply_constraints_to_fmri(
+ conp)
except constraint.ConstraintException, e:
error = 1
- constraint_violations.extend(str(e).split("\n"))
+ constraint_violations.extend(str(e).split("\n"))
continue
try:
- matches = list(self.inventory([ conp ],
+ matches = list(self.inventory([conp],
all_known = True))
except api_errors.InventoryException, e:
assert(not (e.notfound and e.illegal))
@@ -2478,7 +2440,7 @@
npnames = {}
npmatch = []
for m, state in matches:
- if m.preferred_authority():
+ if m.preferred_publisher():
pnames[m.get_pkg_stem()] = 1
pmatch.append(m)
else:
@@ -2503,7 +2465,7 @@
if error != 0:
raise api_errors.PlanCreationException(unfound_fmris,
- multiple_matches, [], illegal_fmris,
+ multiple_matches, [], illegal_fmris,
constraint_violations=constraint_violations)
if verbose:
@@ -2599,7 +2561,7 @@
#
# This routine makes the distinction between the "target image",
# which will be altered, and the "running image", which is
- # to say whatever image appears to contain the version of the
+ # to say whatever image appears to contain the version of the
# pkg command we're running.
#
@@ -2621,7 +2583,7 @@
progtrack = progress.QuietProgressTracker()
img = self
-
+
if not img.is_liveroot():
newimg = Image()
cmdpath = os.path.join(os.getcwd(), actual_cmd)