]> git.pld-linux.org Git - projects/pld-ftp-admin.git/blame - modules/ftptree.py
- add check for obsoletes
[projects/pld-ftp-admin.git] / modules / ftptree.py
CommitLineData
098f4a50
MM
1# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
2
ce470bd9 3import os, config, string, urllib, re, rpm
81e9387d 4from common import fileexists, noarchcachedir
151d31cc 5from baseftptree import BasePkg, BaseFtpTree
aeb928ae 6from sign import is_signed
098f4a50 7
aeb928ae
ER
8errnum = 0
9quietmode = False
0a108b7f 10
76c9cb26 11class SomeError(Exception):
db126e0b
ER
12 def __init__(self):
13 return
14
15 def __str__(self):
16 print "","An Error occured!"
17
098f4a50
MM
18def bailoutonerror():
19 if not errnum == 0:
20 print "%d error(s) encountered... aborting" % errnum
76c9cb26 21 raise SomeError
098f4a50 22
d9b3388c 23def pinfo(msg):
0767aa5e 24 print 'INFO: ' + msg
d9b3388c 25
098f4a50
MM
26def perror(msg):
27 global errnum
213a164a 28 errnum = errnum + 1
492b6398 29 print 'ERR: ' + msg
098f4a50 30
796b7867 31def pwarning(msg):
492b6398 32 print 'WARN: ' + msg
796b7867 33
213a164a 34def rm(file, test = False):
a0b52be0
AM
35 if test:
36 if not os.path.exists(file):
37 pinfo("TEST os.remove(%s): file doesn't exists" % file)
38 else:
2d70dbd8
AM
39 try:
40 os.remove(file)
41 except OSError, e:
42 pinfo("os.remove(%s): %s" % (file, e))
8911f226 43 #raise
a0b52be0 44
213a164a 45def mv(src, dst, test = False):
a0b52be0 46 fsrc = src
213a164a 47 fdst = dst + '/' + src.split('/')[-1]
a0b52be0
AM
48 if test:
49 if not os.path.exists(src):
50 pinfo("TEST os.rename(%s, %s): source doesn't exists" % (fsrc, fdst))
c4647bc3
ER
51 if not os.path.exists(dst):
52 pinfo("TEST destination doesn't exist: %s" % dst)
a0b52be0
AM
53 else:
54 try:
55 os.rename(fsrc, fdst)
56 except OSError, e:
aee80850 57 pinfo("os.rename(%s, %s): %s" % (fsrc, fdst, e))
a0b52be0 58 raise
098f4a50 59
151d31cc 60class Pkg(BasePkg):
85f3481a
MM
61 def __init__(self, nvr, tree):
62 BasePkg.__init__(self, nvr, tree)
8911f226
ER
63 self.name = string.join(nvr.split('-')[:-2], '-')
64 self.version = nvr.split('-')[-2]
65 self.release = nvr.split('-')[-1]
66 self.marked4removal = False
67 self.marked4moving = False
68 self.marked4movingpool = []
69 self.errors = []
70 self.warnings = []
098f4a50 71
d7667ebe
MM
72 def __cmp__(self, pkg):
73 if self.name > pkg.name:
74 return 1
75 elif self.name < pkg.name:
76 return -1
77 else:
78 return rpm.labelCompare(('0', self.version, self.release),
79 ('0', pkg.version, pkg.release))
80
54ff0049
ER
81
82 # unfortunately can't do new Pkg(NVR), and have no "tree" in this pkg context
83 # so this static function
84 def is_debuginfo(self, nvr):
85 """
86 returns true if NVR is debuginfo package and separate debuginfo is enabled
87 """
88 if not config.separate_debuginfo:
89 return False
90 pkg = nvr.split('-')[:-2]
91 return pkg[-1] == 'debuginfo'
92
14085d11
MM
93 def mark4moving(self):
94 if not self.marked4moving:
f301e305
MM
95 # Only one pkg in this pool can be marked for moving
96 for pkg in self.marked4movingpool:
97 pkg.unmark4moving()
14085d11
MM
98 self.tree.marked4moving.append(self)
99 self.marked4moving=True
100
f301e305
MM
101 def unmark4moving(self):
102 if self.marked4moving:
103 self.tree.marked4moving.remove(self)
104 self.marked4moving=False
105
14085d11
MM
106 def mark4removal(self):
107 if not self.marked4removal:
108 self.tree.marked4removal.append(self)
109 self.marked4removal=True
098f4a50 110
85f3481a
MM
111 def error(self, msg):
112 self.errors.append(msg)
113 if not quietmode:
114 perror('%s %s' % (self.nvr, msg))
115
116 def warning(self, msg):
117 self.warnings.append(msg)
118 if not quietmode:
119 pwarning('%s %s' % (self.nvr, msg))
120
bb2cb325
MM
121 def load(self, content=None):
122 BasePkg.load(self, content)
098f4a50 123 if self.info.has_key('move'):
14085d11 124 self.mark4moving()
098f4a50
MM
125
126 def writeinfo(self):
8911f226 127 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
098f4a50
MM
128 for bid in self.build.keys():
129 f.write("info:build:%s:requester:%s\ninfo:build:%s:requester_email:%s\n" % (bid, self.build[bid].requester, bid, self.build[bid].requester_email))
130 for key in self.info.keys():
131 f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
151d31cc
MM
132 for arch in self.files.keys():
133 for rpm in self.files[arch]:
098f4a50
MM
134 f.write("file:%s:%s\n" % (arch, rpm))
135
8911f226
ER
136 def remove(self, test = False):
137 """
138 Remove package from ftp
139 """
151d31cc
MM
140 for arch in self.files.keys():
141 for rpm in self.files[arch]:
54ff0049
ER
142 if self.is_debuginfo(rpm):
143 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
144 else:
145 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
8911f226
ER
146 if arch == 'noarch':
147 if fileexists(noarchcachedir + rpm + '.filelist'):
148 rm(noarchcachedir + rpm + '.filelist', test)
149 if fileexists(noarchcachedir + rpm + '.reqlist'):
150 rm(noarchcachedir + rpm + '.reqlist', test)
151 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
098f4a50 152
d38e8382
ER
153 def rpmfiles(self):
154 """
155 Return rpm files related to this package
156 """
157 files = []
158 for arch, rpms in self.files.items():
159 for nvr in rpms:
160 if self.is_debuginfo(nvr):
161 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
162 else:
163 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
164 return files
165
f6dff636
ER
166 def obsoletes(self):
167 """
168 Return obsoletes for all packages in Pkg:
169
170 {'php-geshi': set(['geshi'])}
171
172 """
173 def rpmhdr(pkg):
174 ts = rpm.ts()
175 ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
176 fdno = os.open(pkg, os.O_RDONLY)
177 hdr = ts.hdrFromFdno(fdno)
178 os.close(fdno)
179 return hdr
180
181 obsoletes = {}
182 for rpmfile in self.rpmfiles():
183 hdr = rpmhdr(rpmfile)
184 if not hdr[rpm.RPMTAG_OBSOLETES]:
185 continue
186
187 name = hdr[rpm.RPMTAG_NAME]
188 if not name in obsoletes:
189 obsoletes[name] = set()
190
191 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
192 obsoletes[name].add(tag)
193
194 return obsoletes
195
213a164a 196 def move(self, dsttree, test = False):
85f3481a 197 if dsttree.has_key(self.nvr):
8911f226 198 movedany = False
151d31cc 199 for arch in self.files.keys():
85f3481a 200 if arch in dsttree[self.nvr].files.keys():
77b616ee
AM
201 msg = ""
202 if test:
203 msg = "TEST "
204 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
151d31cc 205 for rpm in self.files[arch]:
54ff0049
ER
206 if self.is_debuginfo(rpm):
207 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
208 else:
209 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
098f4a50 210 else:
8911f226
ER
211 movedany = True
212 dsttree[self.nvr].files[arch] = self.files[arch]
151d31cc 213 for rpm in self.files[arch]:
54ff0049
ER
214 if self.is_debuginfo(rpm):
215 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
216 else:
217 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
213a164a 218 if not test and movedany:
098f4a50 219 for bid in self.build.keys():
8911f226 220 dsttree[self.nvr].build[bid] = self.build[bid]
85f3481a 221 dsttree[self.nvr].writeinfo()
8911f226 222 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
098f4a50 223 else:
8911f226 224 # move files
151d31cc
MM
225 for arch in self.files.keys():
226 for rpm in self.files[arch]:
54ff0049
ER
227 if self.is_debuginfo(rpm):
228 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
229 else:
230 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
098f4a50 231
8911f226
ER
232 # move metadata
233 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
098f4a50 234
151d31cc 235class FtpTree(BaseFtpTree):
098f4a50 236 def __init__(self, tree, loadall=False):
151d31cc 237 BaseFtpTree.__init__(self, tree)
8911f226
ER
238 self.loadedpkgs = {}
239 self.marked4removal = []
240 self.marked4moving = []
241 self.pkgnames = []
d9b3388c 242 self.__loadpkgnames()
098f4a50 243 if loadall:
d9b3388c 244 for pkgname in self.pkgnames:
8911f226 245 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
796b7867 246 # Tests:
8911f226 247 self.do_checkbuild = True
d9b3388c 248
098f4a50
MM
249 def __getitem__(self, key):
250 if self.loadedpkgs.has_key(key):
251 return self.loadedpkgs[key]
252 elif key in self.pkgnames:
253 pkg=Pkg(key, self)
254 self.loadedpkgs[key]=pkg
255 return pkg
256 else:
257 raise KeyError, key
796b7867 258
098f4a50
MM
259 def has_key(self, key):
260 if key in self.pkgnames:
261 return True
262 else:
263 return False
796b7867 264
1610d209
MM
265 def keys(self):
266 return self.pkgnames
098f4a50 267
d7667ebe
MM
268 def values(self):
269 return self.loadedpkgs.values()
270
67b5bf38 271 def checktree(self, dsttree):
85f3481a
MM
272 self.__checkbuild(self.loadedpkgs.values())
273 self.__checkarchs(dsttree, self.loadedpkgs.values())
0767aa5e 274
213a164a 275 def testmove(self, dsttree, archivetree = None):
67b5bf38
MM
276 self.__checkbuild(self.marked4moving)
277 self.__checkarchs(dsttree, self.marked4moving)
aeb928ae
ER
278
279 self.__checksigns(dsttree, self.marked4moving, test = True)
f6dff636 280 self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
a0b52be0 281
8911f226 282 self.__rmolderfromsrc(test = True)
213a164a 283 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
a0b52be0
AM
284
285 for pkg in self.marked4moving:
8911f226 286 pkg.move(dsttree, test = True)
9b00920e 287
213a164a 288 def movepkgs(self, dsttree, archivetree = None):
f053c9e6 289 if self.do_checkbuild:
67b5bf38 290 self.__checkbuild(self.marked4moving)
098f4a50 291 bailoutonerror()
aeb928ae 292
67b5bf38 293 self.__checkarchs(dsttree, self.marked4moving)
098f4a50 294 bailoutonerror()
aeb928ae
ER
295
296 self.__checksigns(dsttree, self.marked4moving)
297 bailoutonerror()
298
098f4a50 299 self.__rmolderfromsrc()
213a164a 300 self.__rmotherfromdst(dsttree, archivetree = archivetree)
098f4a50 301
14085d11 302 for pkg in self.marked4moving:
098f4a50
MM
303 pkg.move(dsttree)
304
d38e8382
ER
305 def rpmfiles(self):
306 if self.do_checkbuild:
307 self.__checkbuild(self.marked4moving)
d38e8382
ER
308
309 files = []
310 for pkg in self.marked4moving:
311 files += pkg.rpmfiles()
312 return files
313
14085d11 314 def removepkgs(self):
6bc6286e 315 if self.do_checkbuild:
67b5bf38 316 self.__checkbuild(self.marked4removal)
6bc6286e 317 bailoutonerror()
14085d11
MM
318 for pkg in self.marked4removal:
319 pkg.remove()
320
321 def mark4removal(self, wannabepkgs):
322 self.__mark4something(wannabepkgs, Pkg.mark4removal)
323
324 def mark4moving(self, wannabepkgs):
325 self.__mark4something(wannabepkgs, Pkg.mark4moving)
326
327
328 # Internal functions below
329
d9b3388c
MM
330 def __loadpkgnames(self):
331 def checkfiletype(name):
332 if name[-13:]=='.src.rpm.info':
333 return True
334 else:
335 return False
8911f226
ER
336 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
337 self.pkgnames = map((lambda x: x[:-13]), list)
d9b3388c 338
14085d11 339 def __mark4something(self, wannabepkgs, markfunction):
098f4a50 340 def chopoffextension(pkg):
f6dff636
ER
341 found = pkg.find('.src.rpm')
342 if found == -1:
098f4a50
MM
343 return pkg
344 else:
345 return pkg[:found]
f6dff636 346
098f4a50 347 for wannabepkg in wannabepkgs:
f6dff636 348 pkgname = chopoffextension(wannabepkg)
098f4a50
MM
349 if pkgname in self.pkgnames:
350 if not pkgname in self.loadedpkgs.keys():
351 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
14085d11 352 markfunction(self.loadedpkgs[pkgname])
098f4a50 353 else:
492b6398 354 perror('%s not found in source tree' % pkgname)
098f4a50 355 bailoutonerror()
14085d11 356
67b5bf38 357 def __checkbuild(self, marked):
8911f226
ER
358 """
359 Checks queue file if all arches are built
360
361 Reads config.builderqueue to grab the info
362 """
363 f = urllib.urlopen(config.builderqueue)
364 requests = {}
365 reid = re.compile(r'^.*id=(.*) pri.*$')
366 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
098f4a50 367 for i in re.findall(regb, f.read()):
8911f226
ER
368 if i[0] == 'g':
369 id = reid.sub(r'\1', i)
370 requests[id] = ""
098f4a50 371 elif i[0]=='b':
8911f226 372 requests[id] = requests[id] + i
098f4a50 373 f.close()
8911f226 374
6bc6286e 375 for pkg in marked:
098f4a50
MM
376 for bid in pkg.build.keys():
377 if requests.has_key(bid) and not requests[bid].find('?') == -1:
85f3481a 378 pkg.error("(buildid %s) building not finished" % bid)
098f4a50 379
67b5bf38 380 def __checkarchs(self, dsttree, marked):
8911f226
ER
381 """
382 Checks marked pkgs it is built on all archs.
383 """
67b5bf38 384 for pkg in marked:
4802753d 385 if len(pkg.files.keys()) <= 1:
85f3481a 386 pkg.error('has only src.rpm built')
4802753d 387 continue
8911f226
ER
388 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
389
390 # check if we're not removing some archs
391 if otherpkgnames:
392 curarchs = []
393 missingarchs = []
796b7867
MM
394 for somepkg in otherpkgnames:
395 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
396 for arch in curarchs:
397 if arch not in pkg.files.keys():
398 missingarchs.append(arch)
399 if missingarchs:
85f3481a 400 pkg.error('moving would remove archs: %s' % missingarchs)
8911f226
ER
401 else:
402 # warn if a package isn't built for all archs
403 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
492b6398 404 continue
8911f226
ER
405 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
406 missingarchs = []
492b6398
MM
407 for arch in config.ftp_archs:
408 if arch not in pkg.files.keys():
409 missingarchs.append(arch)
85f3481a 410 pkg.warning('not built for archs: %s' % missingarchs)
098f4a50 411
213a164a 412 def __rmolderfromsrc(self, test = False):
14085d11 413 for pkg in self.marked4moving:
8911f226 414 olderpkgnames = self.__find_older_pkgs(pkg)
098f4a50 415 for i in olderpkgnames:
a0b52be0 416 Pkg(i, self).remove(test)
098f4a50 417
213a164a 418 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
14085d11 419 for pkg in self.marked4moving:
8911f226 420 pkgnames = self.__find_other_pkgs(pkg, dsttree)
098f4a50 421 for i in pkgnames:
213a164a
ER
422 if archivetree == None:
423 Pkg(i, dsttree).remove(test)
424 else:
425 Pkg(i, dsttree).move(archivetree, test = test)
098f4a50 426
d9b3388c 427 # Used more than once filter functions
d9b3388c 428 def __find_other_pkgs(self, pkg, tree):
8911f226
ER
429 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
430 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
d9b3388c 431 def filter_other_pkgs(x):
85f3481a 432 if ziewre.match(x) and not x == pkg.nvr:
d9b3388c
MM
433 return True
434 else:
435 return False
436 return filter(filter_other_pkgs, tree.pkgnames)
437
438 def __find_older_pkgs(self, pkg):
439 def filter_older_pkgs(x):
8911f226 440 c = x.split('-')
ce470bd9
MM
441 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
442 ('0', c[-2], c[-1]))
443 if rc == 1: # pkg > x
d9b3388c 444 return True
d9b3388c
MM
445 else:
446 return False
447 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
448
aeb928ae
ER
449 def __checksigns(self, tree, pkgs, test = False):
450 """
451 Checks if pkgs in tree are all signed.
452
453 in case of test = true, error flag is set for unsigned packages
454 """
455 if not tree.treename in config.signed_trees:
456 return
457
458 for pkg in pkgs:
459 unsigned = 0
460 for file in pkg.rpmfiles():
461 if not is_signed(file):
f6dff636 462 unsigned += 1
aeb928ae
ER
463
464 if unsigned != 0:
465 if test == True:
466 if not quietmode:
467 pkg.warning('%d files not signed' % unsigned)
468 else:
469 pkg.error('%d files not signed' % unsigned)
f6dff636
ER
470
471 def __checkforobsoletes(self, tree, pkgs, test = False):
472 """
473 Checks queue file if package obsoletes something in destination tree and suggest for removal.
474
475 Only NAME tag is compared, i.e virtual packages do not get reported.
476
477 """
478 if test != True:
479 return
480
481 def findbyname(name):
482 def x(nvr):
483 return '-'.join(nvr.split('-')[:-2]) == name
484 return filter(x, tree.pkgnames)
485
486 for pkg in pkgs:
487 obsoletes = pkg.obsoletes()
488 if not obsoletes:
489 continue
490
491 for pn, setlist in obsoletes.items():
492 for item in setlist:
493 p = findbyname(item)
494 if p:
495 pkg.warning('obsoletes %s in dest tree, perhaps you want rmpkg' % (p,))
This page took 0.657032 seconds and 4 git commands to generate.