]> git.pld-linux.org Git - projects/pld-ftp-admin.git/blame - modules/ftptree.py
- don't die on lack of FTPADM
[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
331
d9b3388c
MM
332 def __loadpkgnames(self):
333 def checkfiletype(name):
334 if name[-13:]=='.src.rpm.info':
335 return True
336 else:
337 return False
8911f226
ER
338 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
339 self.pkgnames = map((lambda x: x[:-13]), list)
d9b3388c 340
14085d11 341 def __mark4something(self, wannabepkgs, markfunction):
098f4a50 342 def chopoffextension(pkg):
f6dff636
ER
343 found = pkg.find('.src.rpm')
344 if found == -1:
098f4a50
MM
345 return pkg
346 else:
347 return pkg[:found]
f6dff636 348
098f4a50 349 for wannabepkg in wannabepkgs:
f6dff636 350 pkgname = chopoffextension(wannabepkg)
098f4a50
MM
351 if pkgname in self.pkgnames:
352 if not pkgname in self.loadedpkgs.keys():
353 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
14085d11 354 markfunction(self.loadedpkgs[pkgname])
098f4a50 355 else:
492b6398 356 perror('%s not found in source tree' % pkgname)
098f4a50 357 bailoutonerror()
14085d11 358
67b5bf38 359 def __checkbuild(self, marked):
8911f226
ER
360 """
361 Checks queue file if all arches are built
362
363 Reads config.builderqueue to grab the info
364 """
365 f = urllib.urlopen(config.builderqueue)
366 requests = {}
367 reid = re.compile(r'^.*id=(.*) pri.*$')
368 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
098f4a50 369 for i in re.findall(regb, f.read()):
8911f226
ER
370 if i[0] == 'g':
371 id = reid.sub(r'\1', i)
372 requests[id] = ""
098f4a50 373 elif i[0]=='b':
8911f226 374 requests[id] = requests[id] + i
098f4a50 375 f.close()
8911f226 376
6bc6286e 377 for pkg in marked:
098f4a50
MM
378 for bid in pkg.build.keys():
379 if requests.has_key(bid) and not requests[bid].find('?') == -1:
85f3481a 380 pkg.error("(buildid %s) building not finished" % bid)
098f4a50 381
67b5bf38 382 def __checkarchs(self, dsttree, marked):
8911f226
ER
383 """
384 Checks marked pkgs it is built on all archs.
385 """
67b5bf38 386 for pkg in marked:
4802753d 387 if len(pkg.files.keys()) <= 1:
85f3481a 388 pkg.error('has only src.rpm built')
4802753d 389 continue
8911f226
ER
390 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
391
392 # check if we're not removing some archs
393 if otherpkgnames:
394 curarchs = []
395 missingarchs = []
796b7867
MM
396 for somepkg in otherpkgnames:
397 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
398 for arch in curarchs:
399 if arch not in pkg.files.keys():
400 missingarchs.append(arch)
401 if missingarchs:
85f3481a 402 pkg.error('moving would remove archs: %s' % missingarchs)
8911f226
ER
403 else:
404 # warn if a package isn't built for all archs
405 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
492b6398 406 continue
8911f226
ER
407 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
408 missingarchs = []
492b6398
MM
409 for arch in config.ftp_archs:
410 if arch not in pkg.files.keys():
411 missingarchs.append(arch)
85f3481a 412 pkg.warning('not built for archs: %s' % missingarchs)
098f4a50 413
213a164a 414 def __rmolderfromsrc(self, test = False):
14085d11 415 for pkg in self.marked4moving:
8911f226 416 olderpkgnames = self.__find_older_pkgs(pkg)
098f4a50 417 for i in olderpkgnames:
a0b52be0 418 Pkg(i, self).remove(test)
098f4a50 419
213a164a 420 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
14085d11 421 for pkg in self.marked4moving:
8911f226 422 pkgnames = self.__find_other_pkgs(pkg, dsttree)
098f4a50 423 for i in pkgnames:
213a164a
ER
424 if archivetree == None:
425 Pkg(i, dsttree).remove(test)
426 else:
427 Pkg(i, dsttree).move(archivetree, test = test)
098f4a50 428
d9b3388c 429 # Used more than once filter functions
d9b3388c 430 def __find_other_pkgs(self, pkg, tree):
8911f226
ER
431 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
432 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
d9b3388c 433 def filter_other_pkgs(x):
85f3481a 434 if ziewre.match(x) and not x == pkg.nvr:
d9b3388c
MM
435 return True
436 else:
437 return False
438 return filter(filter_other_pkgs, tree.pkgnames)
439
440 def __find_older_pkgs(self, pkg):
441 def filter_older_pkgs(x):
8911f226 442 c = x.split('-')
ce470bd9
MM
443 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
444 ('0', c[-2], c[-1]))
445 if rc == 1: # pkg > x
d9b3388c 446 return True
d9b3388c
MM
447 else:
448 return False
449 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
450
aeb928ae
ER
451 def __checksigns(self, tree, pkgs, test = False):
452 """
453 Checks if pkgs in tree are all signed.
454
455 in case of test = true, error flag is set for unsigned packages
456 """
457 if not tree.treename in config.signed_trees:
458 return
459
460 for pkg in pkgs:
461 unsigned = 0
462 for file in pkg.rpmfiles():
463 if not is_signed(file):
f6dff636 464 unsigned += 1
aeb928ae
ER
465
466 if unsigned != 0:
467 if test == True:
468 if not quietmode:
469 pkg.warning('%d files not signed' % unsigned)
470 else:
471 pkg.error('%d files not signed' % unsigned)
f6dff636
ER
472
473 def __checkforobsoletes(self, tree, pkgs, test = False):
474 """
475 Checks queue file if package obsoletes something in destination tree and suggest for removal.
476
477 Only NAME tag is compared, i.e virtual packages do not get reported.
478
479 """
480 if test != True:
481 return
482
483 def findbyname(name):
484 def x(nvr):
485 return '-'.join(nvr.split('-')[:-2]) == name
486 return filter(x, tree.pkgnames)
487
488 for pkg in pkgs:
489 obsoletes = pkg.obsoletes()
490 if not obsoletes:
491 continue
492
493 for pn, setlist in obsoletes.items():
494 for item in setlist:
495 p = findbyname(item)
496 if p:
9e047859 497 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
This page took 0.222565 seconds and 4 git commands to generate.