resetting manifest requested domain to floor
[platform/upstream/createrepo.git] / dmd.py
1 #!/usr/bin/python
2
3 # dmd - Generate and apply deltas between repository metadata
4 #
5 # Copyright (C) 2007 James Bowes <jbowes@redhat.com>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 import sys
22 from lxml.etree import parse, tostring, Element
23
24
25 class MdType(object):
26     def __init__(self, namespace, rootelem):
27         self.ns = "http://linux.duke.edu/metadata/%s" % namespace
28         self.sns = "{%s}" % self.ns
29         self.deltasns = "{http://linux.duke.edu/metadata/delta}"
30         self.root = rootelem
31
32     def get_pkg_id(self, pkg):
33         return pkg.findtext(self.sns + "checksum")
34
35     def make_hash(self, tree):
36         pkgshash = {}
37         for pkg in tree:
38             pkgid = self.get_pkg_id(pkg)
39             pkgshash[pkgid] = pkg
40
41         return pkgshash
42
43     def make_pkg_elem(self, pkgid, pkg):
44         pkgelem = Element("package")
45         pkgelem.set('name', pkg.findtext(self.sns + 'name'))
46         pkgelem.set('arch', pkg.findtext(self.sns + 'arch'))
47         pkgelem.set('pkgid', pkgid)
48         verelem = pkg.find(self.sns + 'version')
49         verelem.tag = "version"
50         pkgelem.append(verelem)
51
52         return pkgelem
53
54     def diff_trees(self, oldtree, newtree):
55         oldpkgs = oldtree.getroot().getchildren()
56         newpkgs = newtree.getroot().getchildren()
57
58         oldpkgshash = self.make_hash(oldpkgs)
59         newpkgshash = self.make_hash(newpkgs)
60
61         diff =  Element(self.root,
62                 nsmap = {None : self.ns,
63                          "rpm" : "http://linux.duke.edu/metadata/rpm",
64                          "delta" : "http://linux.duke.edu/metadata/delta"})
65         additions = Element("delta:additions")
66         diff.append(additions)
67         removals = Element("delta:removals")
68
69         diff.append(removals)
70
71         for pkgid, pkg in newpkgshash.iteritems():
72             if not oldpkgshash.has_key(pkgid):
73                 additions.append(pkg)
74
75         for pkgid, pkg in oldpkgshash.iteritems():
76             if not newpkgshash.has_key(pkgid):
77                 pkgelem = self.make_pkg_elem(pkgid, pkg)
78                 removals.append(pkgelem)
79
80         diff.set("packages", str(len(removals) + len(additions)))
81
82         print tostring(diff, pretty_print=True)
83
84     def patch_tree(self, oldtree, deltatree):
85         oldroot = oldtree.getroot()
86         oldpkgs = oldroot.getchildren()
87
88         oldpkgshash = self.make_hash(oldpkgs)
89
90         additions = deltatree.find(self.deltasns + 'additions').getchildren()
91         removals = deltatree.find(self.deltasns + 'removals').getchildren()
92
93         for pkg in additions:
94             pkgid = self.get_pkg_id(pkg)
95             if oldpkgshash.has_key(pkgid):
96                 print >> sys.stderr, "Package %s already exists" % pkgid
97                 sys.exit(1)
98             oldroot.append(pkg)
99
100         for pkg in removals:
101             pkgid = pkg.get('pkgid')
102             if not oldpkgshash.has_key(pkgid):
103                 print >> sys.stderr, "Package %s does not exist" % pkgid
104                 sys.exit(1)
105             oldroot.remove(oldpkgshash[pkgid])
106
107         oldcount = int(oldroot.get('packages'))
108         newcount = oldcount + len(additions) - len(removals)
109         oldroot.set('packages', str(newcount))
110         print tostring(oldtree, pretty_print=True)
111
112
113 class OtherMdType(MdType):
114     def get_pkg_id(self, pkg):
115         return pkg.get('pkgid')
116
117     def make_pkg_elem(self, pkgid, pkg):
118         pkgelem = Element("package")
119         pkgelem.set('name', pkg.get('name'))
120         pkgelem.set('arch', pkg.get('arch'))
121         pkgelem.set('pkgid', pkgid)
122         verelem = pkg.find(self.sns + 'version')
123         verelem.tag = "version"
124
125         return pkgelem
126
127
128 mdtypeinfo = {
129         'primary' : MdType('common', 'metadata'),
130         'filelists' : OtherMdType('filelists', 'filelists'),
131         'other' : OtherMdType('other', 'other'),
132         }
133
134
135 def usage(progname):
136     print "usage: %s [diff|patch] MDTYPE FILE1 FILE2" % progname
137     sys.exit()
138
139 def main(args):
140     if len(args) != 5:
141         usage(args[0])
142     if args[1] not in ('diff', 'patch'):
143         usage(args[0])
144     if args[2] not in ('primary', 'filelists', 'other'):
145         usage(args[0])
146
147     oldtree = parse(args[3])
148     newtree = parse(args[4])
149
150     if args[1] == 'diff':
151         mdtypeinfo[args[2]].diff_trees(oldtree, newtree)
152     else:
153         mdtypeinfo[args[2]].patch_tree(oldtree, newtree)
154
155 if __name__ == "__main__":
156     main(sys.argv)