]> git.pld-linux.org Git - projects/pld-ftp-admin.git/blame - modules/ftptree.py
- allow skipping debugfiles in Pkg.rpmfiles
[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
8643d2cd
ER
93 # returns true if package build is integer
94 def is_release(self):
95 """
96 To account Release tags with subver macros, we consider integer release
97 if it contains odd number of dots:
98
99 1 -> True
100 0.1 -> False
101 0.%{subver}.%{rel}, %{rel} = 1 -> 0.20010.1 -> True
102 0.%{subver}.%{rel}, %{rel} = 0.1 -> 0.20010.0.1 -> False
103 """
1af4af39 104 return self.release.count('.') % 2 == 0
8643d2cd 105
14085d11
MM
106 def mark4moving(self):
107 if not self.marked4moving:
f301e305
MM
108 # Only one pkg in this pool can be marked for moving
109 for pkg in self.marked4movingpool:
110 pkg.unmark4moving()
14085d11
MM
111 self.tree.marked4moving.append(self)
112 self.marked4moving=True
113
f301e305
MM
114 def unmark4moving(self):
115 if self.marked4moving:
116 self.tree.marked4moving.remove(self)
117 self.marked4moving=False
118
14085d11
MM
119 def mark4removal(self):
120 if not self.marked4removal:
121 self.tree.marked4removal.append(self)
122 self.marked4removal=True
098f4a50 123
85f3481a
MM
124 def error(self, msg):
125 self.errors.append(msg)
126 if not quietmode:
127 perror('%s %s' % (self.nvr, msg))
128
129 def warning(self, msg):
130 self.warnings.append(msg)
131 if not quietmode:
132 pwarning('%s %s' % (self.nvr, msg))
133
bb2cb325
MM
134 def load(self, content=None):
135 BasePkg.load(self, content)
098f4a50 136 if self.info.has_key('move'):
14085d11 137 self.mark4moving()
098f4a50
MM
138
139 def writeinfo(self):
8911f226 140 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
098f4a50
MM
141 for bid in self.build.keys():
142 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))
143 for key in self.info.keys():
144 f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
151d31cc
MM
145 for arch in self.files.keys():
146 for rpm in self.files[arch]:
098f4a50
MM
147 f.write("file:%s:%s\n" % (arch, rpm))
148
8911f226
ER
149 def remove(self, test = False):
150 """
151 Remove package from ftp
152 """
151d31cc
MM
153 for arch in self.files.keys():
154 for rpm in self.files[arch]:
54ff0049
ER
155 if self.is_debuginfo(rpm):
156 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
157 else:
158 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
8911f226
ER
159 if arch == 'noarch':
160 if fileexists(noarchcachedir + rpm + '.filelist'):
161 rm(noarchcachedir + rpm + '.filelist', test)
162 if fileexists(noarchcachedir + rpm + '.reqlist'):
163 rm(noarchcachedir + rpm + '.reqlist', test)
164 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
098f4a50 165
3dd276bd 166 def rpmfiles(self, debugfiles = True):
d38e8382
ER
167 """
168 Return rpm files related to this package
169 """
170 files = []
171 for arch, rpms in self.files.items():
172 for nvr in rpms:
173 if self.is_debuginfo(nvr):
3dd276bd
ER
174 if debugfiles:
175 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
d38e8382
ER
176 else:
177 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
178 return files
179
f6dff636
ER
180 def obsoletes(self):
181 """
182 Return obsoletes for all packages in Pkg:
183
184 {'php-geshi': set(['geshi'])}
185
186 """
187 def rpmhdr(pkg):
188 ts = rpm.ts()
189 ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
190 fdno = os.open(pkg, os.O_RDONLY)
191 hdr = ts.hdrFromFdno(fdno)
192 os.close(fdno)
193 return hdr
194
195 obsoletes = {}
196 for rpmfile in self.rpmfiles():
c5d9d3e9
AM
197 if not os.path.exists(rpmfile):
198 continue
f6dff636
ER
199 hdr = rpmhdr(rpmfile)
200 if not hdr[rpm.RPMTAG_OBSOLETES]:
201 continue
202
203 name = hdr[rpm.RPMTAG_NAME]
204 if not name in obsoletes:
205 obsoletes[name] = set()
206
207 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
208 obsoletes[name].add(tag)
209
210 return obsoletes
211
213a164a 212 def move(self, dsttree, test = False):
85f3481a 213 if dsttree.has_key(self.nvr):
8911f226 214 movedany = False
151d31cc 215 for arch in self.files.keys():
85f3481a 216 if arch in dsttree[self.nvr].files.keys():
77b616ee
AM
217 msg = ""
218 if test:
219 msg = "TEST "
220 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
151d31cc 221 for rpm in self.files[arch]:
3dd276bd 222 if self.il-page-warningus_debuginfo(rpm):
54ff0049
ER
223 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
224 else:
225 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
098f4a50 226 else:
8911f226
ER
227 movedany = True
228 dsttree[self.nvr].files[arch] = self.files[arch]
151d31cc 229 for rpm in self.files[arch]:
54ff0049
ER
230 if self.is_debuginfo(rpm):
231 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
232 else:
233 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
213a164a 234 if not test and movedany:
098f4a50 235 for bid in self.build.keys():
8911f226 236 dsttree[self.nvr].build[bid] = self.build[bid]
85f3481a 237 dsttree[self.nvr].writeinfo()
8911f226 238 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
098f4a50 239 else:
8911f226 240 # move files
151d31cc
MM
241 for arch in self.files.keys():
242 for rpm in self.files[arch]:
54ff0049
ER
243 if self.is_debuginfo(rpm):
244 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
245 else:
246 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
098f4a50 247
8911f226
ER
248 # move metadata
249 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
098f4a50 250
151d31cc 251class FtpTree(BaseFtpTree):
098f4a50 252 def __init__(self, tree, loadall=False):
151d31cc 253 BaseFtpTree.__init__(self, tree)
8911f226
ER
254 self.loadedpkgs = {}
255 self.marked4removal = []
256 self.marked4moving = []
257 self.pkgnames = []
d9b3388c 258 self.__loadpkgnames()
098f4a50 259 if loadall:
d9b3388c 260 for pkgname in self.pkgnames:
8911f226 261 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
796b7867 262 # Tests:
8911f226 263 self.do_checkbuild = True
d9b3388c 264
098f4a50
MM
265 def __getitem__(self, key):
266 if self.loadedpkgs.has_key(key):
267 return self.loadedpkgs[key]
268 elif key in self.pkgnames:
269 pkg=Pkg(key, self)
270 self.loadedpkgs[key]=pkg
271 return pkg
272 else:
273 raise KeyError, key
796b7867 274
098f4a50
MM
275 def has_key(self, key):
276 if key in self.pkgnames:
277 return True
278 else:
279 return False
796b7867 280
1610d209
MM
281 def keys(self):
282 return self.pkgnames
098f4a50 283
d7667ebe
MM
284 def values(self):
285 return self.loadedpkgs.values()
286
67b5bf38 287 def checktree(self, dsttree):
85f3481a
MM
288 self.__checkbuild(self.loadedpkgs.values())
289 self.__checkarchs(dsttree, self.loadedpkgs.values())
0767aa5e 290
213a164a 291 def testmove(self, dsttree, archivetree = None):
67b5bf38
MM
292 self.__checkbuild(self.marked4moving)
293 self.__checkarchs(dsttree, self.marked4moving)
aeb928ae
ER
294
295 self.__checksigns(dsttree, self.marked4moving, test = True)
f6dff636 296 self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
8643d2cd 297 self.__checkforrelease(dsttree, self.marked4moving, test = True)
a0b52be0 298
8911f226 299 self.__rmolderfromsrc(test = True)
213a164a 300 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
a0b52be0
AM
301
302 for pkg in self.marked4moving:
8911f226 303 pkg.move(dsttree, test = True)
9b00920e 304
213a164a 305 def movepkgs(self, dsttree, archivetree = None):
f053c9e6 306 if self.do_checkbuild:
67b5bf38 307 self.__checkbuild(self.marked4moving)
098f4a50 308 bailoutonerror()
aeb928ae 309
67b5bf38 310 self.__checkarchs(dsttree, self.marked4moving)
098f4a50 311 bailoutonerror()
aeb928ae
ER
312
313 self.__checksigns(dsttree, self.marked4moving)
314 bailoutonerror()
315
098f4a50 316 self.__rmolderfromsrc()
213a164a 317 self.__rmotherfromdst(dsttree, archivetree = archivetree)
098f4a50 318
14085d11 319 for pkg in self.marked4moving:
098f4a50
MM
320 pkg.move(dsttree)
321
d38e8382
ER
322 def rpmfiles(self):
323 if self.do_checkbuild:
324 self.__checkbuild(self.marked4moving)
d38e8382
ER
325
326 files = []
327 for pkg in self.marked4moving:
328 files += pkg.rpmfiles()
329 return files
330
14085d11 331 def removepkgs(self):
6bc6286e 332 if self.do_checkbuild:
67b5bf38 333 self.__checkbuild(self.marked4removal)
6bc6286e 334 bailoutonerror()
14085d11
MM
335 for pkg in self.marked4removal:
336 pkg.remove()
337
338 def mark4removal(self, wannabepkgs):
339 self.__mark4something(wannabepkgs, Pkg.mark4removal)
340
341 def mark4moving(self, wannabepkgs):
342 self.__mark4something(wannabepkgs, Pkg.mark4moving)
343
344
345 # Internal functions below
b36df4e6
ER
346 def __arch_stringify(self, list):
347 ret = []
348 # XXX: is dist line in any config?
349 dist = 'ac'
350 for arch in list:
351 ret.append(dist + '-' + arch)
352 return ' '.join(ret)
14085d11 353
d9b3388c
MM
354 def __loadpkgnames(self):
355 def checkfiletype(name):
356 if name[-13:]=='.src.rpm.info':
357 return True
358 else:
359 return False
8911f226
ER
360 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
361 self.pkgnames = map((lambda x: x[:-13]), list)
d9b3388c 362
14085d11 363 def __mark4something(self, wannabepkgs, markfunction):
098f4a50 364 def chopoffextension(pkg):
f6dff636
ER
365 found = pkg.find('.src.rpm')
366 if found == -1:
098f4a50
MM
367 return pkg
368 else:
369 return pkg[:found]
f6dff636 370
098f4a50 371 for wannabepkg in wannabepkgs:
f6dff636 372 pkgname = chopoffextension(wannabepkg)
098f4a50
MM
373 if pkgname in self.pkgnames:
374 if not pkgname in self.loadedpkgs.keys():
375 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
14085d11 376 markfunction(self.loadedpkgs[pkgname])
098f4a50 377 else:
492b6398 378 perror('%s not found in source tree' % pkgname)
098f4a50 379 bailoutonerror()
14085d11 380
67b5bf38 381 def __checkbuild(self, marked):
8911f226
ER
382 """
383 Checks queue file if all arches are built
384
385 Reads config.builderqueue to grab the info
386 """
387 f = urllib.urlopen(config.builderqueue)
388 requests = {}
389 reid = re.compile(r'^.*id=(.*) pri.*$')
390 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
098f4a50 391 for i in re.findall(regb, f.read()):
8911f226
ER
392 if i[0] == 'g':
393 id = reid.sub(r'\1', i)
394 requests[id] = ""
098f4a50 395 elif i[0]=='b':
8911f226 396 requests[id] = requests[id] + i
098f4a50 397 f.close()
8911f226 398
6bc6286e 399 for pkg in marked:
098f4a50
MM
400 for bid in pkg.build.keys():
401 if requests.has_key(bid) and not requests[bid].find('?') == -1:
85f3481a 402 pkg.error("(buildid %s) building not finished" % bid)
098f4a50 403
67b5bf38 404 def __checkarchs(self, dsttree, marked):
8911f226
ER
405 """
406 Checks marked pkgs it is built on all archs.
407 """
67b5bf38 408 for pkg in marked:
4802753d 409 if len(pkg.files.keys()) <= 1:
85f3481a 410 pkg.error('has only src.rpm built')
4802753d 411 continue
8911f226
ER
412 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
413
414 # check if we're not removing some archs
415 if otherpkgnames:
416 curarchs = []
417 missingarchs = []
796b7867
MM
418 for somepkg in otherpkgnames:
419 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
420 for arch in curarchs:
421 if arch not in pkg.files.keys():
422 missingarchs.append(arch)
423 if missingarchs:
b36df4e6 424 pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
8911f226
ER
425 else:
426 # warn if a package isn't built for all archs
427 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
492b6398 428 continue
8911f226
ER
429 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
430 missingarchs = []
492b6398
MM
431 for arch in config.ftp_archs:
432 if arch not in pkg.files.keys():
433 missingarchs.append(arch)
b36df4e6 434 pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
098f4a50 435
213a164a 436 def __rmolderfromsrc(self, test = False):
14085d11 437 for pkg in self.marked4moving:
8911f226 438 olderpkgnames = self.__find_older_pkgs(pkg)
098f4a50 439 for i in olderpkgnames:
a0b52be0 440 Pkg(i, self).remove(test)
098f4a50 441
213a164a 442 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
14085d11 443 for pkg in self.marked4moving:
8911f226 444 pkgnames = self.__find_other_pkgs(pkg, dsttree)
098f4a50 445 for i in pkgnames:
213a164a
ER
446 if archivetree == None:
447 Pkg(i, dsttree).remove(test)
448 else:
449 Pkg(i, dsttree).move(archivetree, test = test)
098f4a50 450
d9b3388c 451 # Used more than once filter functions
d9b3388c 452 def __find_other_pkgs(self, pkg, tree):
8911f226
ER
453 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
454 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
d9b3388c 455 def filter_other_pkgs(x):
85f3481a 456 if ziewre.match(x) and not x == pkg.nvr:
d9b3388c
MM
457 return True
458 else:
459 return False
460 return filter(filter_other_pkgs, tree.pkgnames)
461
462 def __find_older_pkgs(self, pkg):
463 def filter_older_pkgs(x):
8911f226 464 c = x.split('-')
ce470bd9
MM
465 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
466 ('0', c[-2], c[-1]))
467 if rc == 1: # pkg > x
d9b3388c 468 return True
d9b3388c
MM
469 else:
470 return False
471 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
472
aeb928ae
ER
473 def __checksigns(self, tree, pkgs, test = False):
474 """
475 Checks if pkgs in tree are all signed.
476
477 in case of test = true, error flag is set for unsigned packages
478 """
479 if not tree.treename in config.signed_trees:
480 return
481
482 for pkg in pkgs:
483 unsigned = 0
484 for file in pkg.rpmfiles():
485 if not is_signed(file):
f6dff636 486 unsigned += 1
aeb928ae
ER
487
488 if unsigned != 0:
489 if test == True:
490 if not quietmode:
491 pkg.warning('%d files not signed' % unsigned)
492 else:
493 pkg.error('%d files not signed' % unsigned)
f6dff636
ER
494
495 def __checkforobsoletes(self, tree, pkgs, test = False):
496 """
497 Checks queue file if package obsoletes something in destination tree and suggest for removal.
498
499 Only NAME tag is compared, i.e virtual packages do not get reported.
500
501 """
502 if test != True:
503 return
504
505 def findbyname(name):
506 def x(nvr):
507 return '-'.join(nvr.split('-')[:-2]) == name
508 return filter(x, tree.pkgnames)
509
510 for pkg in pkgs:
511 obsoletes = pkg.obsoletes()
512 if not obsoletes:
513 continue
514
515 for pn, setlist in obsoletes.items():
516 for item in setlist:
517 p = findbyname(item)
518 if p:
9e047859 519 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
8643d2cd
ER
520
521 def __checkforrelease(self, tree, pkgs, test = False):
522 """
523 Checks queue file if package release is non integer.
524
525 """
526 if test != True:
527 return
528
529 for pkg in pkgs:
530 if not pkg.is_release():
531 pkg.warning('non-integer release: %s' % pkg.release)
This page took 0.18158 seconds and 4 git commands to generate.