[yum-git] Branch 'yum-3_2_X' - yum/metalink.py

James Antill james at linux.duke.edu
Wed Aug 13 19:47:19 UTC 2008


 yum/metalink.py |  251 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 251 insertions(+)

New commits:
commit 478c407d1779613133ea76ef2d1ff1118b68b753
Author: James Antill <james at and.org>
Date:   Wed Aug 13 15:24:22 2008 -0400

     First attempt at a parser for the new MirrorManager MetaLink output.
     Anyone relying on this API atm. is liable for a free stabbing.
    
     This just parses the data into a usable form, hooking it into
    yumRepos is not done, neither is the metalink= [urls] repo. config.
    variable.

diff --git a/yum/metalink.py b/yum/metalink.py
new file mode 100755
index 0000000..9aa210a
--- /dev/null
+++ b/yum/metalink.py
@@ -0,0 +1,251 @@
+#!/usr/bin/python -t
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2008 Red Hat
+#
+# James Antill <james at fedoraproject.org>
+
+# Parse the new MirrorManager metalink output:
+
+import sys
+print >>sys.stderr, "Warning: Relying on the API to be stable is not recommended, yet."
+
+import os
+import time
+from urlgrabber.progress import format_number
+
+try:
+    from xml.etree import cElementTree
+except ImportError:
+    import cElementTree
+xmlparse = cElementTree.parse
+
+class MetaLinkRepoErrorParseFail:
+    """ An exception thrown for an unparsable MetaLinkRepoMD file. """
+    pass
+
+__XML_NS_ML__ = 'http://www.metalinker.org/'
+__XML_NS_MM__ = 'http://fedorahosted.org/mirrormanager'
+__XML_FMT__   = {'ml' : __XML_NS_ML__,
+                 'mm' : __XML_NS_MM__}
+
+__ML_FILE_ELEMENT__ = """\
+{%(ml)s}files/{%(ml)s}file\
+""" % __XML_FMT__
+__ML_OLD_FILE_ELEMENTS__ = """\
+{%(mm)s}alternates/{%(mm)s}alternate\
+""" % __XML_FMT__
+__ML_RESOURCES__ = """\
+{%(ml)s}resources\
+""" % __XML_FMT__
+
+class MetaLinkFile:
+    """ Parse the file metadata out of a metalink file. """
+
+    def __init__(self, elem):
+        chksums = set(["md5", 'sha1', 'sha256', 'sha512'])
+
+        for celem in elem:
+            if False: pass
+            elif celem.tag == "{%s}timestamp" % __XML_NS_MM__:
+                self.timestamp = int(celem.text)
+            elif celem.tag == "{%s}size" % __XML_NS_ML__:
+                self.size = int(celem.text)
+            elif celem.tag == "{%s}verification" % __XML_NS_ML__:
+                self.chksums = {}
+                for helem in celem:
+                    if (helem.tag == "{%s}hash"  % __XML_NS_ML__ and
+                        helem.get("type") in chksums):
+                        self.chksums[helem.get("type").lower()] = helem.text
+
+        if not hasattr(self, 'timestamp'):
+            raise MetaLinkRepoErrorParseFail, "No timestamp for file"
+        if not hasattr(self, 'size'):
+            raise MetaLinkRepoErrorParseFail, "No size for file"
+        if not hasattr(self, 'chksums'):
+            raise MetaLinkRepoErrorParseFail, "No verifications for file"
+
+    def __str__(self):
+        return """\
+Timestamp: %s
+Size:      %5s (%d)
+MD5:       %s
+SHA1:      %s
+SHA256:    %s
+SHA512:    %s
+""" % (time.ctime(self.timestamp), format_number(self.size), self.size,
+       self.md5, self.sha1, self.sha256, self.sha512)
+
+    def _get_md5(self):
+        return self.chksums.get('md5', '')
+    md5 = property(_get_md5)
+    def _get_sha1(self):
+        return self.chksums.get('sha1', '')
+    sha1 = property(_get_sha1)
+    def _get_sha256(self):
+        return self.chksums.get('sha256', '')
+    sha256 = property(_get_sha256)
+    def _get_sha512(self):
+        return self.chksums.get('sha512', '')
+    sha512 = property(_get_sha512)
+
+    def __cmp__(self, other):
+        if other is None:
+            return 1
+        ret = cmp(self.timestamp, other.timestamp)
+        if ret:
+            return -ret
+        ret = cmp(self.size, other.size)
+        if ret:
+            return ret
+        ret = cmp(self.md5, other.md5)
+        if ret:
+            return ret
+        ret = cmp(self.sha1, other.sha1)
+        if ret:
+            return ret
+        ret = cmp(self.sha256, other.sha256)
+        if ret:
+            return ret
+        ret = cmp(self.sha512, other.sha512)
+        if ret:
+            return ret
+        return 0
+
+
+class MetaLinkURL:
+    """ Parse the URL metadata out of a metalink file. """
+
+    def __init__(self, elem, max_connections):
+        assert elem.tag == '{%s}url' % __XML_NS_ML__
+
+        self.max_connections = max_connections
+
+        self.url        = elem.text
+        self.preference = int(elem.get("preference", -1))
+        self.protocol   = elem.get("protocol")
+        self.location   = elem.get("location")
+
+    def __str__(self):
+        return """\
+URL:             %s
+Preference:      %d
+Max-Connections: %d
+Protocol:        %s
+Location:        %s
+""" % (self.url, self.preference, self.max_connections,
+       self.protocol, self.location)
+
+    def __cmp__(self, other):
+        if other is None:
+            return 1
+        ret = cmp(self.preference, other.preference)
+        if ret:
+            return -ret
+        ret = cmp(self.protocol == "https", other.protocol == "https")
+        if ret:
+            return -ret
+        ret = cmp(self.protocol == "http", other.protocol == "http")
+        if ret:
+            return -ret
+        return cmp(self.url, other.url)
+
+    def usable(self):
+        if self.protocol is None:
+            return False
+        if not self.url:
+            return False
+        return True
+
+class MetaLinkRepoMD:
+    """ Parse a metalink file for repomd.xml. """
+
+    def __init__(self, filename):
+        self.name   = None
+        self.repomd = None
+        self.old_repomds = []
+        self.mirrors = []
+        root = xmlparse(filename)
+
+        for elem in root.findall(__ML_FILE_ELEMENT__):
+            name = elem.get('name')
+            if os.path.basename(name) != 'repomd.xml':
+                continue
+
+            if self.name is not None and self.name != name:
+                raise MetaLinkRepoErrorParseFail, "Different paths for repomd file"
+            self.name = name
+
+            repomd = MetaLinkFile(elem)
+
+            if self.repomd is not None and self.repomd != repomd:
+                raise MetaLinkRepoErrorParseFail, "Different data for repomd file"
+            self.repomd = repomd
+
+            for celem in elem.findall(__ML_OLD_FILE_ELEMENTS__):
+                self.old_repomds.append(MetaLinkFile(celem))
+
+            for celem in elem.findall(__ML_RESOURCES__):
+                max_connections = int(celem.get("maxconnections"))
+                for uelem in celem:
+                    if uelem.tag == "{%s}url"  % __XML_NS_ML__:
+                        self.mirrors.append(MetaLinkURL(uelem, max_connections))
+
+        self.old_repomds.sort()
+        self.mirrors.sort()
+
+        if self.repomd is None:
+            raise MetaLinkRepoErrorParseFail, "No repomd file"
+        if len(self.mirrors) < 1:
+            raise MetaLinkRepoErrorParseFail, "No mirror"
+
+    def __str__(self):
+        ret = str(self.repomd)
+        done = False
+        for orepomd in self.old_repomds:
+            if not done: ret += "%s\n" % ("-" * 79)
+            if done:     ret += "\n"
+            done = True
+            ret += str(orepomd)
+        done = False
+        for url in self.mirrors:
+            if not done: ret += "%s\n" % ("-" * 79)
+            if done:     ret += "\n"
+            done = True
+            ret += str(url)
+        return ret
+
+
+def main():
+    """ MetaLinkRepoMD test function. """
+
+    def usage():
+        print >> sys.stderr, "Usage: %s <metalink> ..." % sys.argv[0]
+        sys.exit(1)
+
+    if len(sys.argv) < 2:
+        usage()
+
+    for filename in sys.argv[1:]:
+        if not os.path.exists(filename):
+            print "No such file:", filename
+            continue
+
+        print "File:", filename
+        print MetaLinkRepoMD(filename)
+        print ''
+
+if __name__ == '__main__':
+    main()



More information about the Yum-cvs-commits mailing list