diff --git a/createrepo/__init__.py b/createrepo/__init__.py
index 744bb67..2b3acac 100644
--- a/createrepo/__init__.py
+++ b/createrepo/__init__.py
@@ -25,7 +25,7 @@ from bz2 import BZ2File
from urlgrabber import grabber
import tempfile
-from yum import misc, Errors
+from yum import misc, Errors, to_unicode
from yum.sqlutils import executeSQL
from yum.packageSack import MetaSack
from yum.packages import YumAvailablePackage
@@ -44,6 +44,7 @@ except ImportError:
pass
from utils import _gzipOpen, bzipFile, checkAndMakeDir, GzipFile, checksum_and_rename
+import deltarpms
__version__ = '0.9.6'
@@ -52,10 +53,11 @@ class MetaDataConfig(object):
def __init__(self):
self.quiet = False
self.verbose = False
+ self.profile = False
self.excludes = []
self.baseurl = None
self.groupfile = None
- self.sumtype = 'sha'
+ self.sumtype = 'sha256'
self.pretty = False
self.cachedir = None
self.use_cache = False
@@ -63,6 +65,13 @@ class MetaDataConfig(object):
self.checkts = False
self.split = False
self.update = False
+ self.deltas = False # do the deltarpm thing
+ self.deltadir = None # where to put the .drpms - defaults to 'drpms' inside 'repodata'
+ self.delta_relative = 'drpms/'
+ self.oldpackage_paths = [] # where to look for the old packages -
+ self.deltafile = 'prestodelta.xml.gz'
+ self.num_deltas = 1 # number of older versions to delta (max)
+ self.update_md_path = None
self.skip_stat = False
self.database = False
self.outputdir = None
@@ -149,7 +158,6 @@ class MetaDataGenerator:
if not self.conf.outputdir:
self.conf.outputdir = os.path.join(self.conf.basedir, self.conf.relative_dir)
-
def _test_setup_dirs(self):
# start the sanity/stupidity checks
for mydir in self.conf.directories:
@@ -178,13 +186,23 @@ class MetaDataGenerator:
if not checkAndMakeDir(temp_final):
raise MDError, _('Cannot create/verify %s') % temp_final
+ if self.conf.deltas:
+ temp_delta = os.path.join(self.conf.outputdir, self.conf.delta_relative)
+ if not checkAndMakeDir(temp_delta):
+ raise MDError, _('Cannot create/verify %s') % temp_delta
+ self.conf.deltadir = temp_delta
+
if os.path.exists(os.path.join(self.conf.outputdir, self.conf.olddir)):
raise MDError, _('Old data directory exists, please remove: %s') % self.conf.olddir
# make sure we can write to where we want to write to:
# and pickup the mdtimestamps while we're at it
- for direc in ['tempdir', 'finaldir']:
- filepath = os.path.join(self.conf.outputdir, direc)
+ direcs = ['tempdir' , 'finaldir']
+ if self.conf.deltas:
+ direcs.append('deltadir')
+
+ for direc in direcs:
+ filepath = os.path.join(self.conf.outputdir, getattr(self.conf, direc))
if os.path.exists(filepath):
if not os.access(filepath, os.W_OK):
raise MDError, _('error in must be able to write to metadata dir:\n -> %s') % filepath
@@ -300,11 +318,17 @@ class MetaDataGenerator:
'verbose' : self.conf.verbose,
'pkgdir' : os.path.normpath(self.package_dir)
}
+
if self.conf.skip_stat:
opts['do_stat'] = False
+ if self.conf.update_md_path:
+ old_repo_path = os.path.normpath(self.conf.update_md_path)
+ else:
+ old_repo_path = self.conf.outputdir
+
#and scan the old repo
- self.oldData = readMetadata.MetadataIndex(self.conf.outputdir, opts)
+ self.oldData = readMetadata.MetadataIndex(old_repo_path, opts)
def _setup_grabber(self):
if not hasattr(self, '_grabber'):
@@ -340,6 +364,8 @@ class MetaDataGenerator:
self.primaryfile = self._setupPrimary()
self.flfile = self._setupFilelists()
self.otherfile = self._setupOther()
+ if self.conf.deltas:
+ self.deltafile = self._setupDelta()
def _setupPrimary(self):
# setup the primary metadata file
@@ -367,6 +393,14 @@ class MetaDataGenerator:
fo.write('<otherdata xmlns="http://linux.duke.edu/metadata/other" packages="%s">' %
self.pkgcount)
return fo
+
+ def _setupDelta(self):
+ # setup the other file
+ deltafilepath = os.path.join(self.conf.outputdir, self.conf.tempdir, self.conf.deltafile)
+ fo = _gzipOpen(deltafilepath, 'w')
+ fo.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+ fo.write('<prestodelta>\n')
+ return fo
def read_in_package(self, rpmfile, pkgpath=None, reldir=None):
@@ -444,7 +478,8 @@ class MetaDataGenerator:
nodes = self.oldData.getNodes(old_pkg)
if nodes is not None:
recycled = True
-
+
+ # FIXME also open up the delta file
# otherwise do it individually
if not recycled:
@@ -462,6 +497,11 @@ class MetaDataGenerator:
# need to say something here
self.callback.errorlog("\nError %s: %s\n" % (pkg, e))
continue
+ # we can use deltas:
+ presto_md = self._do_delta_rpm_package(po)
+ if presto_md:
+ self.deltafile.write(presto_md)
+
else:
po = pkg
@@ -492,6 +532,9 @@ class MetaDataGenerator:
outfile.write('\n')
self.oldData.freeNodes(pkg)
+ #FIXME - if we're in update and we have deltas enabled
+ # check the presto data for this pkg and write its info back out
+ # to our deltafile
if not self.conf.quiet:
if self.conf.verbose:
@@ -532,6 +575,87 @@ class MetaDataGenerator:
self.otherfile.write('\n</otherdata>')
self.otherfile.close()
+ if not self.conf.quiet:
+ self.callback.log(_('Saving delta metadata'))
+ self.deltafile.write('\n</prestodelta>')
+ self.deltafile.close()
+
+ def _do_delta_rpm_package(self, pkg):
+ """makes the drpms, if possible, for this package object.
+ returns the presto/delta xml metadata as a string
+ """
+
+ results = u""
+ thisdeltastart = u""" <newpackage name="%s" epoch="%s" version="%s" release="%s" arch="%s">\n""" % (pkg.name,
+ pkg.epoch, pkg.ver, pkg.release, pkg.arch)
+ thisdeltaend = u""" </newpackage>\n"""
+
+ # generate a list of all the potential 'old rpms'
+ opl = self._get_old_package_list()
+ # get list of potential candidates which are likely to match
+ pot_cand = []
+ for fn in opl:
+ if os.path.basename(fn).startswith(pkg.name):
+ pot_cand.append(fn)
+
+ candidates = []
+ for fn in pot_cand:
+ try:
+ thispo = yumbased.CreateRepoPackage(self.ts, fn)
+ except Errors.MiscError, e:
+ continue
+ if (thispo.name, thispo.arch) != (pkg.name, pkg.arch):
+ # not the same, doesn't matter
+ continue
+ if thispo == pkg: #exactly the same, doesn't matter
+ continue
+ if thispo.EVR >= pkg.EVR: # greater or equal, doesn't matter
+ continue
+ candidates.append(thispo)
+ candidates.sort()
+ candidates.reverse()
+
+ drpm_results = u""
+ for delta_p in candidates[0:self.conf.num_deltas]:
+ #make drpm of pkg and delta_p
+ drpmfn = deltarpms.create_drpm(delta_p, pkg, self.conf.deltadir)
+
+ if drpmfn:
+ # TODO more sanity check the drpm for size, etc
+ # make xml of drpm
+ try:
+ drpm_po = yumbased.CreateRepoPackage(self.ts, drpmfn)
+ except Errors.MiscError, e:
+ os.unlink(drpmfn)
+ continue
+ rel_drpmfn = drpmfn.replace(self.conf.outputdir, '')
+ if rel_drpmfn[0] == '/':
+ rel_drpmfn = rel_drpmfn[1:]
+ if not self.conf.quiet:
+ if self.conf.verbose:
+ self.callback.log('created drpm from %s to %s: %s' % (
+ delta_p, pkg, drpmfn))
+
+ drpm = deltarpms.DeltaRPMPackage(drpm_po, self.conf.outputdir, rel_drpmfn)
+ drpm_results += to_unicode(drpm.xml_dump_metadata())
+
+ if drpm_results:
+ results = thisdeltastart + drpm_results + thisdeltaend
+
+ return results
+
+ def _get_old_package_list(self):
+ if hasattr(self, '_old_package_list'):
+ return self._old_package_list
+
+ opl = []
+ for d in self.conf.oldpackage_paths:
+ for f in self.getFileList(d, 'rpm'):
+ opl.append(d + '/' + f)
+
+ self._old_package_list = opl
+ return self._old_package_list
+
def addArbitraryMetadata(self, mdfile, mdtype, xml_node, compress=True,
compress_type='gzip', attribs={}):
"""add random metadata to the repodata dir and repomd.xml
@@ -630,6 +754,8 @@ class MetaDataGenerator:
db_workfiles = []
repoid='garbageid'
+ if self.conf.deltas:
+ workfiles.append((self.conf.deltafile, 'deltainfo'))
if self.conf.database:
if not self.conf.quiet: self.callback.log('Generating sqlite DBs')
try:
diff --git a/createrepo/deltarpms.py b/createrepo/deltarpms.py
new file mode 100644
index 0000000..4b4acaf
--- /dev/null
+++ b/createrepo/deltarpms.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python -tt
+# util functions for deltarpms
+# 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 2009 - Red Hat
+
+import os.path
+import commands
+from yum import misc
+import gzip
+
+class DeltaRPMPackage:
+ """each drpm is one object, you pass it a drpm file
+ it opens the file, and pulls the information out in bite-sized chunks :)
+ """
+
+ mode_cache = {}
+
+ def __init__(self, pkgobj, basedir, filename):
+ try:
+ stats = os.stat(os.path.join(basedir, filename))
+ self.size = stats[6]
+ self.mtime = stats[8]
+ del stats
+ except OSError, e:
+ raise MDError, "Error Stat'ing file %s %s" % (basedir, filename)
+ self.csum_type = 'sha256'
+ self.relativepath = filename
+ self.po = pkgobj
+
+ fd = os.open(self.po.localpath, os.O_RDONLY)
+ os.lseek(fd, 0, 0)
+ fo = os.fdopen(fd, 'rb')
+ self.csum = misc.checksum(self.csum_type, fo)
+ fo.seek(int(self.po.hdrend))
+ self._getOldInfo(fo)
+ del fo
+ del fd
+
+ def _stringToNEVR(self, string):
+ i = string.rfind("-", 0, string.rfind("-")-1)
+ name = string[:i]
+ (epoch, ver, rel) = self._stringToVersion(string[i+1:])
+ return (name, epoch, ver, rel)
+
+ def _getLength(self, in_data):
+ length = 0
+ for val in in_data:
+ length = length * 256
+ length += ord(val)
+ return length
+
+ def _getOldInfo(self, fo):
+ try:
+ compobj = gzip.GzipFile("", "rb", 9, fo)
+ except:
+ raise zlibError("Data not stored in gzip format")
+
+ if compobj.read(4)[:3] != "DLT":
+ raise Exception("Not a deltarpm")
+
+ nevr_length = self._getLength(compobj.read(4))
+ nevr = compobj.read(nevr_length).strip("\x00")
+ seq_length = self._getLength(compobj.read(4))
+ seq = compobj.read(seq_length)
+ hex_seq = ""
+ for char in seq:
+ hex_seq += str("%02x" % ord(char))
+ self.oldnevrstring = nevr
+ self.oldnevr = self._stringToNEVR(nevr)
+ self.sequence = hex_seq
+ compobj.close()
+
+ def _stringToVersion(self, strng):
+ i = strng.find(':')
+ if i != -1:
+ epoch = strng[:i]
+ else:
+ epoch = '0'
+ j = strng.find('-')
+ if j != -1:
+ if strng[i + 1:j] == '':
+ version = None
+ else:
+ version = strng[i + 1:j]
+ release = strng[j + 1:]
+ else:
+ if strng[i + 1:] == '':
+ version = None
+ else:
+ version = strng[i + 1:]
+ release = None
+ return (epoch, version, release)
+
+ def xml_dump_metadata(self):
+ """takes an xml doc object and a package metadata entry node, populates a
+ package node with the md information"""
+
+ (oldname, oldepoch, oldver, oldrel) = self.oldnevr
+ sequence = "%s-%s" % (self.oldnevrstring, self.sequence)
+
+ delta_tag = """
+ <delta oldepoch="%s" oldversion="%s" oldrelease="%s">
+ <filename>%s</filename>
+ <sequence>%s</sequence>
+ <size>%s</size>
+ <checksum type="%s">%s</checksum>
+ </delta>\n""" % (oldepoch, oldver, oldrel, self.relativepath, sequence,
+ self.size, self.csum_type, self.csum)
+ return delta_tag
+
+def create_drpm(old_pkg, new_pkg, destdir):
+ """make a drpm file, if possible. returns None if nothing could
+ be created"""
+ drpmfn = '%s-%s-%s_%s-%s.%s.drpm' % (old_pkg.name, old_pkg.ver,
+ old_pkg.release, new_pkg.ver, new_pkg.release,
+ old_pkg.arch)
+ delta_rpm_path = os.path.join(destdir, drpmfn)
+ delta_command = '/usr/bin/makedeltarpm %s %s %s' % (old_pkg.localpath,
+ new_pkg.localpath,
+ delta_rpm_path)
+ if not os.path.exists(delta_rpm_path):
+ #TODO - check/verify the existing one a bit?
+ (code, out) = commands.getstatusoutput(delta_command)
+ if code:
+ print "Error genDeltaRPM for %s: exitcode was %s - Reported Error: %s" % (old_pkg.name, code, out)
+ return None
+
+ return delta_rpm_path
diff --git a/createrepo/merge.py b/createrepo/merge.py
index d096c61..d2997b7 100644
--- a/createrepo/merge.py
+++ b/createrepo/merge.py
@@ -35,13 +35,23 @@ import tempfile
class RepoMergeBase():
- def __init__(self, repolist=[]):
+ def __init__(self, repolist=[], yumbase=None, mdconf=None, mdbase_class=None ):
self.repolist = repolist
self.outputdir = '%s/merged_repo' % os.getcwd()
self.exclude_tuples = []
self.sort_func = self._sort_func # callback function to magically sort pkgs
- self.mdconf = createrepo.MetaDataConfig()
- self.yumbase = yum.YumBase()
+ if not mdconf:
+ self.mdconf = createrepo.MetaDataConfig()
+ else:
+ self.mdconf = mdconf
+ if not mdbase_class
+ self.mdbase_class = createrepo.MetaDataGenerator
+ else:
+ self.mdbase_class = mdbase_class
+ if not yumbase:
+ self.yumbase = yum.YumBase()
+ else:
+ self.yumbase = yumbase
self.yumbase.conf.cachedir = getCacheDir()
self.yumbase.conf.cache = 0
# default to all arches
@@ -121,7 +131,7 @@ class RepoMergeBase():
if not os.path.exists(self.mdconf.directory):
os.makedirs(self.mdconf.directory)
- mdgen = createrepo.MetaDataGenerator(config_obj=self.mdconf)
+ mdgen = self.mdbase_class(config_obj=self.mdconf)
mdgen.doPkgMetadata()
mdgen.doRepoMetadata()
mdgen.doFinalMove()
diff --git a/createrepo/utils.py b/createrepo/utils.py
index 1b0d015..3f6414b 100644
--- a/createrepo/utils.py
+++ b/createrepo/utils.py
@@ -36,7 +36,10 @@ class GzipFile(gzip.GzipFile):
def _write_gzip_header(self):
self.fileobj.write('\037\213') # magic header
self.fileobj.write('\010') # compression method
- fname = self.filename[:-3]
+ if hasattr(self, 'name'):
+ fname = self.name[:-3]
+ else:
+ fname = self.filename[:-3]
flags = 0
if fname:
flags = FNAME
@@ -100,7 +103,7 @@ def checkAndMakeDir(dir):
def checksum_and_rename(fn_path):
"""checksum the file rename the file to contain the checksum as a prefix
return the new filename"""
- csum = misc.checksum('sha', fn_path)
+ csum = misc.checksum('sha256', fn_path)
fn = os.path.basename(fn_path)
fndir = os.path.dirname(fn_path)
csum_fn = csum + '-' + fn
diff --git a/createrepo/yumbased.py b/createrepo/yumbased.py
index 60568e3..b820fd8 100644
--- a/createrepo/yumbased.py
+++ b/createrepo/yumbased.py
@@ -19,7 +19,6 @@ import os
import rpm
import types
import re
-import md5
from yum.packages import YumLocalPackage
from yum.Errors import *
@@ -49,6 +48,7 @@ class CreateRepoPackage(YumLocalPackage):
# not using the cachedir
if not self._cachedir:
self._checksum = misc.checksum(self.checksum_type, self.localpath)
+ self._checksums = [(self.checksum_type, self._checksum, 1)]
return self._checksum
@@ -60,7 +60,9 @@ class CreateRepoPackage(YumLocalPackage):
if type(self.hdr[rpm.RPMTAG_HDRID]) is not types.NoneType:
t.append("".join(self.hdr[rpm.RPMTAG_HDRID]))
- key = md5.new("".join(t)).hexdigest()
+ kcsum = misc.Checksums()
+ kcsum.update("".join(t))
+ key = kcsum.hexdigest()
csumtag = '%s-%s-%s-%s' % (os.path.basename(self.localpath),
key, self.size, self.filetime)
@@ -72,12 +74,12 @@ class CreateRepoPackage(YumLocalPackage):
csumo.close()
else:
- checksum = misc.checksum('sha', self.localpath)
+ checksum = misc.checksum(self.checksum_type, self.localpath)
# This is atomic cache creation via. rename, so we can have two
# tasks using the same cachedir ... mash does this.
try:
- (csumo, tmpfilename) = tempfile.mkstemp(dir=self.crp_cachedir)
+ (csumo, tmpfilename) = tempfile.mkstemp(dir=self._cachedir)
csumo = os.fdopen(csumo, 'w', -1)
csumo.write(checksum)
csumo.close()
@@ -86,6 +88,7 @@ class CreateRepoPackage(YumLocalPackage):
pass
self._checksum = checksum
+ self._checksums = [(self.checksum_type, checksum, 1)]
return self._checksum
diff --git a/docs/createrepo.8 b/docs/createrepo.8
index 1a31190..e7cce4f 100644
--- a/docs/createrepo.8
+++ b/docs/createrepo.8
@@ -69,6 +69,11 @@ Show help menu.
Generate sqlite databases for use with yum.
.IP "\fB\-S --skip-symlinks\fP"
Ignore symlinks of packages
+.IP "\fB\-s --checksum\fP"
+Choose the checksum type used in repomd.xml and for packages in the metadata.
+The default is now sha256, the older default was sha which is actually sha1.
+.IP "\fB\-profile\fP"
+Output time based profiling information.
.IP "\fB\--changelog-limit\fP CHANGELOG_LIMIT"
Only import the last N changelog entries, from each rpm, into the metadata
.IP "\fB\--unique-md-filenames\fP"
diff --git a/genpkgmetadata.py b/genpkgmetadata.py
index 77b4095..041bc02 100755
--- a/genpkgmetadata.py
+++ b/genpkgmetadata.py
@@ -41,6 +41,8 @@ def parseArgs(args, conf):
help="output nothing except for serious errors")
parser.add_option("-v", "--verbose", default=False, action="store_true",
help="output more debugging info.")
+ parser.add_option("--profile", default=False, action="store_true",
+ help="output timing/profile info.")
parser.add_option("-x", "--excludes", default=[], action="append",
help="files to exclude")
parser.add_option("--basedir", default=os.getcwd(),
@@ -49,7 +51,7 @@ def parseArgs(args, conf):
help="baseurl to append on all files")
parser.add_option("-g", "--groupfile", default=None,
help="path to groupfile to include in metadata")
- parser.add_option("-s", "--checksum", default="sha", dest='sumtype',
+ parser.add_option("-s", "--checksum", default="sha256", dest='sumtype',
help="Deprecated, ignore")
parser.add_option("-p", "--pretty", default=False, action="store_true",
help="make sure all xml generated is formatted")
@@ -64,6 +66,8 @@ def parseArgs(args, conf):
# help="Only make the sqlite databases - does not work with --update, yet")
parser.add_option("--update", default=False, action="store_true",
help="use the existing repodata to speed up creation of new")
+ parser.add_option("--update-md-path", default=None, dest='update_md_path',
+ help="use the existing repodata for --update from this path")
parser.add_option("--skip-stat", dest='skip_stat', default=False, action="store_true",
help="skip the stat() call on a --update, assumes if the file" \
"name is the same then the file is still the same" \
@@ -89,6 +93,13 @@ def parseArgs(args, conf):
help="tags for the content in the repository")
parser.add_option("--revision", default=None,
help="user-specified revision for this repository")
+ parser.add_option("--deltas", default=False, action="store_true",
+ help="create delta rpms and metadata")
+ parser.add_option("--oldpackagedirs", default=[], dest="oldpackage_paths",
+ action="append", help="paths to look for older pkgs to delta against")
+ parser.add_option("--num-deltas", default=1, dest='num_deltas', type='int',
+ help="the number of older versions to make deltas against")
+
(opts, argsleft) = parser.parse_args(args)
if len(argsleft) > 1 and not opts.split:
@@ -164,7 +175,7 @@ def main(args):
start_st = time.time()
conf = createrepo.MetaDataConfig()
conf = parseArgs(args, conf)
- if conf.verbose:
+ if conf.profile:
print ('start time: %0.3f' % (time.time() - start_st))
mid_st = time.time()
@@ -178,20 +189,20 @@ def main(args):
print _('repo is up to date')
sys.exit(0)
- if conf.verbose:
+ if conf.profile:
print ('mid time: %0.3f' % (time.time() - mid_st))
pm_st = time.time()
mdgen.doPkgMetadata()
- if conf.verbose:
+ if conf.profile:
print ('pm time: %0.3f' % (time.time() - pm_st))
rm_st = time.time()
mdgen.doRepoMetadata()
- if conf.verbose:
+ if conf.profile:
print ('rm time: %0.3f' % (time.time() - rm_st))
fm_st = time.time()
mdgen.doFinalMove()
- if conf.verbose:
+ if conf.profile:
print ('fm time: %0.3f' % (time.time() - fm_st))
diff --git a/modifyrepo.py b/modifyrepo.py
index 632d824..1efe966 100755
--- a/modifyrepo.py
+++ b/modifyrepo.py
@@ -35,6 +35,7 @@ class RepoMetadata:
""" Parses the repomd.xml file existing in the given repo directory. """
self.repodir = os.path.abspath(repo)
self.repomdxml = os.path.join(self.repodir, 'repomd.xml')
+ self.checksum_type = 'sha256'
if not os.path.exists(self.repomdxml):
raise Exception('%s not found' % self.repomdxml)
self.doc = minidom.parse(self.repomdxml)
@@ -84,7 +85,7 @@ class RepoMetadata:
newmd.close()
print "Wrote:", destmd
- open_csum = checksum('sha', metadata)
+ open_csum = checksum(self.checksum_type, metadata)
csum, destmd = checksum_and_rename(destmd)
@@ -105,13 +106,13 @@ class RepoMetadata:
self._insert_element(data, 'location',
attrs={ 'href' : 'repodata/' + base_destmd })
data.appendChild(self.doc.createTextNode("\n "))
- self._insert_element(data, 'checksum', attrs={ 'type' : 'sha' },
+ self._insert_element(data, 'checksum', attrs={ 'type' : self.checksum_type },
text=csum)
data.appendChild(self.doc.createTextNode("\n "))
self._insert_element(data, 'timestamp',
text=str(os.stat(destmd).st_mtime))
data.appendChild(self.doc.createTextNode("\n "))
- self._insert_element(data, 'open-checksum', attrs={ 'type' : 'sha' },
+ self._insert_element(data, 'open-checksum', attrs={ 'type' : self.checksum_type },
text=open_csum)
data.appendChild(self.doc.createTextNode("\n "))