[yum-cvs] yum/yum update_md.py,1.2,1.3
Luke Macken
lmacken at linux.duke.edu
Wed Aug 9 14:44:09 UTC 2006
Update of /home/groups/yum/cvs/yum/yum
In directory login1.linux.duke.edu:/tmp/cvs-serv5798/yum
Modified Files:
update_md.py
Log Message:
2006-08-09 10:40 lmacken
* yum/update_md.py:
Rewrite to handle new metadata schema.
Allow scripted metadata access.
Accept YumRepository objects in add().
Cache notices for quick searching
* yum-updatesd.py:
Remove (most of) the old metadata retrieval code.
Try and grab the update 'type' from the metadata so we can check if we
are dealing with any security updates.
Index: update_md.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/update_md.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- update_md.py 13 Jun 2006 18:57:10 -0000 1.2
+++ update_md.py 9 Aug 2006 14:44:07 -0000 1.3
@@ -1,5 +1,4 @@
#!/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
@@ -14,178 +13,261 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Copyright 2005 Duke University
-
+#
+# Seth Vidal <skvidal at linux.duke.edu>
+# Luke Macken <lmacken at redhat.com>
import sys
-from cElementTree import iterparse
+import gzip
+import string
import exceptions
+from yum.yumRepo import YumRepository
+
+from cElementTree import iterparse
class UpdateNoticeException(exceptions.Exception):
pass
-
-
+
+
class UpdateNotice(object):
def __init__(self, elem=None):
- self.cves = []
- self.urls = []
- self.packages = []
- self.description = ''
- self.update_id = None
- self.distribution = None
- self.release_date = None
- self.status = None
- self.type = None
- self.title = ''
+ self._md = {
+ 'from' : '',
+ 'type' : '',
+ 'status' : '',
+ 'version' : '',
+ 'pushcount' : '',
+ 'update_id' : '',
+ 'issued' : '',
+ 'updated' : '',
+ 'description' : '',
+ 'references' : [],
+ 'pkglist' : [],
+ 'reboot_suggested' : False
+ }
if elem:
- self.parse(elem)
-
+ self._parse(elem)
+
+ def __getitem__(self, item):
+ """ Allows scriptable metadata access (ie: un['update_id']). """
+ return self._md.has_key(item) and self._md[item] or None
+
def __str__(self):
- cveinfo = pkglist = related = ''
-
head = """
-Type: %s
-Status: %s
-Distribution: %s
-ID: %s
-Release date: %s
-Description:
+Id : %s
+Type : %s
+Status : %s
+Issued : %s
+Updated : %s
+Description :
%s
- """ % (self.type, self.status, self.distribution,
- self.update_id, self.release_date, self.description)
+ """ % (self._md['update_id'], self._md['type'], self._md['status'],
+ self._md['issued'], self._md['updated'], self._md['description'])
+
+ refs = '\n== References ==\n'
+ for ref in self._md['references']:
+ type = ref['type']
+ if type == 'cve':
+ refs += '\n%s : %s\n%s\n' % (ref['id'], ref['href'],
+ ref.has_key('summary') and
+ ref['summary'] or '')
+ elif type == 'bugzilla':
+ refs += '\nBug #%s : %s\n%s\n' % (ref['id'], ref['href'],
+ ref.has_key('summary') and
+ ref['summary'] or '')
+
+ pkglist = '\n== Updated Packages ==\n'
+ for pkg in self._md['pkglist']:
+ pkglist += '\n%s\n' % pkg['name']
+ for file in pkg['packages']:
+ pkglist += ' %s %s\n' % (file['sum'][1], file['filename'])
+
+ msg = head + refs + pkglist
- if self.urls:
- related = '\nRelated URLS:\n'
- for url in self.urls:
- related = related + ' %s\n' % url
-
- if self.cves:
- cveinfo = '\nResolves CVES:\n'
- for cve in self.cves:
- cveinfo = cveinfo + ' %s\n' % cve
-
- if self.packages:
- pkglist = '\nPackages: \n'
- for pkg in self.packages:
- pkgstring = '%s-%s-%s.%s\t\t%s\n' % (pkg['name'], pkg['ver'],
- pkg['rel'], pkg['arch'],
- pkg['pkgid'])
- pkglist = pkglist + pkgstring
-
- msg = head + related + cveinfo + pkglist
-
return msg
- def parse(self, elem):
+ def get_metadata(self):
+ """ Return the metadata dict. """
+ return self._md
+
+ def _parse(self, elem):
+ """ Parse an update element.
+
+ <!ELEMENT update (id, pushcount, synopsis?, issued, updated,
+ references, description, pkglist)>
+ <!ATTLIST update type (errata|security) "errata">
+ <!ATTLIST update status (final|testing) "final">
+ <!ATTLIST update version CDATA #REQUIRED>
+ <!ATTLIST update from CDATA #REQUIRED>
+ """
if elem.tag == 'update':
- id = elem.attrib.get('id')
- if not id:
- raise UpdateNoticeException
- self.update_id = id
-
- self.release_date = elem.attrib.get('release_date')
- self.status = elem.attrib.get('status')
- c = elem.attrib.get('type')
- if not c:
- self.type = 'update'
+ for attrib in ('from', 'type', 'status', 'version'):
+ self._md[attrib] = elem.attrib.get(attrib)
+ for child in elem:
+ if child.tag == 'id':
+ if not child.text:
+ raise UpdateNoticeException
+ self._md['update_id'] = child.text
+ elif child.tag == 'pushcount':
+ self._md['pushcount'] = child.text
+ elif child.tag == 'issued':
+ self._md['issued'] = child.attrib.get('date')
+ elif child.tag == 'updated':
+ self._md['updated'] = child.attrib.get('date')
+ elif child.tag == 'references':
+ self._parse_references(child)
+ elif child.tag == 'description':
+ self._md['description'] = child.text
+ elif child.tag == 'pkglist':
+ self._parse_pkglist(child)
+ else:
+ raise UpdateNoticeException('No update element found')
+
+ def _parse_references(self, elem):
+ """ Parse the update references.
+
+ <!ELEMENT references (reference*)>
+ <!ELEMENT reference (summary*)>
+ <!ATTLIST reference href CDATA #REQUIRED>
+ <!ATTLIST reference type (self|cve|bugzilla) "self">
+ <!ATTLIST reference id CDATA #IMPLIED>
+ <!ELEMENT cve (#PCDATA)>
+ <!ELEMENT bugzilla (#PCDATA)>
+ <!ELEMENT summary (#PCDATA)>
+ <!ELEMENT description (#PCDATA)>
+ """
+ for reference in elem:
+ if reference.tag == 'reference':
+ data = {}
+ for refattrib in ('id', 'href', 'type'):
+ data[refattrib] = reference.attrib.get(refattrib)
+ for child in reference:
+ if child.tag == 'summary':
+ data['summary'] = child.text
+ self._md['references'].append(data)
else:
- self.type = c
+ raise UpdateNoticeException('No reference element found')
- for child in elem:
-
- if child.tag == 'cve':
- self.cves.append(child.text)
+ def _parse_pkglist(self, elem):
+ """ Parse the package list.
- elif child.tag == 'url':
- self.urls.append(child.text)
-
- elif child.tag == 'description':
- self.description = child.text
-
- elif child.tag == 'distribution':
- self.distribution = child.text
-
- elif child.tag == 'title':
- self.title = child.text
-
- elif child.tag == 'package':
- self.parse_package(child)
-
- def parse_package(self, elem):
-
- pkg = {}
- pkg['pkgid'] = elem.attrib.get('pkgid')
- pkg['name'] = elem.attrib.get('name')
- pkg['arch'] = elem.attrib.get('arch')
+ <!ELEMENT pkglist (collection+)>
+ <!ELEMENT collection (name?, package+)>
+ <!ATTLIST collection short CDATA #IMPLIED>
+ <!ATTLIST collection name CDATA #IMPLIED>
+ <!ELEMENT name (#PCDATA)>
+ """
+ for collection in elem:
+ data = { 'packages' : [] }
+ if collection.attrib.has_key('short'):
+ data['short'] = collection.attrib.get('short')
+ for item in collection:
+ if item.tag == 'name':
+ data['name'] = item.text
+ elif item.tag == 'package':
+ data['packages'].append(self._parse_package(item))
+ self._md['pkglist'].append(data)
+
+ def _parse_package(self, elem):
+ """ Parse an individual package.
+
+ <!ELEMENT package (filename, sum, reboot_suggested)>
+ <!ATTLIST package name CDATA #REQUIRED>
+ <!ATTLIST package version CDATA #REQUIRED>
+ <!ATTLIST package release CDATA #REQUIRED>
+ <!ATTLIST package arch CDATA #REQUIRED>
+ <!ATTLIST package epoch CDATA #REQUIRED>
+ <!ATTLIST package src CDATA #REQUIRED>
+ <!ELEMENT reboot_suggested (#PCDATA)>
+ <!ELEMENT filename (#PCDATA)>
+ <!ELEMENT sum (#PCDATA)>
+ <!ATTLIST sum type (md5|sha1) "sha1">
+ """
+ package = {}
+ for pkgfield in ('arch', 'epoch', 'name', 'version', 'release', 'src'):
+ package[pkgfield] = elem.attrib.get(pkgfield)
for child in elem:
- if child.tag == 'version':
- pkg['ver'] = child.attrib.get('ver')
- pkg['rel'] = child.attrib.get('rel')
- pkg['epoch'] = child.attrib.get('epoch')
-
- self.packages.append(pkg)
-
+ if child.tag == 'filename':
+ package['filename'] = child.text
+ elif child.tag == 'sum':
+ package['sum'] = (child.attrib.get('type'), child.text)
+ elif child.tag == 'reboot_suggested':
+ self._md['reboot_suggested'] = True
+ return package
class UpdateMetadata(object):
def __init__(self):
self._notices = {}
-
+ self._cache = {} # a pkg name => notice cache for quick lookups
+ self._repos = [] # list of repo ids that we've parsed
+
def get_notices(self):
+ """ Return all notices. """
return self._notices.values()
notices = property(get_notices)
def get_notice(self, nvr):
""" Retrieve an update notice for a given (name, version, release). """
- for notice in self._notices.values():
- for pkg in notice.packages:
- if pkg['name'] == nvr[0] and \
- pkg['ver'] == nvr[1] and \
- pkg['rel'] == nvr[2]:
- return notice
- return None
+ nvr = string.join(nvr, '-')
+ return self._cache.has_key(nvr) and self._cache[nvr] or None
- def add(self, srcfile):
- if not srcfile:
+ def add(self, obj, mdtype='updateinfo'):
+ """ Parse a metadata from a given YumRepository, file, or filename. """
+ if not obj:
raise UpdateNoticeException
-
- if type(srcfile) == type('str'):
- infile = open(srcfile, 'rt')
- else: # srcfile is a file object
- infile = srcfile
-
- parser = iterparse(infile)
+ if type(obj) == type('str'):
+ infile = obj.endswith('.gz') and gzip.open(obj) or open(obj, 'rt')
+ elif isinstance(obj, YumRepository):
+ if obj.id not in self._repos:
+ self._repos.append(obj.id)
+ md = obj.retrieveMD(mdtype)
+ if not md:
+ raise UpdateNoticeException()
+ infile = gzip.open(md)
+ else: # obj is a file object
+ infile = obj
- for event, elem in parser:
+ for event, elem in iterparse(infile):
if elem.tag == 'update':
un = UpdateNotice(elem)
- if not self._notices.has_key(un.update_id):
- self._notices[un.update_id] = un
-
-
- del parser
+ if not self._notices.has_key(un['update_id']):
+ self._notices[un['update_id']] = un
+ for pkg in un['pkglist']:
+ for file in pkg['packages']:
+ self._cache['%s-%s-%s' % (file['name'],
+ file['version'],
+ file['release'])] = un
- def dump(self):
+ def __str__(self):
+ ret = ''
for notice in self.notices:
- print notice
+ ret += str(notice)
+ return ret
def main():
+ def usage():
+ print >> sys.stderr, "Usage: %s <update metadata> ..." % sys.argv[0]
+ sys.exit(1)
+
+ if len(sys.argv) < 2:
+ usage()
try:
print sys.argv[1]
um = UpdateMetadata()
for srcfile in sys.argv[1:]:
um.add(srcfile)
-
- um.dump()
-
+ print um
except IOError:
- print >> sys.stderr, "update_md.py: No such file:\'%s\'" % sys.argv[1:]
- sys.exit(1)
-
+ print >> sys.stderr, "%s: No such file:\'%s\'" % (sys.argv[0],
+ sys.argv[1:])
+ usage()
+
if __name__ == '__main__':
main()
More information about the Yum-cvs-commits
mailing list