manifest.py revision 2843
1516N/A#!/usr/bin/python
39N/A#
39N/A# CDDL HEADER START
39N/A#
39N/A# The contents of this file are subject to the terms of the
39N/A# Common Development and Distribution License (the "License").
39N/A# You may not use this file except in compliance with the License.
39N/A#
39N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
39N/A# or http://www.opensolaris.org/os/licensing.
39N/A# See the License for the specific language governing permissions
39N/A# and limitations under the License.
39N/A#
39N/A# When distributing Covered Code, include this CDDL HEADER in each
39N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
39N/A# If applicable, add the following below this CDDL HEADER, with the
39N/A# fields enclosed by brackets "[]" replaced with your own identifying
39N/A# information: Portions Copyright [yyyy] [name of copyright owner]
39N/A#
39N/A# CDDL HEADER END
39N/A#
926N/A
926N/A#
2854N/A# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
926N/A#
39N/A
2453N/Afrom collections import namedtuple, defaultdict
342N/Aimport errno
1516N/Aimport hashlib
1636N/Aimport os
1386N/Aimport tempfile
2910N/Afrom itertools import groupby, chain, repeat, izip
2639N/Afrom operator import itemgetter
39N/A
51N/Aimport pkg.actions as actions
2073N/Aimport pkg.client.api_errors as apx
2910N/Aimport pkg.misc as misc
2144N/Aimport pkg.portable as portable
1066N/Aimport pkg.variant as variant
1231N/Aimport pkg.version as version
2453N/A
1352N/Afrom pkg.misc import EmptyDict, EmptyI, expanddirs, PKG_FILE_MODE, PKG_DIR_MODE
1890N/Afrom pkg.actions.attribute import AttributeAction
296N/A
2876N/Aclass ManifestDifference(
39N/A namedtuple("ManifestDifference", "added changed removed")):
2690N/A
2690N/A __slots__ = []
2690N/A
2690N/A __state__desc = tuple([
2690N/A [ ( actions.generic.NSG, actions.generic.NSG ) ],
2690N/A [ ( actions.generic.NSG, actions.generic.NSG ) ],
2690N/A [ ( actions.generic.NSG, actions.generic.NSG ) ],
2690N/A ])
2690N/A
2690N/A __state__commonize = frozenset([
2690N/A actions.generic.NSG,
2690N/A ])
2690N/A
2690N/A @staticmethod
2690N/A def getstate(obj, je_state=None):
2690N/A """Returns the serialized state of this object in a format
2690N/A that that can be easily stored using JSON, pickle, etc."""
2690N/A return misc.json_encode(ManifestDifference.__name__,
2690N/A tuple(obj),
2690N/A ManifestDifference.__state__desc,
2690N/A commonize=ManifestDifference.__state__commonize,
2690N/A je_state=je_state)
2690N/A
2690N/A @staticmethod
2690N/A def fromstate(state, jd_state=None):
2690N/A """Allocate a new object using previously serialized state
2690N/A obtained via getstate()."""
2690N/A
2690N/A # decode serialized state into python objects
2690N/A state = misc.json_decode(ManifestDifference.__name__,
2690N/A state,
2690N/A ManifestDifference.__state__desc,
2690N/A commonize=ManifestDifference.__state__commonize,
2690N/A jd_state=jd_state)
2690N/A
2690N/A return ManifestDifference(*state)
2690N/A
2690N/Aclass Manifest(object):
1713N/A """A Manifest is the representation of the actions composing a specific
39N/A package version on both the client and the repository. Both purposes
39N/A utilize the same storage format.
39N/A
39N/A The serialized structure of a manifest is an unordered list of actions.
39N/A
205N/A The special action, "set", represents a package attribute.
39N/A
205N/A The reserved attribute, "fmri", represents the package and version
39N/A described by this manifest. It is available as a string via the
39N/A attributes dictionary, and as an FMRI object from the fmri member.
39N/A
39N/A The list of manifest-wide reserved attributes is
39N/A
39N/A base_directory Default base directory, for non-user images.
39N/A fmri Package FMRI.
39N/A isa Package is intended for a list of ISAs.
39N/A platform Package is intended for a list of platforms.
39N/A relocatable Suitable for User Image.
39N/A
39N/A All non-prefixed attributes are reserved to the framework. Third
39N/A parties may prefix their attributes with a reversed domain name, domain
39N/A name, or stock symbol. An example might be
39N/A
39N/A com.example,supported
39N/A
39N/A as an indicator that a specific package version is supported by the
39N/A vendor, example.com.
39N/A
39N/A manifest.null is provided as the null manifest. Differences against the
39N/A null manifest result in the complete set of attributes and actions of
48N/A the non-null manifest, meaning that all operations can be viewed as
48N/A tranitions between the manifest being installed and the manifest already
48N/A present in the image (which may be the null manifest).
48N/A """
48N/A
59N/A def __init__(self, pfmri=None):
48N/A self.fmri = pfmri
2073N/A
2073N/A self._cache = {}
39N/A self._facets = None # facets seen in package
2639N/A self._variants = None # variants seen in package
2910N/A self.actions = []
39N/A self.actions_bytype = {}
567N/A self.attributes = {} # package-wide attributes
838N/A self.signatures = EmptyDict
1890N/A self.excludes = EmptyI
2608N/A
39N/A def __str__(self):
39N/A r = ""
39N/A if "pkg.fmri" not in self.attributes and self.fmri != None:
1431N/A r += "set name=pkg.fmri value=%s\n" % self.fmri
1431N/A
39N/A for act in sorted(self.actions):
227N/A r += "%s\n" % act
315N/A return r
315N/A
39N/A def as_lines(self):
1352N/A """A generator function that returns the unsorted manifest
1352N/A contents as lines of text."""
1352N/A
1352N/A if "pkg.fmri" not in self.attributes and self.fmri != None:
1431N/A yield "set name=pkg.fmri value=%s\n" % self.fmri
1431N/A
429N/A for act in self.actions:
315N/A yield "%s\n" % act
1352N/A
429N/A def tostr_unsorted(self):
1352N/A return "".join((l for l in self.as_lines()))
1352N/A
39N/A def difference(self, origin, origin_exclude=EmptyI,
926N/A self_exclude=EmptyI):
926N/A """Return three lists of action pairs representing origin and
203N/A destination actions. The first list contains the pairs
203N/A representing additions, the second list contains the pairs
203N/A representing updates, and the third list contains the pairs
203N/A representing removals. All three lists are in the order in
1045N/A which they should be executed."""
1045N/A # XXX Do we need to find some way to assert that the keys are
72N/A # all unique?
72N/A
59N/A if isinstance(origin, EmptyFactoredManifest):
2054N/A # No origin was provided, so nothing has been changed or
1045N/A # removed; only added. In addition, this doesn't need
1045N/A # to be sorted since the caller likely already does
1045N/A # (such as pkgplan/imageplan).
1045N/A return ManifestDifference(
1713N/A [(None, a) for a in self.gen_actions(self_exclude)],
1045N/A [], [])
1045N/A
1045N/A def hashify(v):
2084N/A """handle key values that may be lists"""
2084N/A if type(v) is not list:
2639N/A return v
2084N/A return frozenset(v)
2854N/A
2084N/A def dictify(mf, excludes):
2842N/A # Transform list of actions into a dictionary keyed by
2842N/A # action key attribute, key attribute and mediator, or
2842N/A # id if there is no key attribute.
2842N/A for a in mf.gen_actions(excludes):
2842N/A if (a.name == "link" or
2842N/A a.name == "hardlink") and \
2842N/A a.attrs.get("mediator"):
2842N/A akey = (a.name, frozenset([
2854N/A a.key_attr,
2854N/A a.attrs.get("mediator-version"),
2842N/A a.attrs.get("mediator-implementation")
2842N/A ]))
2842N/A else:
2842N/A akey = (a.name, hashify(a.attrs.get(
2842N/A a.key_attr, id(a))))
2842N/A yield (akey, a)
2842N/A
2842N/A sdict = dict(dictify(self, self_exclude))
2842N/A odict = dict(dictify(origin, origin_exclude))
2842N/A
59N/A sset = set(sdict.iterkeys())
2639N/A oset = set(odict.iterkeys())
2639N/A
59N/A added = [(None, sdict[i]) for i in sset - oset]
72N/A removed = [(odict[i], None) for i in oset - sset]
72N/A changed = [
72N/A (odict[i], sdict[i])
72N/A for i in oset & sset
72N/A if odict[i].different(sdict[i])
2144N/A ]
72N/A
59N/A # XXX Do changed actions need to be sorted at all? This is
72N/A # likely to be the largest list, so we might save significant
72N/A # time by not sorting. Should we sort above? Insert into a
72N/A # sorted list?
72N/A
59N/A # singlesort = lambda x: x[0] or x[1]
72N/A addsort = itemgetter(1)
2639N/A remsort = itemgetter(0)
2639N/A removed.sort(key=remsort, reverse=True)
2639N/A added.sort(key=addsort)
2639N/A changed.sort(key=addsort)
2639N/A
59N/A return ManifestDifference(added, changed, removed)
1713N/A
59N/A @staticmethod
838N/A def comm(compare_m):
2240N/A """Like the unix utility comm, except that this function
838N/A takes an arbitrary number of manifests and compares them,
838N/A returning a tuple consisting of each manifest's actions
838N/A that are not the same for all manifests, followed by a
926N/A list of actions that are the same in each manifest."""
838N/A
838N/A # Must specify at least one manifest.
2240N/A assert compare_m
2240N/A dups = []
2284N/A
2240N/A # construct list of dictionaries of actions in each
838N/A # manifest, indexed by unique key and variant combination
2240N/A m_dicts = []
2240N/A for m in compare_m:
2240N/A m_dict = {}
2240N/A for a in m.gen_actions():
2240N/A # The unique key for each action is based on its
2240N/A # type, key attribute, and unique variants set
2240N/A # on the action.
2240N/A try:
2240N/A key = set(a.attrlist(a.key_attr))
2317N/A key.update(
2240N/A "%s=%s" % (v, a.attrs[v])
2240N/A for v in a.get_varcet_keys()[0]
2240N/A )
2240N/A key = tuple(key)
2240N/A except KeyError:
2240N/A # If there is no key attribute for the
2240N/A # action, then fallback to the object
2240N/A # id for the action as its identifier.
2240N/A key = (id(a),)
2240N/A
2240N/A # catch duplicate actions here...
2284N/A if m_dict.setdefault((a.name, key), a) != a:
2284N/A dups.append((m_dict[(a.name, key)], a))
2284N/A
2284N/A m_dicts.append(m_dict)
2240N/A
2240N/A if dups:
2284N/A raise ManifestError(duplicates=dups)
2284N/A
2284N/A # construct list of key sets in each dict
838N/A m_sets = [
838N/A set(m.keys())
838N/A for m in m_dicts
838N/A ]
838N/A
838N/A common_keys = reduce(lambda a, b: a & b, m_sets)
838N/A
926N/A # determine which common_keys have common actions
838N/A for k in common_keys.copy():
838N/A for i in range(len(m_dicts) - 1):
838N/A if m_dicts[i][k].different(
838N/A m_dicts[i + 1][k]):
838N/A common_keys.remove(k)
838N/A break
838N/A return tuple(
845N/A [
845N/A [m_dicts[i][k] for k in m_sets[i] - common_keys]
926N/A for i in range(len(m_dicts))
838N/A ]
845N/A +
845N/A [
845N/A [ m_dicts[0][k] for k in common_keys ]
838N/A ]
845N/A )
845N/A
838N/A def combined_difference(self, origin, ov=EmptyI, sv=EmptyI):
926N/A """Where difference() returns three lists, combined_difference()
203N/A returns a single list of the concatenation of the three."""
315N/A return list(chain(*self.difference(origin, ov, sv)))
838N/A
203N/A def humanized_differences(self, other, ov=EmptyI, sv=EmptyI):
838N/A """Output expects that self is newer than other. Use of sets
48N/A requires that we convert the action objects into some marshalled
48N/A form, otherwise set member identities are derived from the
48N/A object pointers, rather than the contents."""
48N/A
48N/A l = self.difference(other, ov, sv)
838N/A out = ""
203N/A
48N/A for src, dest in chain(*l):
203N/A if not src:
72N/A out += "+ %s\n" % str(dest)
181N/A elif not dest:
72N/A out += "- %s\n" + str(src)
181N/A else:
46N/A out += "%s -> %s\n" % (src, dest)
181N/A return out
237N/A
46N/A def _gen_dirs_to_str(self):
2453N/A """Generate contents of dircache file containing all dirctories
2453N/A referenced explicitly or implicitly from self.actions. Include
2453N/A variants as values; collapse variants where possible."""
2453N/A
2339N/A def gen_references(a):
2453N/A for d in expanddirs(a.directory_references()):
2453N/A yield d
2453N/A
2453N/A dirs = self._actions_to_dict(gen_references)
2453N/A for d in dirs:
2339N/A for v in dirs[d]:
2339N/A yield "dir path=%s %s\n" % \
2876N/A (d, " ".join("%s=%s" % t \
2876N/A for t in v.iteritems()))
2339N/A
2453N/A def _gen_mediators_to_str(self):
2453N/A """Generate contents of mediatorcache file containing all
2453N/A mediators referenced explicitly or implicitly from self.actions.
2453N/A Include variants as values; collapse variants where possible."""
2453N/A
2453N/A def gen_references(a):
2453N/A if (a.name == "link" or a.name == "hardlink") and \
2453N/A "mediator" in a.attrs:
2453N/A yield (a.attrs.get("mediator"),
2453N/A a.attrs.get("mediator-priority"),
2453N/A a.attrs.get("mediator-version"),
2453N/A a.attrs.get("mediator-implementation"))
2339N/A
2453N/A mediators = self._actions_to_dict(gen_references)
2453N/A for mediation, mvariants in mediators.iteritems():
2453N/A values = {
2453N/A "mediator-priority": mediation[1],
2453N/A "mediator-version": mediation[2],
2453N/A "mediator-implementation": mediation[3],
2453N/A }
2453N/A for mvariant in mvariants:
2453N/A a = "set name=pkg.mediator " \
2837N/A "value=%s %s %s\n" % (mediation[0],
2453N/A " ".join((
2453N/A "=".join(t)
2453N/A for t in values.iteritems()
2453N/A if t[1]
2453N/A )),
2453N/A " ".join((
2453N/A "=".join(t)
2453N/A for t in mvariant.iteritems()
2453N/A ))
2453N/A )
2453N/A yield a
2453N/A
2910N/A def _actions_to_dict(self, references):
2910N/A """create dictionary of all actions referenced explicitly or
2910N/A implicitly from self.actions... include variants as values;
2910N/A collapse variants where possible"""
2910N/A
2910N/A refs = {}
2910N/A # build a dictionary containing all directories tagged w/
2910N/A # variants
2910N/A for a in self.actions:
2910N/A v, f = a.get_varcet_keys()
2910N/A variants = dict((name, a.attrs[name]) for name in v + f)
2910N/A for ref in references(a):
2910N/A if ref not in refs:
2910N/A refs[ref] = [variants]
2910N/A elif variants not in refs[ref]:
2910N/A refs[ref].append(variants)
2910N/A
2910N/A # remove any tags if any entries are always delivered (NULL)
2910N/A for ref in refs:
2910N/A if {} in refs[ref]:
2910N/A refs[ref] = [{}]
2910N/A continue
2910N/A # could collapse refs where all variants are present
2910N/A # (the current logic only collapses them if at least
2910N/A # one reference is delivered without a facet or
2910N/A # variant)
2910N/A return refs
2910N/A
2910N/A def get_directories(self, excludes):
2910N/A """ return a list of directories implicitly or
2910N/A explicitly referenced by this object"""
2910N/A
2910N/A if self.excludes == excludes:
2910N/A excludes = EmptyI
2910N/A assert excludes == EmptyI or self.excludes == EmptyI
2910N/A try:
2910N/A alist = self._cache["manifest.dircache"]
2910N/A except KeyError:
2910N/A # generate actions that contain directories
2910N/A alist = self._cache["manifest.dircache"] = [
2910N/A actions.fromstr(s.rstrip())
2910N/A for s in self._gen_dirs_to_str()
2910N/A ]
2910N/A
2910N/A s = set([
2910N/A a.attrs["path"]
2910N/A for a in alist
2910N/A if not excludes or a.include_this(excludes)
2910N/A ])
2910N/A
2910N/A return list(s)
2910N/A
2910N/A def gen_mediators(self, excludes=EmptyI):
2910N/A """A generator function that yields tuples of the form (mediator,
2910N/A mediations) expressing the set of possible mediations for this
2910N/A package, where 'mediations' is a set() of possible mediations for
2910N/A the mediator. Each mediation is a tuple of the form (priority,
2910N/A version, implementation).
2910N/A """
2910N/A
2910N/A if self.excludes == excludes:
2910N/A excludes = EmptyI
2910N/A assert excludes == EmptyI or self.excludes == EmptyI
2910N/A try:
2910N/A alist = self._cache["manifest.mediatorcache"]
2910N/A except KeyError:
2910N/A # generate actions that contain mediators
2910N/A alist = self._cache["manifest.mediatorcache"] = [
2910N/A actions.fromstr(s.rstrip())
2910N/A for s in self._gen_mediators_to_str()
2910N/A ]
2910N/A
2910N/A ret = defaultdict(set)
2910N/A for attrs in (
2910N/A act.attrs
2910N/A for act in alist
2910N/A if not excludes or act.include_this(excludes)):
2910N/A med_ver = attrs.get("mediator-version")
2910N/A if med_ver:
2910N/A try:
2910N/A med_ver = version.Version(med_ver,
2910N/A "5.11")
2910N/A except version.VersionError:
2910N/A # Consider this mediation unavailable
2910N/A # if it can't be parsed for whatever
2910N/A # reason.
2910N/A continue
2910N/A
2910N/A ret[attrs["value"]].add((
2910N/A attrs.get("mediator-priority"),
2910N/A med_ver,
2910N/A attrs.get("mediator-implementation"),
2910N/A ))
2910N/A
2910N/A for m in ret:
2910N/A yield m, ret[m]
2910N/A
2910N/A def gen_actions(self, excludes=EmptyI):
2910N/A """Generate actions in manifest through ordered callable list"""
2910N/A
2910N/A if self.excludes == excludes:
2910N/A excludes = EmptyI
2910N/A assert excludes == EmptyI or self.excludes == EmptyI
2910N/A for a in self.actions:
2910N/A for c in excludes:
2910N/A if not c(a):
2910N/A break
2910N/A else:
2910N/A yield a
2910N/A
2910N/A def gen_actions_by_type(self, atype, excludes=EmptyI):
2910N/A """Generate actions in the manifest of type "type"
2910N/A through ordered callable list"""
2910N/A
2910N/A if self.excludes == excludes:
2910N/A excludes = EmptyI
2910N/A assert excludes == EmptyI or self.excludes == EmptyI
2910N/A for a in self.actions_bytype.get(atype, []):
2910N/A for c in excludes:
2910N/A if not c(a):
2910N/A break
2910N/A else:
2910N/A yield a
2910N/A
2910N/A def gen_actions_by_types(self, atypes, excludes=EmptyI):
2910N/A """Generate actions in the manifest of types "atypes"
2910N/A through ordered callable list."""
2910N/A
2910N/A for atype in atypes:
2910N/A for a in self.gen_actions_by_type(atype,
2910N/A excludes=excludes):
2910N/A yield a
2910N/A
2910N/A def gen_key_attribute_value_by_type(self, atype, excludes=EmptyI):
2910N/A """Generate the value of the key attribute for each action
2910N/A of type "type" in the manifest."""
2910N/A
2910N/A return (
2910N/A a.attrs.get(a.key_attr)
2910N/A for a in self.gen_actions_by_type(atype, excludes)
2910N/A )
2910N/A
2910N/A def duplicates(self, excludes=EmptyI):
2910N/A """Find actions in the manifest which are duplicates (i.e.,
2910N/A represent the same object) but which are not identical (i.e.,
2910N/A have all the same attributes)."""
2910N/A
2910N/A def fun(a):
2910N/A """Return a key on which actions can be sorted."""
2910N/A return a.name, a.attrs.get(a.key_attr, id(a))
2910N/A
2910N/A alldups = []
2910N/A acts = [a for a in self.gen_actions(excludes)]
2910N/A
2910N/A for k, g in groupby(sorted(acts, key=fun), fun):
2910N/A glist = list(g)
2910N/A dups = set()
2910N/A for i in range(len(glist) - 1):
2910N/A if glist[i].different(glist[i + 1]):
2910N/A dups.add(glist[i])
2910N/A dups.add(glist[i + 1])
2910N/A if dups:
2910N/A alldups.append((k, dups))
2910N/A return alldups
2910N/A
2910N/A def __content_to_actions(self, content):
2910N/A accumulate = ""
2910N/A lineno = 0
2910N/A errors = []
2910N/A
2910N/A if isinstance(content, basestring):
2910N/A # Get an iterable for the string.
2910N/A content = content.splitlines()
2910N/A
2910N/A for l in content:
2910N/A lineno += 1
2910N/A l = l.lstrip()
2910N/A if l.endswith("\\"): # allow continuation chars
2910N/A accumulate += l[0:-1] # elide backslash
2910N/A continue
2910N/A elif accumulate:
2910N/A l = accumulate + l
2910N/A accumulate = ""
2910N/A
2910N/A if not l or l[0] == "#": # ignore blank lines & comments
2910N/A continue
2910N/A
2910N/A try:
2910N/A yield actions.fromstr(l)
2910N/A except actions.ActionError, e:
2910N/A # Accumulate errors and continue so that as
2910N/A # much of the action data as possible can be
2910N/A # parsed.
2910N/A e.fmri = self.fmri
2910N/A e.lineno = lineno
2910N/A errors.append(e)
2910N/A
2910N/A if errors:
2910N/A raise apx.InvalidPackageErrors(errors)
2910N/A
2910N/A def set_content(self, content=None, excludes=EmptyI, pathname=None,
2910N/A signatures=False):
2910N/A """Populate the manifest with actions.
2910N/A
2910N/A 'content' is an optional value containing either the text
2910N/A representation of the manifest or an iterable of
2910N/A action objects.
2910N/A
2910N/A 'excludes' is optional. If provided it must be a length two
2910N/A list with the variants to be excluded as the first element and
2910N/A the facets to be excluded as the second element.
2910N/A
2910N/A 'pathname' is an optional filename containing the location of
2910N/A the manifest content.
2910N/A
2910N/A 'signatures' is an optional boolean value that indicates whether
2910N/A a manifest signature should be generated. This is only possible
2910N/A when 'content' is a string or 'pathname' is provided.
2910N/A """
2910N/A
2910N/A assert content is not None or pathname is not None
2910N/A assert not (content and pathname)
2910N/A
2910N/A self.actions = []
2910N/A self.actions_bytype = {}
2910N/A self._variants = None
2910N/A self._facets = None
2910N/A self.attributes = {}
2910N/A self._cache = {}
2910N/A
2910N/A # So we could build up here the type/key_attr dictionaries like
2910N/A # sdict and odict in difference() above, and have that be our
2910N/A # main datastore, rather than the simple list we have now. If
2910N/A # we do that here, we can even assert that the "same" action
2910N/A # can't be in a manifest twice. (The problem of having the same
2910N/A # action more than once in packages that can be installed
2910N/A # together has to be solved somewhere else, though.)
2910N/A if pathname:
2910N/A try:
2910N/A with open(pathname, "rb") as mfile:
2910N/A content = mfile.read()
2910N/A except EnvironmentError, e:
2910N/A raise apx._convert_error(e)
2910N/A if isinstance(content, basestring):
2910N/A if signatures:
2910N/A # Generate manifest signature based upon
2910N/A # input content, but only if signatures
2910N/A # were requested.
2910N/A self.signatures = {
2910N/A "sha-1": self.hash_create(content)
2910N/A }
2910N/A content = self.__content_to_actions(content)
2910N/A
2910N/A for action in content:
2453N/A self.add_action(action, excludes)
2453N/A self.excludes = excludes
2453N/A # Make sure that either no excludes were provided or that both
2453N/A # variants and facet excludes were.
2453N/A assert len(self.excludes) in (0, 2)
2453N/A
2910N/A def exclude_content(self, excludes):
2339N/A """Remove any actions from the manifest which should be
2339N/A excluded."""
2339N/A
2339N/A self.set_content(content=self.actions, excludes=excludes)
2453N/A
2453N/A def add_action(self, action, excludes):
2453N/A """Performs any needed transformations on the action then adds
2453N/A it to the manifest.
2453N/A
2339N/A The "action" parameter is the action object that should be
2453N/A added to the manifest.
2453N/A
2453N/A The "excludes" parameter is the variants to exclude from the
2453N/A manifest."""
2339N/A
2453N/A attrs = action.attrs
2453N/A aname = action.name
2453N/A
2453N/A # XXX handle legacy transition issues; not needed once support
2453N/A # for upgrading images from older releases (< build 151) has
2339N/A # been removed.
2339N/A if "opensolaris.zone" in attrs and \
2339N/A "variant.opensolaris.zone" not in attrs:
2339N/A attrs["variant.opensolaris.zone"] = \
2339N/A attrs["opensolaris.zone"]
2608N/A
2608N/A if aname == "set" and attrs["name"] == "authority":
2608N/A # Translate old action to new.
2339N/A attrs["name"] = "publisher"
2453N/A
2453N/A if excludes and not action.include_this(excludes):
2339N/A return
2453N/A
2639N/A if self._variants:
2540N/A # Reset facet/variant cache if needed (if one is set,
2339N/A # then both are set, so only need to check for one).
2339N/A self._facets = None
2339N/A self._variants = None
2339N/A
2339N/A self.actions.append(action)
2639N/A try:
2339N/A self.actions_bytype[aname].append(action)
2339N/A except KeyError:
2339N/A self.actions_bytype.setdefault(aname, []).append(action)
2339N/A
2910N/A # add any set actions to attributes
2910N/A if aname == "set":
2910N/A self.fill_attributes(action)
2910N/A
2910N/A def fill_attributes(self, action):
2910N/A """Fill attribute array w/ set action contents."""
2910N/A try:
2910N/A keyvalue = action.attrs["name"]
2910N/A if keyvalue == "fmri":
2910N/A keyvalue = "pkg.fmri"
2910N/A if keyvalue not in self.attributes:
2910N/A self.attributes[keyvalue] = \
2910N/A action.attrs["value"]
2910N/A except KeyError: # ignore broken set actions
2910N/A pass
2910N/A
2910N/A @staticmethod
2910N/A def search_dict(file_path, excludes, return_line=False,
2910N/A log=None):
2910N/A """Produces the search dictionary for a specific manifest.
2910N/A A dictionary is constructed which maps a tuple of token,
2910N/A action type, key, and the value that matched the token to
2910N/A the byte offset into the manifest file. file_path is the
2910N/A path to the manifest file. excludes is the variants which
2910N/A should be allowed in this image. return_line is a debugging
2910N/A flag which makes the function map the information to the
2910N/A string of the line, rather than the byte offset to allow
2910N/A easier debugging."""
2910N/A
2910N/A if log is None:
2910N/A log = lambda x: None
2910N/A
2910N/A try:
2910N/A file_handle = file(file_path, "rb")
2910N/A except EnvironmentError, e:
2910N/A if e.errno != errno.ENOENT:
2910N/A raise
2910N/A log((_("%(fp)s:\n%(e)s") %
2910N/A { "fp": file_path, "e": e }))
2910N/A return {}
2910N/A cur_pos = 0
2910N/A line = file_handle.readline()
2910N/A action_dict = {}
2910N/A def __handle_list(lst, cp):
2910N/A """Translates what actions.generate_indices produces
2910N/A into a dictionary mapping token, action_name, key, and
2910N/A the value that should be displayed for matching that
2910N/A token to byte offsets into the manifest file.
2910N/A
2910N/A The "lst" parameter is the data to be converted.
2910N/A
2910N/A The "cp" parameter is the byte offset into the file
2910N/A for the action which produced lst."""
2910N/A
2910N/A for action_name, subtype, tok, full_value in lst:
2910N/A if action_name == "set":
2910N/A if full_value is None:
2910N/A full_value = tok
2910N/A else:
2910N/A if full_value is None:
2910N/A full_value = subtype
2910N/A if full_value is None:
2910N/A full_value = action_name
2910N/A if isinstance(tok, list):
2910N/A __handle_list([
2910N/A (action_name, subtype, t,
2910N/A full_value)
2910N/A for t in tok
2910N/A ], cp)
2910N/A else:
2910N/A if (tok, action_name, subtype,
2910N/A full_value) in action_dict:
2910N/A action_dict[(tok, action_name,
2910N/A subtype, full_value)
2910N/A ].append(cp)
2910N/A else:
2910N/A action_dict[(tok, action_name,
2910N/A subtype, full_value)] = [cp]
2910N/A while line:
2910N/A l = line.strip()
2910N/A if l and l[0] != "#":
2910N/A try:
2910N/A action = actions.fromstr(l)
2910N/A except actions.ActionError, e:
2910N/A log((_("%(fp)s:\n%(e)s") %
2910N/A { "fp": file_path, "e": e }))
2910N/A else:
2910N/A if not excludes or \
2910N/A action.include_this(excludes):
2910N/A if action.attrs.has_key("path"):
2910N/A np = action.attrs["path"].lstrip(os.path.sep)
2910N/A action.attrs["path"] = \
2910N/A np
2910N/A try:
2910N/A inds = action.generate_indices()
2910N/A except KeyError, k:
2910N/A log(_("%(fp)s contains "
2910N/A "an action which is"
2910N/A " missing the "
2910N/A "expected attribute"
2910N/A ": %(at)s.\nThe "
2910N/A "action is:"
2910N/A "%(act)s") %
2910N/A {
2910N/A "fp": file_path,
2910N/A "at": k.args[0],
2910N/A "act":l
2910N/A })
2910N/A else:
2910N/A arg = cur_pos
2910N/A if return_line:
2453N/A arg = l
2453N/A __handle_list(inds, arg)
2453N/A cur_pos = file_handle.tell()
2453N/A line = file_handle.readline()
2453N/A file_handle.close()
2453N/A return action_dict
2453N/A
2453N/A @staticmethod
2608N/A def hash_create(mfstcontent):
2608N/A """This method takes a string representing the on-disk
2608N/A manifest content, and returns a hash value."""
2453N/A
2453N/A sha_1 = hashlib.sha1()
2453N/A if isinstance(mfstcontent, unicode):
2453N/A # Byte stream expected, so pass encoded.
2453N/A sha_1.update(mfstcontent.encode("utf-8"))
2639N/A else:
2540N/A sha_1.update(mfstcontent)
2453N/A
2453N/A return sha_1.hexdigest()
2453N/A
2453N/A def validate(self, signatures):
2453N/A """Verifies whether the signatures for the contents of
2453N/A the manifest match the specified signature data. Raises
2639N/A the 'BadManifestSignatures' exception on failure."""
2453N/A
2453N/A if signatures != self.signatures:
2453N/A raise apx.BadManifestSignatures(self.fmri)
2958N/A
2453N/A def store(self, mfst_path):
2453N/A """Store the manifest contents to disk."""
2453N/A
2453N/A t_dir = os.path.dirname(mfst_path)
2453N/A t_prefix = os.path.basename(mfst_path) + "."
2453N/A
2453N/A try:
2453N/A os.makedirs(t_dir, mode=PKG_DIR_MODE)
2453N/A except EnvironmentError, e:
2453N/A if e.errno == errno.EACCES:
2453N/A raise apx.PermissionsException(e.filename)
2453N/A if e.errno == errno.EROFS:
2453N/A raise apx.ReadOnlyFileSystemException(
2453N/A e.filename)
2453N/A if e.errno != errno.EEXIST:
838N/A raise
838N/A
2608N/A try:
2608N/A fd, fn = tempfile.mkstemp(dir=t_dir, prefix=t_prefix)
2608N/A except EnvironmentError, e:
2608N/A if e.errno == errno.EACCES:
838N/A raise apx.PermissionsException(e.filename)
838N/A if e.errno == errno.EROFS:
838N/A raise apx.ReadOnlyFileSystemException(
838N/A e.filename)
838N/A raise
838N/A
342N/A mfile = os.fdopen(fd, "wb")
926N/A
838N/A #
838N/A # We specifically avoid sorting manifests before writing
2608N/A # them to disk-- there's really no point in doing so, since
2608N/A # we'll sort actions globally during packaging operations.
2608N/A #
2608N/A mfile.write(self.tostr_unsorted())
926N/A mfile.close()
838N/A
838N/A try:
838N/A os.chmod(fn, PKG_FILE_MODE)
838N/A portable.rename(fn, mfst_path)
838N/A except EnvironmentError, e:
926N/A if e.errno == errno.EACCES:
2453N/A raise apx.PermissionsException(e.filename)
2453N/A if e.errno == errno.EROFS:
2453N/A raise apx.ReadOnlyFileSystemException(
2608N/A e.filename)
2453N/A raise
2453N/A
2453N/A def get_variants(self, name):
2453N/A if name not in self.attributes:
2453N/A return None
926N/A variants = self.attributes[name]
2843N/A if not isinstance(variants, str):
615N/A return variants
615N/A return [variants]
615N/A
615N/A def get_all_variants(self):
926N/A """Return a dictionary mapping variant tags to their values."""
615N/A return variant.VariantCombinationTemplate(dict((
615N/A (name, self.attributes[name])
838N/A for name in self.attributes
111N/A if name.startswith("variant.")
111N/A )))
111N/A
111N/A def get(self, key, default):
111N/A try:
111N/A return self[key]
111N/A except KeyError:
111N/A return default
113N/A
926N/A def getbool(self, key, default):
838N/A """Returns the boolean of the value of the attribute 'key'."""
926N/A
113N/A ret = self.get(key, default).lower()
113N/A if ret == "true":
113N/A return True
113N/A elif ret == "false":
113N/A return False
113N/A else:
113N/A raise ValueError(_("Attribute value '%s' not 'true' or "
113N/A "'false'" % ret))
113N/A
111N/A def get_size(self, excludes=EmptyI):
1500N/A """Returns an integer representing the total size, in bytes, of
1432N/A the Manifest's data payload.
1542N/A
1970N/A 'excludes' is a list of variants which should be allowed when
2073N/A calculating the total.
2073N/A """
2073N/A
2073N/A size = 0
2073N/A for a in self.gen_actions(excludes):
2073N/A size += a.get_size()
1542N/A return size
146N/A
1432N/A def __load_varcets(self):
1432N/A """Private helper function to populate list of facets and
50N/A variants on-demand."""
1432N/A
1432N/A self._facets = {}
1432N/A self._variants = {}
51N/A for action in self.gen_actions():
1636N/A # append any variants and facets to manifest dict
1432N/A attrs = action.attrs
1507N/A v_list, f_list = action.get_varcet_keys()
51N/A
1500N/A if not (v_list or f_list):
591N/A continue
1970N/A
1970N/A try:
1970N/A for v, d in chain(
591N/A izip(v_list, repeat(self._variants)),
1542N/A izip(f_list, repeat(self._facets))):
1970N/A try:
1970N/A d[v].add(attrs[v])
1970N/A except KeyError:
2073N/A d[v] = set([attrs[v]])
1500N/A except TypeError:
2073N/A # Lists can't be set elements.
2073N/A raise actions.InvalidActionError(action,
1713N/A _("%(forv)s '%(v)s' specified multiple times") %
1713N/A {"forv": v.split(".", 1)[0], "v": v})
2073N/A
2073N/A def __get_facets(self):
2073N/A if self._facets is None:
1713N/A self.__load_varcets()
2608N/A return self._facets
2608N/A
2613N/A def __get_variants(self):
2284N/A if self._variants is None:
2073N/A self.__load_varcets()
2073N/A return self._variants
1713N/A
2073N/A def __getitem__(self, key):
2073N/A """Return the value for the package attribute 'key'."""
2073N/A return self.attributes[key]
1713N/A
1713N/A def __setitem__(self, key, value):
2073N/A """Set the value for the package attribute 'key' to 'value'."""
2073N/A self.attributes[key] = value
2073N/A for a in self.actions:
1500N/A if a.name == "set" and a.attrs["name"] == key:
1500N/A a.attrs["value"] = value
1500N/A return
2613N/A
2910N/A new_attr = AttributeAction(None, name=key, value=value)
51N/A self.actions.append(new_attr)
1500N/A self.actions_bytype.setdefault("set", []).append(new_attr)
1500N/A
1500N/A def __contains__(self, key):
1500N/A return key in self.attributes
1500N/A
1500N/A facets = property(lambda self: self.__get_facets())
1500N/A variants = property(lambda self: self.__get_variants())
2073N/A
2073N/Anull = Manifest()
2073N/A
2073N/Aclass FactoredManifest(Manifest):
2073N/A """This class serves as a wrapper for the Manifest class for callers
2073N/A that need efficient access to package data on a per-action type basis.
1890N/A It achieves this by partitioning the manifest into multiple files (one
1500N/A per action type) and then storing an on-disk cache of the directories
2073N/A explictly and implicitly referenced by the manifest each tagged with
2073N/A the appropriate variants/facets."""
2073N/A
1500N/A def __init__(self, fmri, cache_root, contents=None, excludes=EmptyI,
1500N/A pathname=None):
1500N/A """Raises KeyError exception if factored manifest is not present
1500N/A and contents are None; delays reading of manifest until required
926N/A if cache file is present.
1500N/A
2026N/A 'fmri' is a PkgFmri object representing the identity of the
2608N/A package.
2608N/A
2608N/A 'cache_root' is the pathname of the directory where the manifest
2608N/A and cache files should be stored or loaded from.
1500N/A
2026N/A 'contents' is an optional string to use as the contents of the
2026N/A manifest if a cached copy does not already exist.
2026N/A
2026N/A 'excludes' is optional. If provided it must be a length two
2073N/A list with the variants to be excluded as the first element and
2026N/A the facets to be exclduded as the second element.
2026N/A
1500N/A 'pathname' is an optional string containing the pathname of a
1500N/A manifest. If not provided, it is assumed that the manifest is
1500N/A stored in a file named 'manifest' in the directory indicated by
1500N/A 'cache_root'. If provided, and contents is also provided, then
1500N/A 'contents' will be stored in 'pathname' if it does not already
123N/A exist.
1500N/A """
1500N/A
877N/A Manifest.__init__(self, fmri)
2639N/A self.__cache_root = cache_root
2639N/A self.__pathname = pathname
1500N/A # Make sure that either no excludes were provided or that both
2639N/A # variants and facet excludes were.
2639N/A assert len(excludes) in (0, 2)
2639N/A self.loaded = False
2639N/A
2639N/A # Do we have a cached copy?
2639N/A if not os.path.exists(self.pathname):
2639N/A if contents is None:
2639N/A raise KeyError, fmri
2639N/A # we have no cached copy; save one
1500N/A # don't specify excludes so on-disk copy has
2639N/A # all variants
2639N/A self.set_content(content=contents)
2639N/A self.__finiload()
2639N/A if self.__storeback():
1500N/A self.__unload()
1500N/A if excludes:
2639N/A self.exclude_content(excludes)
2639N/A return
2639N/A
2639N/A # we have a cached copy of the manifest
838N/A mdpath = self.__cache_path("manifest.dircache")
1500N/A
2639N/A # have we computed the dircache?
1500N/A if not os.path.exists(mdpath): # we're adding cache
39N/A self.excludes = EmptyI # to existing manifest
956N/A self.__load()
956N/A if self.__storeback():
956N/A self.__unload()
956N/A if excludes:
2910N/A self.excludes = excludes
2910N/A self.__load()
2910N/A return
2910N/A self.exclude_content(excludes)
2910N/A
2910N/A def __cache_path(self, name):
2910N/A return os.path.join(self.__cache_root, name)
2910N/A
2910N/A def __load(self):
2910N/A """Load all manifest contents from on-disk copy of manifest"""
2910N/A self.set_content(excludes=self.excludes, pathname=self.pathname)
2910N/A self.__finiload()
2910N/A
2910N/A def __unload(self):
2910N/A """Unload manifest; used to reduce peak memory comsumption
2910N/A when downloading new manifests"""
2910N/A self.actions = []
2910N/A self.actions_bytype = {}
2910N/A self._variants = None
2910N/A self._facets = None
956N/A self.attributes = {}
956N/A self.loaded = False
2910N/A
2910N/A def __finiload(self):
2910N/A """Finish loading.... this part of initialization is common
2910N/A to multiple code paths"""
2910N/A self.loaded = True
2910N/A
2910N/A def __storeback(self):
2910N/A """ store the current action set; also create per-type
2910N/A caches. Return True if data was saved, False if not"""
2910N/A assert self.loaded
2910N/A try:
2910N/A self.store(self.pathname)
2910N/A self.__storebytype()
2910N/A return True
2910N/A except apx.PermissionsException:
2910N/A # this allows us to try to cache new manifests
2910N/A # when non-root w/o failures.
2910N/A return False
2910N/A
2910N/A def __storebytype(self):
2910N/A """ create manifest.<typename> files to accelerate partial
2910N/A parsing of manifests. Separate from __storeback code to
2910N/A allow upgrade to reuse existing on disk manifests"""
2910N/A
2910N/A assert self.loaded
2910N/A
2910N/A t_dir = self.__cache_root
2910N/A
2910N/A # Ensure target cache directory and intermediates exist.
2910N/A misc.makedirs(t_dir)
2910N/A
2910N/A # create per-action type cache; use rename to avoid
2910N/A # corrupt files if ^C'd in the middle
2910N/A for n in self.actions_bytype.keys():
2910N/A t_prefix = "manifest.%s." % n
2910N/A
2910N/A fd, fn = tempfile.mkstemp(dir=t_dir, prefix=t_prefix)
2910N/A f = os.fdopen(fd, "wb")
941N/A
1007N/A for a in self.actions_bytype[n]:
1007N/A f.write("%s\n" % a)
1007N/A f.close()
1007N/A os.chmod(fn, PKG_FILE_MODE)
1007N/A portable.rename(fn, self.__cache_path("manifest.%s" % n))
1007N/A
1007N/A def create_cache(name, refs):
1007N/A try:
1007N/A fd, fn = tempfile.mkstemp(dir=t_dir,
1007N/A prefix="manifest.dircache.")
1007N/A with os.fdopen(fd, "wb") as f:
1007N/A f.writelines(refs())
1007N/A os.chmod(fn, PKG_FILE_MODE)
1007N/A portable.rename(fn, self.__cache_path(name))
1100N/A except EnvironmentError, e:
2704N/A raise apx._convert_error(e)
2704N/A
2704N/A create_cache("manifest.dircache", self._gen_dirs_to_str)
2704N/A create_cache("manifest.mediatorcache",
2704N/A self._gen_mediators_to_str)
2704N/A
2704N/A @staticmethod
2704N/A def clear_cache(cache_root):
941N/A """Remove all manifest cache files found in the given directory
941N/A (excluding the manifest itself).
144N/A """
941N/A
1100N/A try:
1100N/A for cname in os.listdir(cache_root):
1100N/A if not cname.startswith("manifest."):
1100N/A continue
1100N/A try:
1100N/A portable.remove(os.path.join(
1100N/A cache_root, cname))
1100N/A except EnvironmentError, e:
1100N/A if e.errno != errno.ENOENT:
1100N/A raise
941N/A except EnvironmentError, e:
941N/A if e.errno != errno.ENOENT:
941N/A # Only raise error if failure wasn't due to
941N/A # cache directory not existing.
941N/A raise apx._convert_error(e)
941N/A
941N/A def __load_cached_data(self, name):
941N/A """Private helper function for loading arbitrary cached manifest
941N/A data.
941N/A """
941N/A
941N/A mpath = self.__cache_path(name)
941N/A if not os.path.exists(mpath):
941N/A # no cached copy
941N/A if not self.loaded:
429N/A # need to load from disk
941N/A self.__load()
941N/A assert self.loaded
941N/A return
941N/A
941N/A # we have cached copy on disk; use it
429N/A try:
941N/A with open(mpath, "rb") as f:
941N/A self._cache[name] = [
941N/A a for a in
941N/A (
941N/A actions.fromstr(s.rstrip())
1007N/A for s in f
1007N/A )
1234N/A if not self.excludes or
1007N/A a.include_this(self.excludes)
1007N/A ]
1007N/A except EnvironmentError, e:
2639N/A raise apx._convert_error(e)
2639N/A
1007N/A def get_directories(self, excludes):
1007N/A """ return a list of directories implicitly or explicitly
1007N/A referenced by this object
1007N/A """
1007N/A self.__load_cached_data("manifest.dircache")
1007N/A return Manifest.get_directories(self, excludes)
1007N/A
1007N/A def gen_actions_by_type(self, atype, excludes=EmptyI):
1007N/A """ generate actions of the specified type;
1007N/A use already in-memory stuff if already loaded,
1007N/A otherwise use per-action types files"""
1007N/A
1007N/A if self.loaded: #if already loaded, use in-memory cached version
1007N/A # invoke subclass method to generate action by action
1007N/A for a in Manifest.gen_actions_by_type(self, atype,
1007N/A excludes):
1007N/A yield a
1007N/A return
1007N/A
1007N/A # This checks if we've already written out the factored
1007N/A # manifest files. If so, we'll use it, and if not, then
1007N/A # we'll load the full manifest.
1007N/A mpath = self.__cache_path("manifest.dircache")
1007N/A
941N/A if not os.path.exists(mpath):
941N/A # no cached copy :-(
941N/A if not self.loaded:
144N/A # get manifest from disk
144N/A self.__load()
1472N/A # invoke subclass method to generate action by action
1472N/A for a in Manifest.gen_actions_by_type(self, atype,
1472N/A excludes):
1472N/A yield a
1352N/A else:
1516N/A if excludes == EmptyI:
1890N/A excludes = self.excludes
1890N/A assert excludes == self.excludes or \
1890N/A self.excludes == EmptyI
1890N/A # we have a cached copy - use it
1890N/A mpath = self.__cache_path("manifest.%s" % atype)
1352N/A
1472N/A if not os.path.exists(mpath):
1352N/A return # no such action in this manifest
1352N/A
1352N/A with open(mpath, "rb") as f:
1352N/A for l in f:
1352N/A a = actions.fromstr(l.rstrip())
1352N/A if not excludes or \
1352N/A a.include_this(excludes):
2073N/A yield a
1352N/A
429N/A def gen_mediators(self, excludes):
429N/A """A generator function that yields set actions expressing the
144N/A set of possible mediations for this package.
1386N/A """
1386N/A self.__load_cached_data("manifest.mediatorcache")
1386N/A return Manifest.gen_mediators(self, excludes)
1636N/A
1636N/A def __load_attributes(self):
1636N/A """Load attributes dictionary from cached set actions;
1636N/A this speeds up pkg info a lot"""
2073N/A
1636N/A mpath = self.__cache_path("manifest.set")
2073N/A if not os.path.exists(mpath):
1636N/A return False
1636N/A with open(mpath, "rb") as f:
1636N/A for l in f:
1386N/A a = actions.fromstr(l.rstrip())
1636N/A if not self.excludes or \
1636N/A a.include_this(self.excludes):
1636N/A self.fill_attributes(a)
1636N/A return True
2073N/A
1636N/A def __getitem__(self, key):
2284N/A if not self.loaded and not self.__load_attributes():
1636N/A self.__load()
1636N/A return Manifest.__getitem__(self, key)
1636N/A
1386N/A def __setitem__(self, key, value):
315N/A """No assignments to factored manifests allowed."""
315N/A assert "FactoredManifests are not dicts"
315N/A
315N/A def __contains__(self, key):
315N/A if not self.loaded and not self.__load_attributes():
315N/A self.__load()
315N/A return Manifest.__contains__(self, key)
315N/A
1636N/A def get(self, key, default):
1636N/A try:
1636N/A return self[key]
1636N/A except KeyError:
1636N/A return default
1636N/A
2073N/A def get_variants(self, name):
1636N/A if not self.loaded and not self.__load_attributes():
2073N/A self.__load()
1636N/A return Manifest.get_variants(self, name)
1636N/A
144N/A def get_all_variants(self):
838N/A if not self.loaded and not self.__load_attributes():
838N/A self.__load()
838N/A return Manifest.get_all_variants(self)
926N/A
926N/A @staticmethod
926N/A def search_dict(cache_path, excludes, return_line=False):
926N/A return Manifest.search_dict(cache_path, excludes,
838N/A return_line=return_line)
1231N/A
1231N/A def gen_actions(self, excludes=EmptyI):
2091N/A if not self.loaded:
1231N/A self.__load()
1231N/A return Manifest.gen_actions(self, excludes=excludes)
1231N/A
1231N/A def __str__(self, excludes=EmptyI):
1231N/A if not self.loaded:
205N/A self.__load()
257N/A return Manifest.__str__(self)
257N/A
257N/A def duplicates(self, excludes=EmptyI):
205N/A if not self.loaded:
205N/A self.__load()
1461N/A return Manifest.duplicates(self, excludes=excludes)
1461N/A
1461N/A def difference(self, origin, origin_exclude=EmptyI,
1461N/A self_exclude=EmptyI):
1461N/A if not self.loaded:
1461N/A self.__load()
1461N/A return Manifest.difference(self, origin,
1461N/A origin_exclude=origin_exclude,
1461N/A self_exclude=self_exclude)
1461N/A
1461N/A @property
1461N/A def pathname(self):
1044N/A """The absolute pathname of the file containing the manifest."""
2910N/A
2910N/A if self.__pathname:
2910N/A return self.__pathname
2910N/A return os.path.join(self.__cache_root, "manifest")
1044N/A
2910N/A
2910N/Aclass EmptyFactoredManifest(Manifest):
1044N/A """Special class for pkgplan's need for a empty manifest;
2910N/A the regular null manifest doesn't support get_directories
2910N/A and making the factored manifest code handle this case is
2910N/A too ugly..."""
2910N/A
2910N/A def __init__(self):
1044N/A Manifest.__init__(self)
2910N/A
2910N/A def difference(self, origin, origin_exclude=EmptyI,
2910N/A self_exclude=EmptyI):
2910N/A """Return three lists of action pairs representing origin and
2910N/A destination actions. The first list contains the pairs
2910N/A representing additions, the second list contains the pairs
2910N/A representing updates, and the third list contains the pairs
2910N/A representing removals. All three lists are in the order in
2910N/A which they should be executed."""
2910N/A
2910N/A # The difference for this case is simply everything in the
2286N/A # origin has been removed. This is an optimization for
2910N/A # uninstall.
2910N/A return ManifestDifference([], [],
2910N/A [(a, None) for a in origin.gen_actions(origin_exclude)])
2910N/A
2910N/A @staticmethod
2910N/A def get_directories(excludes):
2910N/A return []
2910N/A
1044N/A def exclude_content(self, *args, **kwargs):
2910N/A # This method is overridden so that self.excludes is never set
2910N/A # on the singleton NullFactoredManifest.
2910N/A return
2910N/A
2910N/A def set_content(self, *args, **kwargs):
2639N/A raise RuntimeError("Cannot call set_content on an "
2910N/A "EmptyFactoredManifest")
2910N/A
2910N/ANullFactoredManifest = EmptyFactoredManifest()
2910N/A
2910N/Aclass ManifestError(Exception):
2910N/A """Simple Exception class to handle manifest specific errors"""
2910N/A
2910N/A def __init__(self, duplicates=EmptyI):
2910N/A self.__duplicates = duplicates
2910N/A
2910N/A def __str__(self):
2910N/A ret = []
2910N/A for d in self.__duplicates:
2910N/A ret.append("%s\n%s\n\n" % d)
2910N/A
2910N/A return "\n".join(ret)
2910N/A
2639N/A
2639N/A