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