manifest.py revision 1066
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# CDDL HEADER START
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# The contents of this file are subject to the terms of the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# Common Development and Distribution License (the "License").
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen# You may not use this file except in compliance with the License.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen# See the License for the specific language governing permissions
6fe91298731abf0b70dfd796ecc30d3be81fa5d1Timo Sirainen# and limitations under the License.
436adac819e7cbeef04af08dcc6a4f3ecd4a1d9eMartti Rannanjärvi# When distributing Covered Code, include this CDDL HEADER in each
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen# If applicable, add the following below this CDDL HEADER, with the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# fields enclosed by brackets "[]" replaced with your own identifying
73a552a9ed06cd6017ad4ee4b252a8b38c8ac42dTimo Sirainen# information: Portions Copyright [yyyy] [name of copyright owner]
73a552a9ed06cd6017ad4ee4b252a8b38c8ac42dTimo Sirainen# CDDL HEADER END
7ccdf78cd45aea9d14e048a5b9f077515c71978fTimo Sirainen# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen# Use is subject to license terms.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainenfrom pkg.actions.attribute import AttributeAction
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen """A Manifest is the representation of the actions composing a specific
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen package version on both the client and the repository. Both purposes
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen utilize the same storage format.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen The serialized structure of a manifest is an unordered list of actions.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen The special action, "set", represents a package attribute.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen The reserved attribute, "fmri", represents the package and version
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen described by this manifest. It is available as a string via the
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen attributes dictionary, and as an FMRI object from the fmri member.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen The list of manifest-wide reserved attributes is
d22301419109ed4a38351715e6760011421dadecTimo Sirainen base_directory Default base directory, for non-user images.
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen fmri Package FMRI.
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen isa Package is intended for a list of ISAs.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen platform Package is intended for a list of platforms.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen relocatable Suitable for User Image.
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen All non-prefixed attributes are reserved to the framework. Third
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen parties may prefix their attributes with a reversed domain name, domain
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen name, or stock symbol. An example might be
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen com.example,supported
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen as an indicator that a specific package version is supported by the
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen vendor, example.com.
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen manifest.null is provided as the null manifest. Differences against the
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen null manifest result in the complete set of attributes and actions of
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen the non-null manifest, meaning that all operations can be viewed as
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen tranitions between the manifest being installed and the manifest already
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen present in the image (which may be the null manifest).
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen self.attributes = {} # package-wide attributes
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen if "fmri" not in self.attributes and self.fmri != None:
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen if "fmri" not in self.attributes and self.fmri != None:
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen def difference(self, origin, origin_exclude=EmptyI,
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen """Return three lists of action pairs representing origin and
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen destination actions. The first list contains the pairs
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen representing additions, the second list contains the pairs
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen representing updates, and the third list contains the pairs
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen representing removals. All three lists are in the order in
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen which they should be executed."""
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen # XXX Do we need to find some way to assert that the keys are
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen # all unique?
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen # No origin was provided, so nothing has been changed or
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen # removed; only added. In addition, this doesn't need
2f2823ad8928654ed405467c6c1f4fd4c6f5cf7cTimo Sirainen # to be sorted since the caller likely already does
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainen [(None, a) for a in self.gen_actions(self_exclude)],
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen added = [(None, sdict[i]) for i in sset - oset]
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainen removed = [(odict[i], None) for i in oset - sset]
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen # XXX for now, we force license actions to always be
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi # different to insure that existing license files for
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen # new versions are always installed
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainen if odict[i].different(sdict[i]) or i[0] == "license"
00efa7d99981e18e286c02b18c1163dde18ee521Timo Sirainen # XXX Do changed actions need to be sorted at all? This is
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # likely to be the largest list, so we might save significant
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # time by not sorting. Should we sort above? Insert into a
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # sorted list?
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen # singlesort = lambda x: x[0] or x[1]
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen """Like the unix utility comm, except that this function
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen takes an arbitrary number of manifests and compares them,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen returning a tuple consisting of each manifest's actions
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen that are not the same for all manifests, followed by a
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen list of actions that are the same in each manifest."""
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # construct list of dictionaries of actions in each
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # manifest, indexed by unique keys
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # construct list of key sets in each dict
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen common_keys = reduce(lambda a, b: a & b, m_sets)
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen # determine which common_keys have common actions
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen [m_dicts[i][k] for k in m_sets[i] - common_keys]
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen def combined_difference(self, origin, ov=EmptyI, sv=EmptyI):
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen """Where difference() returns three lists, combined_difference()
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen returns a single list of the concatenation of the three."""
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi return list(chain(*self.difference(origin, ov, sv)))
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen def humanized_differences(self, other, ov=EmptyI, sv=EmptyI):
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen """Output expects that self is newer than other. Use of sets
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen requires that we convert the action objects into some marshalled
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen form, otherwise set member identities are derived from the
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen object pointers, rather than the contents."""
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen """Generate actions in manifest through ordered callable list"""
ce8244c05b9f89d7729a5abe6f4838c1dce46c64Timo Sirainen def gen_actions_by_type(self, atype, excludes=EmptyI):
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen """Generate actions in the manifest of type "type"
ce8244c05b9f89d7729a5abe6f4838c1dce46c64Timo Sirainen through ordered callable list"""
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen def gen_key_attribute_value_by_type(self, atype, excludes=EmptyI):
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen """Generate the value of the key atrribute for each action
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen of type "type" in the manifest."""
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen for a in self.gen_actions_by_type(atype, excludes)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen """Find actions in the manifest which are duplicates (i.e.,
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen represent the same object) but which are not identical (i.e.,
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen have all the same attributes)."""
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi """Return a key on which actions can be sorted."""
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen acts = [a for a in self.gen_actions(excludes)]
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen for k, g in groupby(sorted(acts, key=fun), fun):
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen def set_content(self, content, excludes=EmptyI):
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen """content is the text representation of the manifest"""
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # So we could build up here the type/key_attr dictionaries like
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # sdict and odict in difference() above, and have that be our
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # main datastore, rather than the simple list we have now. If
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # we do that here, we can even assert that the "same" action
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # can't be in a manifest twice. (The problem of having the same
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # action more than once in packages that can be installed
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # together has to be solved somewhere else, though.)
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # Add the FMRI to the exception and re-raise
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen # Translate old action to new.
log=None):
if log is None:
log = lambda x: None
action_dict = {}
if full_value is None:
if full_value is None:
if full_value is None:
for t in tok
], cp)
while line:
except KeyError, k:
if return_line:
arg = l
return action_dict
except IOError:
except OSError, e:
return variants
return [variants]
except KeyError:
return default
return size
if not contents:
elif excludes:
elif excludes:
f.close()
return True
except EnvironmentError, e:
return False
f.close()
f.write(s)
f.close()
for d in dirs:
for v in dirs[d]:
for t in v.iteritems()))
dirs = {}
v, f = a.get_varcet_keys()
if d not in dirs:
for d in dirs:
if {} in dirs[d]:
dirs[d] = [{}]
return dirs
alist = [
f.close()
s = set([
for a in alist
return list(s)
excludes):
excludes):
f.close()
return False
f.close()
return True
except KeyError:
return default