Index: chromite/lib/binpkg.py |
diff --git a/chromite/lib/binpkg.py b/chromite/lib/binpkg.py |
deleted file mode 100644 |
index a594571cfa13a5c393a0c11413a0dc8a13c781db..0000000000000000000000000000000000000000 |
--- a/chromite/lib/binpkg.py |
+++ /dev/null |
@@ -1,309 +0,0 @@ |
-# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
-# |
-# Adapted from portage/getbinpkg.py -- Portage binary-package helper functions |
-# Copyright 2003-2004 Gentoo Foundation |
-# Distributed under the terms of the GNU General Public License v2 |
- |
-import operator |
-import os |
-import tempfile |
-import time |
-import urllib |
-import urllib2 |
- |
-class PackageIndex(object): |
- """A parser for the Portage Packages index file. |
- |
- The Portage Packages index file serves to keep track of what packages are |
- included in a tree. It contains the following sections: |
- 1) The header. The header tracks general key/value pairs that don't apply |
- to any specific package. E.g., it tracks the base URL of the packages |
- file, and the number of packages included in the file. The header is |
- terminated by a blank line. |
- 2) The body. The body is a list of packages. Each package contains a list |
- of key/value pairs. Packages are either terminated by a blank line or |
- by the end of the file. Every package has a CPV entry, which serves as |
- a unique identifier for the package. |
- """ |
- |
- def __init__(self): |
- """Constructor.""" |
- |
- # The header tracks general key/value pairs that don't apply to any |
- # specific package. E.g., it tracks the base URL of the packages. |
- self.header = {} |
- |
- # A list of packages (stored as a list of dictionaries). |
- self.packages = [] |
- |
- # Whether or not the PackageIndex has been modified since the last time it |
- # was written. |
- self.modified = False |
- |
- def _PopulateDuplicateDB(self, db): |
- """Populate db with SHA1 -> URL mapping for packages. |
- |
- Args: |
- db: Dictionary to populate with SHA1 -> URL mapping for packages. |
- """ |
- |
- uri = self.header['URI'] |
- for pkg in self.packages: |
- cpv, sha1 = pkg['CPV'], pkg.get('SHA1') |
- if sha1: |
- path = pkg.get('PATH', urllib.quote(cpv + '.tbz2')) |
- db[sha1] = '%s/%s' % (uri.rstrip('/'), path) |
- |
- def _ReadPkgIndex(self, pkgfile): |
- """Read a list of key/value pairs from the Packages file into a dictionary. |
- |
- Both header entries and package entries are lists of key/value pairs, so |
- they can both be read by this function. Entries can be terminated by empty |
- lines or by the end of the file. |
- |
- This function will read lines from the specified file until it encounters |
- the a blank line or the end of the file. |
- |
- Keys and values in the Packages file are separated by a colon and a space. |
- Keys may contain capital letters, numbers, and underscores, but may not |
- contain colons. Values may contain any character except a newline. In |
- particular, it is normal for values to contain colons. |
- |
- Lines that have content, and do not contain a valid key/value pair, are |
- ignored. This is for compatibility with the Portage package parser, and |
- to allow for future extensions to the Packages file format. |
- |
- All entries must contain at least one key/value pair. If the end of the |
- fils is reached, an empty dictionary is returned. |
- |
- Args: |
- pkgfile: A python file object. |
- |
- Returns the dictionary of key-value pairs that was read from the file. |
- """ |
- d = {} |
- for line in pkgfile: |
- line = line.rstrip('\n') |
- if not line: |
- assert d, 'Packages entry must contain at least one key/value pair' |
- break |
- line = line.split(': ', 1) |
- if len(line) == 2: |
- k, v = line |
- d[k] = v |
- return d |
- |
- def _WritePkgIndex(self, pkgfile, entry): |
- """Write header entry or package entry to packages file. |
- |
- The keys and values will be separated by a colon and a space. The entry |
- will be terminated by a blank line. |
- |
- Args: |
- pkgfile: A python file object. |
- entry: A dictionary of the key/value pairs to write. |
- """ |
- lines = ['%s: %s' % (k, v) for k, v in sorted(entry.items()) if v] |
- pkgfile.write('%s\n\n' % '\n'.join(lines)) |
- |
- def _ReadHeader(self, pkgfile): |
- """Read header of packages file. |
- |
- Args: |
- pkgfile: A python file object. |
- """ |
- assert not self.header, 'Should only read header once.' |
- self.header = self._ReadPkgIndex(pkgfile) |
- |
- def _ReadBody(self, pkgfile): |
- """Read body of packages file. |
- |
- Before calling this function, you must first read the header (using |
- _ReadHeader). |
- |
- Args: |
- pkgfile: A python file object. |
- """ |
- assert self.header, 'Should read header first.' |
- assert not self.packages, 'Should only read body once.' |
- |
- # Read all of the sections in the body by looping until we reach the end |
- # of the file. |
- while True: |
- d = self._ReadPkgIndex(pkgfile) |
- if not d: |
- break |
- if 'CPV' in d: |
- self.packages.append(d) |
- |
- def Read(self, pkgfile): |
- """Read the entire packages file. |
- |
- Args: |
- pkgfile: A python file object. |
- """ |
- self._ReadHeader(pkgfile) |
- self._ReadBody(pkgfile) |
- |
- def RemoveFilteredPackages(self, filter_fn): |
- """Remove packages which match filter_fn. |
- |
- Args: |
- filter_fn: A function which operates on packages. If it returns True, |
- the package should be removed. |
- """ |
- |
- filtered = [p for p in self.packages if not filter_fn(p)] |
- if filtered != self.packages: |
- self.modified = True |
- self.packages = filtered |
- |
- def ResolveDuplicateUploads(self, pkgindexes): |
- """Point packages at files that have already been uploaded. |
- |
- For each package in our index, check if there is an existing package that |
- has already been uploaded to the same base URI. If so, point that package |
- at the existing file, so that we don't have to upload the file. |
- |
- Args: |
- pkgindexes: A list of PackageIndex objects containing info about packages |
- that have already been uploaded. |
- |
- Returns: |
- A list of the packages that still need to be uploaded. |
- """ |
- db = {} |
- for pkgindex in pkgindexes: |
- pkgindex._PopulateDuplicateDB(db) |
- |
- uploads = [] |
- base_uri = self.header['URI'] |
- for pkg in self.packages: |
- sha1 = pkg.get('SHA1') |
- uri = db.get(sha1) |
- if sha1 and uri and uri.startswith(base_uri): |
- pkg['PATH'] = uri[len(base_uri):].lstrip('/') |
- else: |
- uploads.append(pkg) |
- return uploads |
- |
- def SetUploadLocation(self, base_uri, path_prefix): |
- """Set upload location to base_uri + path_prefix. |
- |
- Args: |
- base_uri: Base URI for all packages in the file. We set |
- self.header['URI'] to this value, so all packages must live under |
- this directory. |
- path_prefix: Path prefix to use for all current packages in the file. |
- This will be added to the beginning of the path for every package. |
- """ |
- self.header['URI'] = base_uri |
- for pkg in self.packages: |
- path = urllib.quote(pkg['CPV'] + '.tbz2') |
- pkg['PATH'] = '%s/%s' % (path_prefix.rstrip('/'), path) |
- |
- def Write(self, pkgfile): |
- """Write a packages file to disk. |
- |
- If 'modified' flag is set, the TIMESTAMP and PACKAGES fields in the header |
- will be updated before writing to disk. |
- |
- Args: |
- pkgfile: A python file object. |
- """ |
- if self.modified: |
- self.header['TIMESTAMP'] = str(long(time.time())) |
- self.header['PACKAGES'] = str(len(self.packages)) |
- self.modified = False |
- self._WritePkgIndex(pkgfile, self.header) |
- for metadata in sorted(self.packages, key=operator.itemgetter('CPV')): |
- self._WritePkgIndex(pkgfile, metadata) |
- |
- def WriteToNamedTemporaryFile(self): |
- """Write pkgindex to a temporary file. |
- |
- Args: |
- pkgindex: The PackageIndex object. |
- |
- Returns: |
- A temporary file containing the packages from pkgindex. |
- """ |
- f = tempfile.NamedTemporaryFile() |
- self.Write(f) |
- f.flush() |
- f.seek(0) |
- return f |
- |
- |
-def _RetryUrlOpen(url, tries=3): |
- """Open the specified url, retrying if we run into temporary errors. |
- |
- We retry for both network errors and 5xx Server Errors. We do not retry |
- for HTTP errors with a non-5xx code. |
- |
- Args: |
- url: The specified url. |
- tries: The number of times to try. |
- |
- Returns: |
- The result of urllib2.urlopen(url). |
- """ |
- for i in range(tries): |
- try: |
- return urllib2.urlopen(url) |
- except urllib2.HTTPError as e: |
- if i + 1 >= tries or e.code < 500: |
- raise |
- else: |
- print 'Cannot GET %s: %s' % (url, str(e)) |
- except urllib2.URLError as e: |
- if i + 1 >= tries: |
- raise |
- else: |
- print 'Cannot GET %s: %s' % (url, str(e)) |
- print 'Sleeping for 10 seconds before retrying...' |
- time.sleep(10) |
- |
- |
-def GrabRemotePackageIndex(binhost_url): |
- """Grab the latest binary package database from the specified URL. |
- |
- Args: |
- binhost_url: Base URL of remote packages (PORTAGE_BINHOST). |
- |
- Returns: |
- A PackageIndex object, if the Packages file can be retrieved. If the |
- server returns status code 404, None is returned. |
- """ |
- |
- url = '%s/Packages' % binhost_url.rstrip('/') |
- try: |
- f = _RetryUrlOpen(url) |
- except urllib2.HTTPError as e: |
- if e.code == 404: |
- return None |
- raise |
- |
- pkgindex = PackageIndex() |
- pkgindex.Read(f) |
- pkgindex.header.setdefault('URI', binhost_url) |
- f.close() |
- return pkgindex |
- |
- |
-def GrabLocalPackageIndex(package_path): |
- """Read a local packages file from disk into a PackageIndex() object. |
- |
- Args: |
- package_path: Directory containing Packages file. |
- |
- Returns: |
- A PackageIndex object. |
- """ |
- packages_file = file(os.path.join(package_path, 'Packages')) |
- pkgindex = PackageIndex() |
- pkgindex.Read(packages_file) |
- packages_file.close() |
- return pkgindex |