manifest.py revision 39
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
39N/Aimport pkg.fmri as fmri
39N/Aimport pkg.package as package
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
39N/A
39N/Aclass ManifestAction(object):
39N/A def __init__(self):
39N/A self.type = None
39N/A self.attrs = {}
39N/A self.mandatory_attrs = []
39N/A
39N/A def is_complete(self):
39N/A for a in self.mandatory_attrs:
39N/A if not self.attrs.has_key(a):
39N/A return False
39N/A
39N/A return True
39N/A
39N/Aclass FileManifestAction(ManifestAction):
39N/A def __init__(self):
39N/A ManifestAction.__init__(self)
39N/A
39N/A self.type = ACTION_FILE
39N/A self.mandatory_attrs = [ "owner", "mode", "hash", "group",
39N/A "path" ]
39N/A
39N/A def __str__(self):
39N/A # XXX generalize to superclass?
39N/A r = "file %s %s %s %s %s" % (self.attrs["mode"],
39N/A self.attrs["owner"], self.attrs["group"],
39N/A self.attrs["path"], self.attrs["hash"])
39N/A
39N/A for k in self.attrs.keys():
39N/A if k in self.mandatory_attrs:
39N/A continue
39N/A r = r + " %s=%s" % (k, self.attrs[k])
39N/A
39N/A return r
39N/A
39N/Aclass LinkManifestAction(ManifestAction):
39N/A def __init__(self):
39N/A ManifestAction.__init__(self)
39N/A
39N/A self.type = ACTION_LINK
39N/A self.mandatory_attrs = [ "path", "target" ]
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
39N/A XXX Need one or more differences methods, so that we can build a list of
39N/A the actions we must take and the actions that are no longer relevant,
39N/A which would include deletions.
39N/A """
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
39N/A def set_fmri(self, fmri):
39N/A self.fmri = fmri
39N/A
39N/A def add_attribute_line(self, str):
39N/A """An attribute line is
39N/A
39N/A set attribute = str
39N/A
39N/A where str becomes the value of the attribute.
39N/A
39N/A XXX For now, the value is left as a simple string. We could in
39N/A principle parse into specific types."""
39N/A
39N/A m = re.match("^set ([a-z,.-_]*)\s*=\s*(.*)$", str)
39N/A self.attributes[m.group(1)] = m.group(2)
39N/A
39N/A return
39N/A
39N/A def add_file_action_line(self, str):
39N/A """A file action line is
39N/A
39N/A file mode owner group path hash n=v
39N/A
39N/A """
39N/A
39N/A m = re.match("^file (\d+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)\s?(.*)", str)
39N/A if m == None:
39N/A raise SyntaxError, "invalid file action '%s'" % str
39N/A
39N/A a = FileManifestAction()
39N/A a.attrs["mode"] = m.group(1)
39N/A a.attrs["owner"] = m.group(2)
39N/A a.attrs["group"] = m.group(3)
39N/A a.attrs["path"] = m.group(4)
39N/A a.attrs["hash"] = m.group(5)
39N/A
39N/A # if any name value settings, add to action's tags
39N/A if m.group(6) != "":
39N/A nvs = re.split("\s+", m.group(6))
39N/A
39N/A for nv in nvs:
39N/A # XXX what if v is empty? syntax error?
39N/A n, v = re.split("=", nv, 1)
39N/A n.strip()
39N/A v.strip()
39N/A a.attrs[n] = v
39N/A
39N/A if len(self.actions) == 0:
39N/A self.actions.append(a)
39N/A else:
39N/A bisect.insort(self.actions, a)
39N/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():
39N/A if re.match("^set ", l):
39N/A self.add_attribute_line(l)
39N/A elif re.match("^file ", l):
39N/A self.add_file_action_line(l)
39N/A else:
39N/A raise SyntaxError, "unknown action '%s'" % l
39N/A
39N/A return
39N/A
39N/Aif __name__ == "__main__":
39N/A m = Manifest()
39N/A
39N/A x = """\
39N/Aset com.sun,test = true
39N/Afile 0555 sch staff /usr/bin/i386/sort fff555fff isa=i386
39N/A"""
39N/A m.set_content(x)
39N/A
39N/A print m