3 from xml.dom import minidom
6 from optparse import OptionParser
13 def get_checksum(fileName, checksum_type="sha256", excludeLine="", includeLine=""):
14 """Compute sha256 hash of the specified file"""
16 if checksum_type == "md5":
20 fd = open(fileName,"rb")
22 print "Unable to open the file in readmode:", filename
24 content = fd.readlines()
26 for eachLine in content:
27 if excludeLine and eachLine.startswith(excludeLine):
34 def __init__(self, patch = None, updates = None, cache = None):
37 self._check_update(cache)
40 self.add_update(patch, updates)
42 def _desc_to_html(self, page, desc):
44 for line in desc.splitlines():
45 if line.startswith('- ') or line.startswith('* '):
58 def get_title(self, t, is_html = False):
62 page.init( title = t, style = CSS_STRIKE)
64 return str(page).rstrip('</html>').rstrip().rstrip('</body>')
68 def get_summary_info(self, sum, is_html = False):
73 self._desc_to_html(page, sum['Description'])
78 return '%s\n%s' %(sum['Title'], sum['Description'])
80 def get_patch_info(self, update, is_html = False):
84 #page.h3(update['Title'])
85 self._desc_to_html(page, update['Description'])
87 if update.has_key("Bugs"):
89 page.add('Resolved Bugs: ')
91 for bug in update['Bugs']:
96 page.a(bug, href='http://bugs.tizen.org/show_bug.cgi?id=%s' %bug, class_="strike_link")
99 if update.has_key("CVEs"):
101 page.add('Resolved CVE Issues: ')
103 for cve in update['CVEs']:
108 page.a(cve, href='http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s\n' % cve, class_="strike_link")
129 update['Repository'],
132 ", ".join(update['Packages']),
133 '\n '.join(update['Description'].splitlines()))
135 if update.has_key("CVEs"):
138 for cve in update['CVEs']:
139 cve_info += ' http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s\n' %cve
142 if update.has_key("Bugs"):
145 for bug in update['Bugs']:
146 bug_info += ' http://bugs.tizen.org/show_bug.cgi?id=%s\n' %bug
149 if update.has_key('Reboot') and update['Reboot']:
150 INFO += ' NOTE: reboot needed\n'
151 if update.has_key('Relogin') and update['Relogin']:
152 INFO += ' NOTE: relogin needed\n'
153 if update.has_key('Restart') and update['Restart']:
154 INFO += ' NOTE: restart needed\n'
159 print "Creating new updates.xml file..."
160 doc = minidom.Document()
161 doc.appendChild(doc.createElement('updates'))
164 def _sanity_check(self):
166 print 'Empty or invalid updates.xml file.'
170 # check for duplicate patch entries
172 def _check_update(self, cache):
176 except AttributeError:
177 if os.path.exists(cache):
178 self.doc = minidom.parse(cache)
180 self.next = len(self.doc.getElementsByTagName('update'))
184 def _insert(self, parent, name, attrs={}, text=None, data=None):
185 """ Helper function to trivialize inserting an element into the doc """
186 child = self.doc.createElement(name)
187 for item in attrs.items():
188 child.setAttribute(item[0], unicode(item[1]))
190 txtnode = self.doc.createTextNode(unicode(text))
191 child.appendChild(txtnode)
193 txtnode = self.doc.createCDATASection(unicode(data))
194 child.appendChild(txtnode)
195 parent.appendChild(child)
198 def _get_notice(self, update_id):
199 return update_id in self.pids
201 def add_update(self, update, location, checksum):
203 Generate the extended metadata for a given update
207 self.next = self.next + 1
208 root = self._insert(self.doc.firstChild, 'update', attrs={
212 self._insert(root, 'title', text=update['Title'])
213 self._insert(root, 'type', text=update['Type'])
214 self._insert(root, 'location', attrs={'href': location})
215 self._insert(root, 'checksum', text=checksum)
216 self._insert(root, 'version', text=update['Release'])
217 times = str(time.time()).split(".")
218 issued_time = times[0]
219 self._insert(root, 'issued', attrs={ 'date' : issued_time })
221 if update.has_key('Reboot') and update['Reboot']:
222 self._insert(root, 'reboot_required', text='True')
223 if update.has_key('Relogin') and update['Relogin']:
224 self._insert(root, 'relogin_required', text='True')
225 if update.has_key('Restart') and update['Restart']:
226 self._insert(root, 'restart_required', text='True')
228 html = self.get_patch_info(update, True)
229 self._insert(root, 'description', data=html)
232 def __init__(self, patch = None, updates = None, cache = None):
235 self._check_update(cache)
237 if patch and updates:
238 self.add_patch(patch, updates)
241 print "Creating new updateinfo.xml file..."
242 doc = minidom.Document()
243 doc.appendChild(doc.createElement('updates'))
247 def _sanity_check(self):
249 print 'Empty or invalid updateinfo.xml file.'
253 # check for duplicate patch entries
254 for u in self.doc.getElementsByTagName('update'):
255 pid =u.getElementsByTagName('id')[0].firstChild.data
257 print 'Found duplicate update entry: %s' % pid
259 self.pids.append(pid)
261 def _check_update(self, cache):
265 except AttributeError:
266 if os.path.exists(cache):
267 self.doc = minidom.parse(cache)
269 self.next = len(self.doc.getElementsByTagName('update'))
274 def _insert(self, parent, name, attrs={}, text=None, data=None):
275 """ Helper function to trivialize inserting an element into the doc """
276 child = self.doc.createElement(name)
277 for item in attrs.items():
278 child.setAttribute(item[0], unicode(item[1]))
280 txtnode = self.doc.createTextNode(unicode(text))
281 child.appendChild(txtnode)
283 txtnode = self.doc.createCDATASection(unicode(data))
284 child.appendChild(txtnode)
285 parent.appendChild(child)
288 def _get_notice(self, update_id):
289 return update_id in self.pids
291 def add_patch(self, update, updates):
293 Generate the extended metadata for a given update
298 self.next = self.next + 1
299 root = self._insert(self.doc.firstChild, 'update', attrs={
300 'type' : update['Type'],
301 'status' : update['Status'],
302 'version' : "%04d" %self.next,
303 'from' : 'updates@tizen.org'
306 self._insert(root, 'id', text=update['ID'])
307 self._insert(root, 'title', text=update['Title'])
308 self._insert(root, 'release', text=update['Release'])
309 times = str(time.time()).split(".")
310 issued_time = times[0]
311 self._insert(root, 'issued', attrs={ 'date' : issued_time })
313 ## Build the references
314 refs = self.doc.createElement('references')
315 if update.has_key("CVEs"):
316 for cve in update['CVEs']:
317 self._insert(refs, 'reference', attrs={
319 'href' : 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s' %cve,
323 if update.has_key("Bugs"):
324 for bug in update['Bugs']:
325 self._insert(refs, 'reference', attrs={
327 'href' : 'http://bugs.tizen.org/show_bug.cgi?id=%s' %bug,
329 'title': 'Bug number %s' %bug
331 root.appendChild(refs)
333 ## Errata description
334 self._insert(root, 'description', text=update['Description'])
337 pkglist = self.doc.createElement('pkglist')
338 collection = self.doc.createElement('collection')
339 #collection.setAttribute('short', update.release.name)
340 #self._insert(collection, 'name', text=update.release.long_name)
343 filename = "rpms/%s" % (os.path.basename(u['binary']))
344 if u['header'][rpm.RPMTAG_SOURCEPACKAGE] or 'debuginfo' in u['binary']:
346 pkg = self._insert(collection, 'package', attrs={
347 'name' : u['header'][rpm.RPMTAG_NAME],
348 'version' : u['header'][rpm.RPMTAG_VERSION],
349 'release' : u['header'][rpm.RPMTAG_RELEASE],
350 'arch' : u['header'][rpm.RPMTAG_ARCH],
352 self._insert(pkg, 'filename', text=filename)
354 if update.has_key('Reboot') and update['Reboot']:
355 self._insert(pkg, 'reboot_suggested', text='True')
356 if update.has_key('Relogin') and update['Relogin']:
357 self._insert(pkg, 'relogin_suggested', text='True')
358 if update.has_key('Restart') and update['Restart']:
359 self._insert(pkg, 'restart_suggested', text='True')
361 collection.appendChild(pkg)
363 pkglist.appendChild(collection)
364 root.appendChild(pkglist)
366 def parse_patch( patch_path):
367 print 'Processing patch file:', patch_path
369 stream = file("%s" % ( patch_path), 'r')
371 print "Cannot read file: %s/%s" % ( patch_path)
374 patch = yaml.load(stream)
375 except yaml.scanner.ScannerError, e:
376 print 'syntax error found in yaml: %s' % str(e)
380 def create_updateinfo(root, patch):
384 patch_id = patch['ID']
386 packages = glob.glob("%s/%s/rpms/*.rpm" % (root, patch_id) ) + glob.glob("%s/%s/new/*.rpm" % (root, patch_id) )
387 for package in packages:
389 u['binary'] = package
390 ts = rpm.TransactionSet("/", rpm._RPMVSF_NOSIGNATURES)
391 fd = os.open(package, os.O_RDONLY)
392 header = ts.hdrFromFdno(fd)
398 ui.add_patch(patch, updates)
401 updateinfo_xml = ui.doc.toxml()
402 f = open("%s/updateinfo.xml" % root, "w")
403 f.write(updateinfo_xml)
406 def create_update_file(target_dir, destination, patch_id):
408 shutil.copyfile(patch_path, "%s/%s" %(target_dir, patch_id))
409 zip = zipfile.ZipFile("%s/%s.zip" % (destination, patch_id ), 'w', zipfile.ZIP_DEFLATED)
410 rootlen = len(target_dir) + 1
411 for base, dirs, files in os.walk(target_dir):
412 basedir = os.path.basename(base)
413 if basedir == "rpms":
416 fn = os.path.join(base, file)
417 zip.write(fn, "%s/%s" % (patch_id, fn[rootlen:]))
419 zip_checksum = get_checksum("%s/%s.zip" % (destination, patch_id))
422 def update_metadata(destination, root, updates_file, patch, zip_checksum):
423 # creates updates.xml
424 patch_id = patch['ID']
425 up = Updates(cache=opts.updatesfile)
426 up.add_update(patch, "%s.zip" %patch_id, zip_checksum)
428 updates_xml = up.doc.toxml()
429 f = open("%s/updates.xml" % root, "w")
433 if not os.path.exists("%s/data/updatemd.xml" %destination):
434 os.mkdir("%s/data" %destination)
435 updatemd = open("%s/data/repomd.xml" %destination, "w")
436 template = """<?xml version="1.0" encoding="UTF-8"?>
437 <repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
440 updatemd.write(template)
443 for line in fileinput.input("%s/data/updatemd.xml" %destination, inplace=1):
444 print line.replace("updatemd", "repomd"),
445 shutil.copyfile("%s/data/updatemd.xml" %destination, "%s/data/repomd.xml" %destination)
447 os.system("modifyrepo --mdtype=updates %s/updates.xml %s/data" % (root, destination))
448 shutil.move("%s/data/repomd.xml" %destination, "%s/data/updatemd.xml" %destination)
449 for line in fileinput.input("%s/data/updatemd.xml" %destination, inplace=1):
450 print line.replace("repodata", "data"),
451 for line in fileinput.input("%s/data/updatemd.xml" %destination, inplace=1):
452 print line.replace("repomd", "updatemd"),
456 parser = OptionParser()
457 parser.add_option('-u', '--updateinfo', metavar='TEXT',
458 help='cached meta updateinfo file')
459 parser.add_option('-U', '--updatesfile', metavar='UPDATES',
460 help='master updates.xml file')
461 parser.add_option('-O', '--original', metavar='ORIGINAL',
462 help='Original and Old package directory')
464 parser.add_option('-q', '--quiet', action='store_true',
465 help='do not show downloading progress')
466 parser.add_option('-d', '--destdir', default='.', metavar='DIR',
467 help='Directory where to store the updates.')
468 parser.add_option('-p', '--patch', metavar='TEXT',
469 help='Patch information')
470 parser.add_option('-P', '--patchdir', metavar='DIR',
471 help='directory with patch files')
472 parser.add_option('-t', '--testing', action='store_true',
475 (opts, args) = parser.parse_args()
479 print "missing options --patch. You need to point to a patch file (YAML format)"
485 patch_path = opts.patch
490 destination = opts.destdir
492 # create deltas (primary, deltainfo)
493 patch = parse_patch ( patch_path)
494 patch_id = patch['ID']
495 target_dir = "%s/%s" % (root, patch_id)
497 os.system("createrepo --deltas --oldpackagedirs=%s %s/%s" % (opts.original, root, patch_id))
500 create_updateinfo(root, patch)
503 os.system("modifyrepo %s/updateinfo.xml %s/%s/repodata" % (root, root, patch_id))
505 zip_checksum = create_update_file(target_dir, destination, patch_id)
507 update_metadata(destination, root, opts.updatesfile, patch, zip_checksum)