depot.py revision 37
0N/A#!/usr/bin/python
20N/A#
20N/A# CDDL HEADER START
20N/A#
20N/A# The contents of this file are subject to the terms of the
20N/A# Common Development and Distribution License (the "License").
20N/A# You may not use this file except in compliance with the License.
20N/A#
20N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
20N/A# or http://www.opensolaris.org/os/licensing.
20N/A# See the License for the specific language governing permissions
20N/A# and limitations under the License.
20N/A#
20N/A# When distributing Covered Code, include this CDDL HEADER in each
20N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
20N/A# If applicable, add the following below this CDDL HEADER, with the
20N/A# fields enclosed by brackets "[]" replaced with your own identifying
20N/A# information: Portions Copyright [yyyy] [name of copyright owner]
20N/A#
20N/A# CDDL HEADER END
20N/A#
20N/A# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
20N/A# Use is subject to license terms.
20N/A#
20N/A
22N/A# pkg.depotd - package repository daemon
0N/A
0N/Aimport BaseHTTPServer
26N/Aimport getopt
0N/Aimport os
0N/Aimport re
0N/Aimport sha
0N/Aimport shutil
0N/Aimport time
22N/Aimport urllib
0N/A
34N/Aimport pkg.catalog as catalog
22N/Aimport pkg.content as content
22N/Aimport pkg.dependency as dependency
22N/Aimport pkg.fmri as fmri
34N/Aimport pkg.package as package
22N/Aimport pkg.version as version
22N/A
26N/Aimport pkg.server.config as config
23N/Aimport pkg.server.transaction as trans
23N/A
26N/Adef usage():
26N/A print """\
26N/AUsage: /usr/lib/pkg.depotd [-n]
26N/A"""
14N/A
0N/Adef catalog(scfg, request):
30N/A scfg.inc_catalog()
14N/A
0N/A request.send_response(200)
25N/A request.send_header('Content-type', 'text/plain')
0N/A request.end_headers()
26N/A request.wfile.write("%s" % scfg.catalog)
26N/A
30N/Adef manifest(scfg, request):
30N/A """The request is an encoded pkg FMRI. If the version is specified
30N/A incompletely, we return the latest matching manifest. The matched
30N/A version is returned in the headers. If an incomplete FMRI is received,
30N/A the client is expected to have provided a build release in the request
30N/A headers.
30N/A
30N/A Legitimate requests are
30N/A
30N/A /manifest/[URL]
30N/A /manifest/branch/[URL]
30N/A /manifest/release/[URL]
30N/A
30N/A allowing the request of the next matching version, based on client
30N/A constraints."""
30N/A
30N/A scfg.inc_manifest()
30N/A
30N/A constraint = None
30N/A
30N/A # Parse request into FMRI component and decode.
30N/A m = re.match("^/manifest/(.*)", request.path)
30N/A pfmri = urllib.unquote(m.group(1))
30N/A
30N/A # Match package stem.
30N/A f = fmri.PkgFmri(pfmri, None)
30N/A
30N/A # Determine closest version.
30N/A vs = scfg.catalog.matching_pkgs(f, constraint)
30N/A
30N/A msg = "Request for %s: " % pfmri
30N/A for v in vs:
30N/A msg = msg + "%s " % v
30N/A
30N/A request.log_message(msg)
30N/A
30N/A # Open manifest and send.
30N/A # file = open("%s/%s/%s", scfg.pkg_root, pkgname, pkgversion)
30N/A # data = file.read()
30N/A
30N/A request.send_response(200)
30N/A request.send_header('Content-type', 'text/plain')
30N/A request.end_headers()
30N/A # request.wfile.write(data)
30N/A
30N/Adef get_file(scfg, request):
30N/A """The request is the SHA-1 hash name for the file."""
30N/A scfg.inc_file()
30N/A
30N/A m = re.match("^/file/(.*)", request.path)
30N/A fhash = m.group(1)
30N/A
30N/A file = open(scfg.file_root + "/" + fhash)
30N/A data = file.read()
30N/A
30N/A request.send_response(200)
30N/A request.send_header("Content-type", "application/data")
30N/A request.end_headers()
30N/A request.wfile.write(data)
0N/A
0N/Adef trans_open(scfg, request):
22N/A # XXX Authentication will be handled by virtue of possessing a signed
22N/A # certificate (or a more elaborate system).
22N/A t = trans.Transaction()
22N/A
22N/A ret = t.open(scfg, request)
22N/A if ret == 200:
26N/A scfg.in_flight_trans[t.get_basename()] = t
0N/A
22N/A request.send_response(200)
22N/A request.send_header('Content-type', 'text/plain')
22N/A request.send_header('Transaction-ID', t.get_basename())
22N/A request.end_headers()
22N/A elif ret == 400:
22N/A request.send_response(400)
22N/A else:
22N/A request.send_response(500)
0N/A
0N/A
0N/Adef trans_close(scfg, request):
0N/A # Pull transaction ID from headers.
0N/A m = re.match("^/close/(.*)", request.path)
0N/A trans_id = m.group(1)
0N/A
22N/A # XXX KeyError?
26N/A t = scfg.in_flight_trans[trans_id]
22N/A t.close(request)
26N/A del scfg.in_flight_trans[trans_id]
22N/A
22N/Adef trans_abandon(scfg, request):
22N/A # Pull transaction ID from headers.
22N/A m = re.match("^/abandon/(.*)", request.path)
22N/A trans_id = m.group(1)
22N/A
26N/A t = scfg.in_flight_trans[trans_id]
22N/A t.abandon(request)
26N/A del scfg.in_flight_trans[trans_id]
0N/A
0N/Adef trans_add(scfg, request):
0N/A m = re.match("^/add/([^/]*)/(.*)", request.path)
0N/A trans_id = m.group(1)
0N/A type = m.group(2)
0N/A
26N/A t = scfg.in_flight_trans[trans_id]
22N/A t.add_content(request, type)
0N/A
20N/Aif "PKG_REPO" in os.environ:
34N/A scfg = config.SvrConfig(os.environ["PKG_REPO"], "pkg.sun.com")
21N/Aelse:
34N/A scfg = config.SvrConfig("/var/pkg/repo", "pkg.sun.com")
0N/A
0N/Aclass pkgHandler(BaseHTTPServer.BaseHTTPRequestHandler):
0N/A
0N/A def do_GET(self):
26N/A # Client APIs
0N/A if re.match("^/catalog$", self.path):
0N/A catalog(scfg, self)
26N/A elif re.match("^/manifest/.*$", self.path):
26N/A manifest(scfg, self)
30N/A elif re.match("^/file/.*$", self.path):
30N/A get_file(scfg, self)
26N/A
26N/A # Publisher APIs
0N/A elif re.match("^/open/(.*)$", self.path):
0N/A trans_open(scfg, self)
0N/A elif re.match("^/close/(.*)$", self.path):
0N/A trans_close(scfg, self)
22N/A elif re.match("^/abandon/(.*)$", self.path):
22N/A trans_abandon(scfg, self)
0N/A elif re.match("^/add/(.*)$", self.path):
0N/A trans_add(scfg, self)
26N/A
26N/A # Informational APIs
26N/A elif re.match("^/$", self.path) or re.match("^/index.html",
26N/A self.path):
22N/A self.send_response(200)
22N/A self.send_header('Content-type', 'text/html')
22N/A self.end_headers()
26N/A self.wfile.write("""\
26N/A<html>
26N/A<body>
26N/A<h1><code>pkg</code> server ok</h1>
37N/A<h2>Statistics</h2>
26N/A<pre>
26N/A""")
26N/A self.wfile.write(scfg.get_status())
26N/A self.wfile.write("""\
26N/A</pre>
37N/A<h2>Catalog</h2>
37N/A<pre>
37N/A""")
37N/A self.wfile.write("%s" % scfg.catalog)
37N/A self.wfile.write("""\
37N/A</pre>
26N/A</body>
26N/A</html>""")
0N/A else:
0N/A self.send_response(404)
22N/A self.send_header('Content-type', 'text/plain')
22N/A self.end_headers()
30N/A self.wfile.write('''404 GET URI %s ; headers %s''' %
30N/A (self.path, self.headers))
0N/A
0N/A
0N/A def do_PUT(self):
0N/A self.send_response(200)
25N/A self.send_header('Content-type', 'text/plain')
0N/A self.end_headers()
30N/A self.wfile.write('''PUT URI %s ; headers %s''' %
30N/A (self.path, self.headers))
0N/A
0N/A def do_POST(self):
0N/A if re.match("^/add/(.*)$", self.path):
0N/A trans_add(scfg, self)
0N/A else:
0N/A self.send_response(404)
0N/A
0N/A def do_DELETE(self):
0N/A self.send_response(200)
25N/A self.send_header('Content-type', 'text/plain')
0N/A self.end_headers()
30N/A self.wfile.write('''URI %s ; headers %s''' %
30N/A (self.path, self.headers))
0N/A
22N/Aif __name__ == "__main__":
22N/A scfg.init_dirs()
26N/A scfg.acquire_in_flight()
26N/A scfg.acquire_catalog()
26N/A
26N/A try:
26N/A opts, pargs = getopt.getopt(sys.argv[1:], "n")
26N/A for opt, arg in opts:
26N/A if opt == "-n":
26N/A sys.exit(0)
26N/A except:
26N/A print "pkg.depotd: unknown option"
26N/A usage()
26N/A
22N/A server = BaseHTTPServer.HTTPServer(('', 10000), pkgHandler)
22N/A server.serve_forever()