manifest.py revision 59
39N/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#
39N/A# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
39N/A# Use is subject to license terms.
39N/A
39N/Aimport bisect
39N/Aimport os
39N/Aimport re
39N/Aimport sha
39N/Aimport shutil
39N/Aimport time
39N/Aimport urllib
39N/A
51N/Aimport pkg.actions as actions
39N/Aimport pkg.fmri as fmri
39N/Aimport pkg.package as package
51N/Aimport pkg.client.retrieve as retrieve
39N/A
39N/A# The type member is used for the ordering of actions.
39N/AACTION_DIR = 10
39N/AACTION_FILE = 20
39N/AACTION_LINK = 50
39N/AACTION_HARDLINK = 55
39N/AACTION_DEVICE = 100
39N/AACTION_USER = 200
39N/AACTION_GROUP = 210
39N/AACTION_SERVICE = 300
39N/AACTION_RESTART = 310
48N/AACTION_DEPEND = 400
48N/A
48N/ADEPEND_REQUIRE = 0
48N/ADEPEND_OPTIONAL = 1
48N/ADEPEND_INCORPORATE =10
48N/A
48N/Adepend_str = { DEPEND_REQUIRE : "require",
48N/A DEPEND_OPTIONAL : "optional",
48N/A DEPEND_INCORPORATE : "incorporate"
48N/A}
39N/A
39N/Aclass Manifest(object):
39N/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 package
39N/A attributes, followed by an unordered list of actions (such as files to
39N/A install).
39N/A
39N/A The special action, "set", represents an attribute setting.
39N/A
39N/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 licenses Package contains software available under a list
39N/A of license terms.
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
48N/A manifest.null is provided as the null manifest. Differences against the
48N/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).
59N/A """
48N/A
59N/A # XXX The difference methods work, but are wrong: we must provide
59N/A # action equivalency relationships, so that we can easily discriminate
59N/A # related actions from independent ones.
39N/A
39N/A def __init__(self):
39N/A self.fmri = None
39N/A
39N/A self.actions = []
39N/A self.attributes = {}
39N/A return
39N/A
39N/A def __str__(self):
39N/A r = ""
39N/A
39N/A if self.fmri != None:
39N/A r = r + "set fmri = %s\n" % self.fmri
39N/A
39N/A for att in sorted(self.attributes.keys()):
39N/A r = r + "set %s = %s\n" % (att, self.attributes[att])
39N/A
39N/A for act in self.actions:
39N/A r = r + "%s\n" % act
39N/A
39N/A return r
39N/A
59N/A def difference(self, other):
59N/A """Output is the list of positive actions to take to move from
59N/A other to self. For instance, a file in self, but not in other,
59N/A would be added."""
59N/A
59N/A sset = set([str(acs) for acs in self.actions])
59N/A oset = set([str(aco) for aco in other.actions])
59N/A
59N/A for ats in self.attributes.keys():
59N/A sset.add("%s=%s" % (ats, self.attributes[ats]))
59N/A
59N/A for ato in other.attributes.keys():
59N/A oset.add("%s=%s" % (ato, other.attributes[ato]))
59N/A
59N/A dset = sset.symmetric_difference(oset)
59N/A
59N/A positive_actions = []
59N/A
59N/A for acs in self.actions:
59N/A rep = str(acs)
59N/A if rep in dset:
59N/A positive_actions.append(acs)
59N/A
59N/A return positive_actions
59N/A
59N/A
59N/A def antidifference(self, other):
59N/A """Output is the list of negative actions to take to move from
59N/A other to self. For instance, a file in other, but not in self,
59N/A would be undone.
59N/A
59N/A XXX The antidifference must be reconciled with any replacement
59N/A actions on the positive side by a higher level routine."""
59N/A
59N/A sset = set([str(acs) for acs in self.actions])
59N/A oset = set([str(aco) for aco in other.actions])
59N/A
59N/A for ats in self.attributes.keys():
59N/A sset.add("%s=%s" % (ats, self.attributes[ats]))
59N/A
59N/A for ato in other.attributes.keys():
59N/A oset.add("%s=%s" % (ato, other.attributes[ato]))
59N/A
59N/A dset = sset.symmetric_difference(oset)
59N/A
59N/A for aco in other.actions:
59N/A rep = "%s" % acs
59N/A if rep in dset:
59N/A negative_actions.append(aco)
59N/A
59N/A return negative_actions
59N/A
46N/A def display_differences(self, other):
48N/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
59N/A sset = set([str(acs) for acs in self.actions])
59N/A oset = set([str(aco) for aco in other.actions])
46N/A
46N/A for ats in self.attributes.keys():
46N/A sset.add("%s=%s" % (ats, self.attributes[ats]))
48N/A
46N/A for ato in other.attributes.keys():
46N/A oset.add("%s=%s" % (ato, other.attributes[ato]))
46N/A
46N/A dset = sset.symmetric_difference(oset)
46N/A
46N/A for att in dset:
46N/A if att in sset:
46N/A print "+ %s" % att
46N/A else:
46N/A print "- %s" % att
46N/A
39N/A def set_fmri(self, fmri):
39N/A self.fmri = fmri
39N/A
51N/A @staticmethod
51N/A def make_opener(fmri, action):
51N/A def opener():
51N/A return retrieve.get_datastream(fmri, action.hash)
51N/A return opener
51N/A
39N/A def set_content(self, str):
39N/A """str is the text representation of the manifest"""
39N/A
39N/A for l in str.splitlines():
51N/A if re.match("^\s*(#.*)?$", l):
50N/A continue
51N/A
51N/A try:
51N/A action = actions.fromstr(l)
51N/A except KeyError:
51N/A raise SyntaxError, \
51N/A "unknown action '%s'" % l.split()[0]
51N/A
51N/A if hasattr(action, "hash"):
51N/A action.data = \
51N/A self.make_opener(self.fmri, action)
51N/A
51N/A if len(self.actions) == 0:
51N/A self.actions.append(action)
39N/A else:
51N/A bisect.insort(self.actions, action)
39N/A
39N/A return
39N/A
48N/Anull = Manifest()
48N/A
39N/Aif __name__ == "__main__":
46N/A m1 = Manifest()
39N/A
39N/A x = """\
51N/Aset com.sun,test=true
59N/Adepend type=require fmri=pkg:/library/libc
51N/Afile fff555fff mode=0555 owner=sch group=staff path=/usr/bin/i386/sort isa=i386
39N/A"""
46N/A m1.set_content(x)
46N/A
46N/A print m1
46N/A
46N/A m2 = Manifest()
39N/A
46N/A y = """\
51N/Aset com.sun,test=true
51N/Aset com.sun,data=true
59N/Adepend type=require fmri=pkg:/library/libc
51N/Afile fff555ff9 mode=0555 owner=sch group=staff path=/usr/bin/i386/sort isa=i386
51N/Afile eeeaaaeee mode=0555 owner=sch group=staff path=/usr/bin/amd64/sort isa=amd64
51N/A
51N/Afile ff555fff mode=0555 owner=root group=bin path=/kernel/drv/foo isa=i386
51N/Afile ff555ffe mode=0555 owner=root group=bin path=/kernel/drv/amd64/foo isa=amd64
51N/Afile ff555ffd mode=0644 owner=root group=bin path=/kernel/drv/foo.conf
46N/A"""
46N/A
46N/A m2.set_content(y)
46N/A
46N/A print m2
46N/A
46N/A m2.display_differences(m1)
48N/A
48N/A print null
48N/A
48N/A m2.display_differences(null)