outline install transaction; poke at dependencies in manifests
authorStephen Hahn <sch@sun.com>
Thu, 14 Jun 2007 16:49:29 -0700
changeset 48 03abcbec50dc
parent 47 0bee2feb0e3b
child 49 c3a70bdc4527
outline install transaction; poke at dependencies in manifests
src/client.py
src/modules/dependency.py
src/modules/manifest.py
--- a/src/client.py	Thu Jun 14 15:47:31 2007 -0700
+++ b/src/client.py	Thu Jun 14 16:49:29 2007 -0700
@@ -161,7 +161,20 @@
         # do we have this manifest?
         #   get it if not
         # request manifest
+        # examine manifest for dependencies
+        #   if satisfied by inventory, continue
+        #   if satisfied by pending transaction, continue
+        #   if unsatisfied, then
+        #     if optional, then evaluate client's optional policy to either
+        #     treat-as-required, treat-as-required-unless-pinned, ignore
+        #     skip if ignoring
+        #     if pinned
+        #       ignore if treat-as-required-unless-pinned
+        #     else
+        #       **evaluation of incorporations**
+        #     pursue installation of this package
         # examine manifest for files
+        #
         # request files
 
         return # a client operation
--- a/src/modules/dependency.py	Thu Jun 14 15:47:31 2007 -0700
+++ b/src/modules/dependency.py	Thu Jun 14 16:49:29 2007 -0700
@@ -26,7 +26,8 @@
 #
 
 REQUIRE = 0
-INCORPORATE = 1
+OPTIONAL = 1
+INCORPORATE = 10
 
 class Dependency(object):
         """A Dependency object is a relationship between one Package and
@@ -38,7 +39,10 @@
                 self.host_pkg_fmri = host_pkg_fmri
                 self.req_pkg_fmri = req_pkg_fmri
 
-                assert type == REQUIRE or type == INCORPORATE
+                assert type == REQUIRE \
+                    or type == INCORPORATE \
+                    or type == OPTIONAL
+
                 self.type = type
 
         def satisfied(self, pkg_fmri):
@@ -48,8 +52,11 @@
 
 	def __repr__(self):
 		if self.type == REQUIRE:
-			return "%s -> %s" % \
+			return "%s => %s" % \
+				(self.host_pkg_fmri, self.req_pkg_fmri)
+		elif self.type == OPTIONAL:
+			return "%s o> %s" % \
 				(self.host_pkg_fmri, self.req_pkg_fmri)
 		elif self.type == INCORPORATE:
-			return "%s > %s" % \
+			return "%s >> %s" % \
 				(self.host_pkg_fmri, self.req_pkg_fmri)
--- a/src/modules/manifest.py	Thu Jun 14 15:47:31 2007 -0700
+++ b/src/modules/manifest.py	Thu Jun 14 16:49:29 2007 -0700
@@ -43,6 +43,16 @@
 ACTION_GROUP = 210
 ACTION_SERVICE = 300
 ACTION_RESTART = 310
+ACTION_DEPEND = 400
+
+DEPEND_REQUIRE = 0
+DEPEND_OPTIONAL = 1
+DEPEND_INCORPORATE =10
+
+depend_str = { DEPEND_REQUIRE : "require",
+                DEPEND_OPTIONAL : "optional",
+                DEPEND_INCORPORATE : "incorporate"
+}
 
 class ManifestAction(object):
         def __init__(self):
@@ -85,6 +95,26 @@
                 self.type = ACTION_LINK
                 self.mandatory_attrs = [ "path", "target" ]
 
+class DependencyManifestAction(ManifestAction):
+        def __init__(self):
+                ManifestAction.__init__(self)
+
+                self.type = ACTION_DEPEND
+                self.mandatory_attrs = [ "fmri", "dtype" ]
+
+        def __str__(self):
+                # XXX generalize to superclass?
+                r = "%s %s" % (depend_str[self.attrs["dtype"]],
+                    self.attrs["fmri"])
+
+                for k in self.attrs.keys():
+                        if k in self.mandatory_attrs:
+                                continue
+                        r = r + " %s=%s" % (k, self.attrs[k])
+
+                return r
+
+
 class Manifest(object):
         """A Manifest is the representation of the actions composing a specific
         package version on both the client and the repository.  Both purposes
@@ -119,6 +149,12 @@
         as an indicator that a specific package version is supported by the
         vendor, example.com.
 
+        manifest.null is provided as the null manifest.  Differences against the
+        null manifest result in the complete set of attributes and actions of
+        the non-null manifest, meaning that all operations can be viewed as
+        tranitions between the manifest being installed and the manifest already
+        present in the image (which may be the null manifest).
+
         XXX Need one or more differences methods, so that we can build a list of
         the actions we must take and the actions that are no longer relevant,
         which would include deletions.
@@ -146,29 +182,23 @@
                 return r
 
         def display_differences(self, other):
-                """Output expects that self is newer than other."""
+                """Output expects that self is newer than other.  Use of sets
+                requires that we convert the action objects into some marshalled
+                form, otherwise set member identities are derived from the
+                object pointers, rather than the contents."""
+
                 sset = set()
                 oset = set()
 
                 for acs in self.actions:
-                        sset.add(acs)
+                        sset.add("%s" % acs)
 
                 for aco in other.actions:
-                        oset.add(aco)
-
-                dset = sset.symmetric_difference(oset)
-
-                for act in dset:
-                        if act in sset:
-                                print "+ %s" % act
-                        else:
-                                print "- %s" % act
-
-                sset = set()
-                oset = set()
+                        oset.add("%s" % aco)
 
                 for ats in self.attributes.keys():
                         sset.add("%s=%s" % (ats, self.attributes[ats]))
+
                 for ato in other.attributes.keys():
                         oset.add("%s=%s" % (ato, other.attributes[ato]))
 
@@ -232,6 +262,46 @@
                 else:
                         bisect.insort(self.actions, a)
 
+        def add_dependency_action_line(self, str):
+                """A dependency action line is one or more of
+
+                require fmri n=v
+                incorporate fmri n=v
+                optional fmri n=v
+
+                """
+
+                m = re.match("^(require|incorporate|optional) ([^\s]+)\s?(.*)", str)
+                if m == None:
+                        raise SyntaxError, "invalid dependency action '%s'" % str
+
+                a = DependencyManifestAction()
+                for k in depend_str.keys():
+                        if depend_str[k] == m.group(1):
+                                a.attrs["dtype"] = k
+                                break
+
+                if a.attrs["dtype"] == None:
+                        raise KeyError, "unknown dependency type '%s'" % m.group(1)
+
+                a.attrs["fmri"] = m.group(2)
+
+                # if any name value settings, add to action's tags
+                if m.group(3) != "":
+                        nvs = re.split("\s+", m.group(6))
+
+                        for nv in nvs:
+                                # XXX what if v is empty?  syntax error?
+                                n, v = re.split("=", nv, 1)
+                                n.strip()
+                                v.strip()
+                                a.attrs[n] = v
+
+                if len(self.actions) == 0:
+                        self.actions.append(a)
+                else:
+                        bisect.insort(self.actions, a)
+
         def set_content(self, str):
                 """str is the text representation of the manifest"""
 
@@ -240,16 +310,21 @@
                                 self.add_attribute_line(l)
                         elif re.match("^file ", l):
                                 self.add_file_action_line(l)
+                        elif re.match("^(require|optional|incorporate) ", l):
+                                self.add_dependency_action_line(l)
                         else:
                                 raise SyntaxError, "unknown action '%s'" % l
 
                 return
 
+null = Manifest()
+
 if __name__ == "__main__":
         m1 = Manifest()
 
         x = """\
 set com.sun,test = true
+require pkg:/library/libc
 file 0555 sch staff /usr/bin/i386/sort fff555fff isa=i386
 """
         m1.set_content(x)
@@ -261,6 +336,7 @@
         y = """\
 set com.sun,test = true
 set com.sun,data = true
+require pkg:/library/libc
 file 0555 sch staff /usr/bin/i386/sort fff555ff9 isa=i386
 file 0555 sch staff /usr/bin/amd64/sort eeeaaaeee isa=amd64
 """
@@ -270,3 +346,7 @@
         print m2
 
         m2.display_differences(m1)
+
+        print null
+
+        m2.display_differences(null)