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