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