start pkg graph management, annotate pkgsend with server states
add reminders on damaged and diskless install states
remove pkgsend pieces from pkg
add catalog, dependency, content (incomplete)
change versions to use timestamps (incomplete)
--- a/doc/pkg-states.txt Tue Apr 03 14:46:40 2007 -0700
+++ b/doc/pkg-states.txt Wed Apr 04 18:26:56 2007 -0700
@@ -152,3 +152,9 @@
operation) get represented in the state? Is there an image state
machine model as well?
+ XXX Need a substate of INSTALLED for damaged packages.
+
+ XXX Need a substate of INSTALLED for packages where the global zone
+ portion is available, but local installation has not finished. Can
+ we generalize this state for all diskless installs?
+
--- a/src/client.py Tue Apr 03 14:46:40 2007 -0700
+++ b/src/client.py Wed Apr 04 18:26:56 2007 -0700
@@ -28,6 +28,15 @@
# We use urllib2 for GET and POST operations, but httplib for PUT and DELETE
# operations.
+# The client is going to maintain an on-disk cache of its state, so that startup
+# assembly of the graph is reduced.
+
+# Client graph is of the entire local catalog. As operations progress, package
+# states will change.
+
+# Deduction operation allows the compilation of the local component of the
+# catalog, only if an authoritative repository can identify critical files.
+
import getopt
import httplib
import os
@@ -36,7 +45,12 @@
import urllib2
import urlparse
+import pkg.catalog
import pkg.config
+import pkg.dependency
+import pkg.fmri
+import pkg.package
+import pkg.version
def usage():
print """\
@@ -45,17 +59,10 @@
Install subcommands:
pkg catalog
- pkg install pkg_name
- pkg uninstall pkg_name
-
-Packager subcommands:
- pkg open [-e] pkg_name
- pkg add file|link|device path file
- pkg delete path
- pkg meta add require|exclude pkg_name
- pkg meta delete pkg_name
- pkg summary
- pkg close
+ pkg install pkg_fmri
+ pkg uninstall pkg_fmri
+ pkg freeze [--version version_spec] [--release] [--branch] pkg_fmri
+ pkg unfreeze pkg_fmri
Options:
--repo, -s
@@ -81,80 +88,24 @@
# compare headers
-def trans_open(config, args):
- opts = None
- pargs = None
- try:
- opts, pargs = getopt.getopt(args, "e")
- except:
- print "pkg: illegal open option(s)"
- usage()
-
- eval_form = False
- for opt, arg in opts:
- if opt == "-e":
- eval_form = True
+def install(config, args):
+ """Attempt to take package specified to INSTALLED state."""
+ return
- if len(pargs) != 1:
- print "pkg: open requires one package name"
- usage()
-
- # POST /open/pkg_name
- repo = config.install_uri
- uri = urlparse.urljoin(repo, "open/%s" % pargs[0])
-
- c = urllib2.urlopen(uri)
-
- lines = c.readlines()
- for line in lines:
- if re.match("^Transaction-ID:", line):
- m = re.match("^Transaction-ID: (.*)", line)
- if eval_form:
- print "export PKG_TRANS_ID=%s" % m.group(1)
- else:
- print m.group(1)
-
+def uninstall(config, args):
+ """Attempt to take package specified to DELETED state."""
return
-def trans_close(config, args):
- # XXX alternately args contains -t trans
- trans_id = os.environ["PKG_TRANS_ID"]
- repo = config.install_uri
- uri = urlparse.urljoin(repo, "close/%s" % trans_id)
- try:
- c = urllib2.urlopen(uri)
- except urllib2.HTTPError:
- print "pkg: transaction close failed"
- sys.exit(1)
-
-def trans_add(config, args):
- """POST the file contents to the transaction. Default is to post to the
- currently open content series. -s option selects a different series."""
-
- if not args[0] in ["file", "link", "package"]:
- print "pkg: unknown add object '%s'" % args[0]
- usage()
+def freeze(config, args):
+ """Attempt to take package specified to FROZEN state, with given
+ restrictions."""
+ return
- trans_id = os.environ["PKG_TRANS_ID"]
- repo = config.install_uri
- uri_exp = urlparse.urlparse(repo)
- host, port = re.split(":", uri_exp[1])
- selector = "/add/%s/%s" % (trans_id, args[0])
+def unfreeze(config, args):
+ """Attempt to return package specified to INSTALLED state from FROZEN state."""
+ return
- if args[0] == "file":
- # XXX Need to handle larger files than available swap.
- file = open(args[2])
- data = file.read()
- else:
- sys.exit(99)
-
- headers = {}
- headers["Path"] = args[1]
-
- c = httplib.HTTPConnection(host, port)
- c.connect()
- c.request("POST", selector, data, headers)
-
+# XXX need an Image configuration by default
pcfg = ParentRepo("http://localhost:10000", ["http://localhost:10000"])
@@ -176,12 +127,14 @@
if subcommand == "catalog":
catalog(pcfg, pargs)
- elif subcommand == "open":
- trans_open(pcfg, pargs)
- elif subcommand == "close":
- trans_close(pcfg, pargs)
- elif subcommand == "add":
- trans_add(pcfg, pargs)
+ elif subcommand == "install":
+ install(pcfg, pargs)
+ elif subcommand == "uninstall":
+ uninstall(pcfg, pargs)
+ elif subcommand == "freeze":
+ freeze(pcfg, pargs)
+ elif subcommand == "unfreeze":
+ unfreeze(pcfg, pargs)
else:
print "pkg: unknown subcommand '%s'" % pargs[0]
usage()
--- a/src/depot.py Tue Apr 03 14:46:40 2007 -0700
+++ b/src/depot.py Wed Apr 04 18:26:56 2007 -0700
@@ -7,7 +7,20 @@
import shutil
import time
+import pkg.version as version
+import pkg.fmri as fmri
+import pkg.catalog as catalog
+import pkg.config as config
+
def catalog(scfg, request):
+ """The marshalled form of the catalog is
+
+ pkg_name (release (branch (sequence ...) ...) ...)
+
+ since we know that the server is only to report packages for which it
+ can offer a record.
+ """
+
request.send_response(200)
request.send_header('Content-type:', 'text/plain')
request.end_headers()
@@ -23,13 +36,16 @@
pass
opening_time = time.time()
m = re.match("^/open/(.*)", request.path)
- pkg = m.group(1)
+ pkg_name = m.group(1)
# XXX opaquify using hash
- trans_basename = "%d_%s" % (opening_time, pkg)
+ trans_basename = "%d_%s" % (opening_time, pkg_name)
os.makedirs("%s/%s" % (trans_root, trans_basename))
# record transaction metadata: opening_time, package, user
+ # lookup package by name
+ # if not found, create package
+ # set package state to TRANSACTING
request.send_response(200)
request.send_header('Content-type:', 'text/plain')
@@ -43,6 +59,12 @@
trans_root = "%s/trans" % scfg.repo_root
# XXX refine try/except
+ #
+ # set package state to SUBMITTED
+ # attempt to reconcile dependencies
+ # if reconciled, set state to PUBLISHED
+ # call back to check incomplete list
+ # else set state to INCOMPLETE
try:
shutil.rmtree("%s/%s" % (trans_root, trans_id))
request.send_response(200)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pkg-modules/catalog.py Wed Apr 04 18:26:56 2007 -0700
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+class Catalog(object):
+ """A Catalog is the representation of the package FMRIs available to
+ this client or repository. Both purposes utilize the same storage
+ format."""
+
+ def __init__(self, authority, catalog_root):
+ self.authority = authority
+ self.catalog_root = catalog_root
+
+ # XXX We should try to open the directory, so that we fail
+ # early.
+
+ def add_package_fmri(self, pkg_fmri):
+ return
+
+ def delete_package_fmri(self, pkg_fmri):
+ return
+
+ def to_string(self):
+ """Return the catalog in its marshallable format."""
+ return ""
+
+ def from_string(self, str):
+ """Parse the given string back into the on-disk catalog."""
+ return
+
+ def difference(self, catalog):
+ """Return a pair of lists, the first list being those package
+ FMRIs present in the current object but not in the presented
+ catalog, the second being those present in the presented catalog
+ but not in the current catalog."""
+ return
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pkg-modules/content.py Wed Apr 04 18:26:56 2007 -0700
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+class Content(object):
+ """A Content object is a piece of one or more Packages. The simplest
+ example of a Content object is a file.
+
+ XXX Is an Action a Content?
+
+ XXX Bad name."""
+
+ def __init__(self, type, name, resource):
+ self.type = type
+ self.name = name
+ self.resource = version
+
+ def set_resource(self, resource):
+ return
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pkg-modules/dependency.py Wed Apr 04 18:26:56 2007 -0700
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+REQUIRE = 0
+INCORPORATE = 1
+
+class Dependency(object):
+ """A Dependency object is a relationship between one Package and
+ another. It is a bidirectional expression.
+
+ A package may require a minimum version of another package."""
+
+ def __init__(self, host_pkg_fmri, req_pkg_fmri, type = REQUIRE):
+ self.host_pkg_fmri = host_pkg_fmri
+ self.req_pkg_fmri = req_pkg_fmri
+
+ assert type == REQUIRE || type == INCORPORATE
+ self.type = type
+
+ def satisfied(self, pkg_fmri):
+ # compare pkg_fmri to req_pkg_fmri
+ # compare versions
+ return False
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pkg-modules/package.py Wed Apr 04 18:26:56 2007 -0700
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+class Package(object):
+ """A Package is the node in the package graph. It consists of the
+ versioning data and authority required to construct a legitimate FMRI,
+ its dependencies and incorporations of other package FMRIs, and the
+ contents metadata used to request and install its extended content.
+
+ The dependencies are presented as a list of Dependency objects.
+
+ The contents are presented as a list of Contents objects."""
+
+ def __init__(self, authority, name, version, dependencies, contents):
+ self.authority = authority
+ self.name = name
+ self.version = version
+ self.dependencies = dependencies
+ self.contents = contents
+
+ # XXX We should try to open the directory, so that we fail
+ # early.
+
+ def add_content(self, content):
+ return
+
+ def add_dependency(self, dependency):
+ return
+
+# XXX PackageHistory or PackageSequence class? Or is it sufficient to have a
+# content_differences(self, pkg) in the Package class?
--- a/src/pkg-modules/version.py Tue Apr 03 14:46:40 2007 -0700
+++ b/src/pkg-modules/version.py Wed Apr 04 18:26:56 2007 -0700
@@ -85,12 +85,12 @@
self.args = args
class Version(object):
- """Version format is release,branch.sequence, which we decompose
+ """Version format is release,branch:sequence, which we decompose
into a DotSequence and branch and sequence values."""
def __init__(self, version_string):
# XXX If illegally formatted, raise exception.
- m = re.match("([\.\d]*),(\d*)\.(\d*)", version_string)
+ m = re.match("([\.\d]*),(\d*)\:(\d*)", version_string)
if m != None:
self.release = DotSequence(m.group(1))
self.branch = int(m.group(2))
@@ -116,7 +116,7 @@
raise IllegalVersion
def __str__(self):
- return "%s,%s.%s" % (self.release, self.branch, self.sequence)
+ return "%s,%s:%s" % (self.release, self.branch, self.sequence)
def __ne__(self, other):
if self.release == other.release and \
@@ -163,8 +163,8 @@
d2 = DotSequence("1.1.3")
assert d1 == d2
- v1 = Version("5.5.1,10.6")
- v2 = Version("5.5.1,10.8")
+ v1 = Version("5.5.1,10:6")
+ v2 = Version("5.5.1,10:8")
v3 = Version("5.5.1,10")
v4 = Version("5.5.1,6")
v5 = Version("5.6,1")