1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """
21 Update metadata (updateinfo.xml) parsing.
22 """
23
24 import sys
25 import gzip
26
27 from yum.i18n import utf8_text_wrap, to_utf8
28 from yum.yumRepo import YumRepository
29 from yum.packages import FakeRepository
30 from yum.misc import to_xml
31 import Errors
32
33 import rpmUtils.miscutils
34
35 try:
36 from xml.etree import cElementTree
37 except ImportError:
38 import cElementTree
39 iterparse = cElementTree.iterparse
40
41
43 """ An exception thrown for bad UpdateNotice data. """
44 pass
45
46
48
49 """
50 A single update notice (for instance, a security fix).
51 """
52
54 self._md = {
55 'from' : '',
56 'type' : '',
57 'title' : '',
58 'release' : '',
59 'status' : '',
60 'version' : '',
61 'pushcount' : '',
62 'update_id' : '',
63 'issued' : '',
64 'updated' : '',
65 'description' : '',
66 'references' : [],
67 'pkglist' : [],
68 'reboot_suggested' : False
69 }
70
71 if elem:
72 self._parse(elem)
73
75 """ Allows scriptable metadata access (ie: un['update_id']). """
76 return self._md.has_key(item) and self._md[item] or None
77
80
82 head = """
83 ===============================================================================
84 %(title)s
85 ===============================================================================
86 Update ID : %(update_id)s
87 Release : %(release)s
88 Type : %(type)s
89 Status : %(status)s
90 Issued : %(issued)s
91 """ % self._md
92
93 if self._md['updated'] and self._md['updated'] != self._md['issued']:
94 head += " Updated : %s" % self._md['updated']
95
96
97 bzs = filter(lambda r: r['type'] == 'bugzilla', self._md['references'])
98 if len(bzs):
99 buglist = " Bugs :"
100 for bz in bzs:
101 buglist += " %s%s\n\t :" % (bz['id'], 'title' in bz
102 and ' - %s' % bz['title'] or '')
103 head += buglist[: - 1].rstrip() + '\n'
104
105
106 cves = filter(lambda r: r['type'] == 'cve', self._md['references'])
107 if len(cves):
108 cvelist = " CVEs :"
109 for cve in cves:
110 cvelist += " %s\n\t :" % cve['id']
111 head += cvelist[: - 1].rstrip() + '\n'
112
113 if self._md['description'] is not None:
114 desc = utf8_text_wrap(self._md['description'], width=64,
115 subsequent_indent=' ' * 12 + ': ')
116 head += "Description : %s\n" % '\n'.join(desc)
117
118
119
120
121 arches = set(rpmUtils.arch.getArchList())
122
123 filelist = " Files :"
124 for pkg in self._md['pkglist']:
125 for file in pkg['packages']:
126 if file['arch'] not in arches:
127 continue
128 filelist += " %s\n\t :" % file['filename']
129 head += filelist[: - 1].rstrip()
130
131 return head
132
136
138 """
139 Parse an update element::
140
141 <!ELEMENT update (id, synopsis?, issued, updated,
142 references, description, pkglist)>
143 <!ATTLIST update type (errata|security) "errata">
144 <!ATTLIST update status (final|testing) "final">
145 <!ATTLIST update version CDATA #REQUIRED>
146 <!ATTLIST update from CDATA #REQUIRED>
147 """
148 if elem.tag == 'update':
149 for attrib in ('from', 'type', 'status', 'version'):
150 self._md[attrib] = elem.attrib.get(attrib)
151 for child in elem:
152 if child.tag == 'id':
153 if not child.text:
154 raise UpdateNoticeException("No id element found")
155 self._md['update_id'] = child.text
156 elif child.tag == 'pushcount':
157 self._md['pushcount'] = child.text
158 elif child.tag == 'issued':
159 self._md['issued'] = child.attrib.get('date')
160 elif child.tag == 'updated':
161 self._md['updated'] = child.attrib.get('date')
162 elif child.tag == 'references':
163 self._parse_references(child)
164 elif child.tag == 'description':
165 self._md['description'] = child.text
166 elif child.tag == 'pkglist':
167 self._parse_pkglist(child)
168 elif child.tag == 'title':
169 self._md['title'] = child.text
170 elif child.tag == 'release':
171 self._md['release'] = child.text
172 else:
173 raise UpdateNoticeException('No update element found')
174
176 """
177 Parse the update references::
178
179 <!ELEMENT references (reference*)>
180 <!ELEMENT reference>
181 <!ATTLIST reference href CDATA #REQUIRED>
182 <!ATTLIST reference type (self|cve|bugzilla) "self">
183 <!ATTLIST reference id CDATA #IMPLIED>
184 <!ATTLIST reference title CDATA #IMPLIED>
185 """
186 for reference in elem:
187 if reference.tag == 'reference':
188 data = {}
189 for refattrib in ('id', 'href', 'type', 'title'):
190 data[refattrib] = reference.attrib.get(refattrib)
191 self._md['references'].append(data)
192 else:
193 raise UpdateNoticeException('No reference element found')
194
196 """
197 Parse the package list::
198
199 <!ELEMENT pkglist (collection+)>
200 <!ELEMENT collection (name?, package+)>
201 <!ATTLIST collection short CDATA #IMPLIED>
202 <!ATTLIST collection name CDATA #IMPLIED>
203 <!ELEMENT name (#PCDATA)>
204 """
205 for collection in elem:
206 data = { 'packages' : [] }
207 if 'short' in collection.attrib:
208 data['short'] = collection.attrib.get('short')
209 for item in collection:
210 if item.tag == 'name':
211 data['name'] = item.text
212 elif item.tag == 'package':
213 data['packages'].append(self._parse_package(item))
214 self._md['pkglist'].append(data)
215
217 """
218 Parse an individual package::
219
220 <!ELEMENT package (filename, sum, reboot_suggested)>
221 <!ATTLIST package name CDATA #REQUIRED>
222 <!ATTLIST package version CDATA #REQUIRED>
223 <!ATTLIST package release CDATA #REQUIRED>
224 <!ATTLIST package arch CDATA #REQUIRED>
225 <!ATTLIST package epoch CDATA #REQUIRED>
226 <!ATTLIST package src CDATA #REQUIRED>
227 <!ELEMENT reboot_suggested (#PCDATA)>
228 <!ELEMENT filename (#PCDATA)>
229 <!ELEMENT sum (#PCDATA)>
230 <!ATTLIST sum type (md5|sha1) "sha1">
231 """
232 package = {}
233 for pkgfield in ('arch', 'epoch', 'name', 'version', 'release', 'src'):
234 package[pkgfield] = elem.attrib.get(pkgfield)
235 for child in elem:
236 if child.tag == 'filename':
237 package['filename'] = child.text
238 elif child.tag == 'sum':
239 package['sum'] = (child.attrib.get('type'), child.text)
240 elif child.tag == 'reboot_suggested':
241 self._md['reboot_suggested'] = True
242 return package
243
245 """Generate the xml for this update notice object"""
246 msg = """
247 <update from="%s" status="%s" type="%s" version="%s">
248 <id>%s</id>
249 <title>%s</title>
250 <release>%s</release>
251 <issued date="%s"/>
252 <description>%s</description>\n""" % (to_xml(self._md['from']),
253 to_xml(self._md['status']), to_xml(self._md['type']),
254 to_xml(self._md['version']), to_xml(self._md['update_id']),
255 to_xml(self._md['title']), to_xml(self._md['release']),
256 to_xml(self._md['issued'], attrib=True),
257 to_xml(self._md['description']))
258
259 if self._md['references']:
260 msg += """ <references>\n"""
261 for ref in self._md['references']:
262 if ref['title']:
263 msg += """ <reference href="%s" id="%s" title="%s" type="%s"/>\n""" % (
264 to_xml(ref['href'], attrib=True), to_xml(ref['id'], attrib=True),
265 to_xml(ref['title'], attrib=True), to_xml(ref['type'], attrib=True))
266 else:
267 msg += """ <reference href="%s" id="%s" type="%s"/>\n""" % (
268 to_xml(ref['href'], attrib=True), to_xml(ref['id'], attrib=True),
269 to_xml(ref['type'], attrib=True))
270
271 msg += """ </references>\n"""
272
273 if self._md['pkglist']:
274 msg += """ <pkglist>\n"""
275 for coll in self._md['pkglist']:
276 msg += """ <collection short="%s">\n <name>%s</name>\n""" % (
277 to_xml(coll['short'], attrib=True),
278 to_xml(coll['name']))
279
280 for pkg in coll['packages']:
281 msg += """ <package arch="%s" name="%s" release="%s" src="%s" version="%s">
282 <filename>%s</filename>
283 </package>\n""" % (to_xml(pkg['arch'], attrib=True),
284 to_xml(pkg['name'], attrib=True),
285 to_xml(pkg['release'], attrib=True),
286 to_xml(pkg['src'], attrib=True),
287 to_xml(pkg['version'], attrib=True),
288 to_xml(pkg['filename']))
289 msg += """ </collection>\n"""
290 msg += """ </pkglist>\n"""
291 msg += """</update>\n"""
292 return msg
293
295 """ Compare two "std." tuples, (n, a, e, v, r). """
296 return rpmUtils.miscutils.compareEVR((tup1[2], tup1[3], tup1[4]),
297 (tup2[2], tup2[3], tup2[4]))
298
435
436
438 """ update_md test function. """
439 import yum.misc
440
441 yum.misc.setup_locale()
442 def usage():
443 print >> sys.stderr, "Usage: %s <update metadata> ..." % sys.argv[0]
444 sys.exit(1)
445
446 if len(sys.argv) < 2:
447 usage()
448
449 try:
450 print sys.argv[1]
451 um = UpdateMetadata()
452 for srcfile in sys.argv[1:]:
453 um.add(srcfile)
454 print unicode(um)
455 except IOError:
456 print >> sys.stderr, "%s: No such file:\'%s\'" % (sys.argv[0],
457 sys.argv[1:])
458 usage()
459
460 if __name__ == '__main__':
461 main()
462