Reuse cached rpm between yum and zypp, making the same package dir for them
[tools/mic.git] / plugins / backend / zypppkgmgr.py
1 #!/usr/bin/python -tt
2 #
3 # Copyright (c) 2010, 2011 Intel, Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation; version 2 of the License
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 import os
19 import shutil
20 import urlparse
21 import rpm
22
23 import zypp
24 if not hasattr(zypp, 'PoolQuery') or \
25    not hasattr(zypp.RepoManager, 'loadSolvFile'):
26     raise ImportError("python-zypp in host system cannot support PoolQuery or "
27                       "loadSolvFile interface, please update it to enhanced "
28                       "version which can be found in repo.meego.com/tools")
29
30 from mic import msger
31 from mic.kickstart import ksparser
32 from mic.utils import misc, rpmmisc, runner, fs_related
33 from mic.utils.proxy import get_proxy_for
34 from mic.utils.errors import CreatorError, RepoError, RpmError
35 from mic.imager.baseimager import BaseImageCreator
36
37 class RepositoryStub:
38     def __init__(self):
39         self.name = None
40         self.baseurl = []
41         self.mirrorlist = None
42         self.proxy = None
43         self.proxy_username = None
44         self.proxy_password = None
45
46         self.enabled = True
47         self.autorefresh = True
48         self.keeppackages = True
49         self.priority = None
50
51 from mic.pluginbase import BackendPlugin
52 class Zypp(BackendPlugin):
53     name = 'zypp'
54
55     def __init__(self, target_arch, instroot, cachedir):
56         self.cachedir = cachedir
57         self.instroot  = instroot
58         self.target_arch = target_arch
59
60         self.__pkgs_license = {}
61         self.__pkgs_content = {}
62         self.repos = []
63         self.to_deselect = []
64         self.localpkgs = {}
65         self.repo_manager = None
66         self.repo_manager_options = None
67         self.Z = None
68         self.ts = None
69         self.probFilterFlags = []
70         self.incpkgs = {}
71         self.excpkgs = {}
72
73         self.has_prov_query = True
74
75     def doFileLogSetup(self, uid, logfile):
76         # don't do the file log for the livecd as it can lead to open fds
77         # being left and an inability to clean up after ourself
78         pass
79
80     def closeRpmDB(self):
81         pass
82
83     def close(self):
84         if self.ts:
85             self.ts.closeDB()
86             self.ts = None
87
88         self.closeRpmDB()
89
90         if not os.path.exists("/etc/fedora-release") and \
91            not os.path.exists("/etc/meego-release"):
92             for i in range(3, os.sysconf("SC_OPEN_MAX")):
93                 try:
94                     os.close(i)
95                 except:
96                     pass
97
98     def __del__(self):
99         self.close()
100
101     def _cleanupRpmdbLocks(self, installroot):
102         # cleans up temporary files left by bdb so that differing
103         # versions of rpm don't cause problems
104         import glob
105         for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
106             os.unlink(f)
107
108     def setup(self):
109         self._cleanupRpmdbLocks(self.instroot)
110
111     def whatObsolete(self, pkg):
112         query = zypp.PoolQuery()
113         query.addKind(zypp.ResKind.package)
114         query.addAttribute(zypp.SolvAttr.obsoletes, pkg)
115         query.setMatchExact()
116         for pi in query.queryResults(self.Z.pool()):
117             return pi
118         return None
119
120     def _splitPkgString(self, pkg):
121         sp = pkg.rsplit(".",1)
122         name = sp[0]
123         arch = None
124         if len(sp) == 2:
125             arch = sp[1]
126             sysarch = zypp.Arch(self.target_arch)
127             if not zypp.Arch(arch).compatible_with (sysarch):
128                 arch = None
129                 name = ".".join(sp)
130         return name, arch
131
132     def selectPackage(self, pkg):
133         """Select a given package or package pattern, can be specified
134         with name.arch or name* or *name
135         """
136
137         if not self.Z:
138             self.__initialize_zypp()
139
140         def markPoolItem(obs, pi):
141             if obs == None:
142                 pi.status().setToBeInstalled (zypp.ResStatus.USER)
143             else:
144                 obs.status().setToBeInstalled (zypp.ResStatus.USER)
145
146         found = False
147         startx = pkg.startswith("*")
148         endx = pkg.endswith("*")
149         ispattern = startx or endx
150         name, arch = self._splitPkgString(pkg)
151
152         q = zypp.PoolQuery()
153         q.addKind(zypp.ResKind.package)
154
155         if ispattern:
156             if startx and not endx:
157                 pattern = '%s$' % (pkg[1:])
158             if endx and not startx:
159                 pattern = '^%s' % (pkg[0:-1])
160             if endx and startx:
161                 pattern = '%s' % (pkg[1:-1])
162             q.setMatchRegex()
163             q.addAttribute(zypp.SolvAttr.name,pattern)
164
165         elif arch:
166             q.setMatchExact()
167             q.addAttribute(zypp.SolvAttr.name,name)
168
169         else:
170             q.setMatchExact()
171             q.addAttribute(zypp.SolvAttr.name,pkg)
172
173         for item in sorted(
174                         q.queryResults(self.Z.pool()),
175                         key=lambda item: str(item.edition()),
176                         reverse=True):
177
178             if item.name() in self.excpkgs.keys() and \
179                self.excpkgs[item.name()] == item.repoInfo().name():
180                 continue
181             if item.name() in self.incpkgs.keys() and \
182                self.incpkgs[item.name()] != item.repoInfo().name():
183                 continue
184
185             found = True
186             obspkg = self.whatObsolete(item.name())
187             if arch:
188                 if arch == str(item.arch()):
189                     item.status().setToBeInstalled (zypp.ResStatus.USER)
190             else:
191                 markPoolItem(obspkg, item)
192             if not ispattern:
193                 break
194
195         # Can't match using package name, then search from packge
196         # provides infomation
197         if found == False and not ispattern:
198             q.addAttribute(zypp.SolvAttr.provides, pkg)
199             q.addAttribute(zypp.SolvAttr.name,'')
200
201             for item in sorted(
202                             q.queryResults(self.Z.pool()),
203                             key=lambda item: str(item.edition()),
204                             reverse=True):
205                 if item.name() in self.excpkgs.keys() and \
206                    self.excpkgs[item.name()] == item.repoInfo().name():
207                     continue
208                 if item.name() in self.incpkgs.keys() and \
209                    self.incpkgs[item.name()] != item.repoInfo().name():
210                     continue
211
212                 found = True
213                 obspkg = self.whatObsolete(item.name())
214                 markPoolItem(obspkg, item)
215                 break
216
217         if found:
218             return None
219         else:
220             raise CreatorError("Unable to find package: %s" % (pkg,))
221
222     def inDeselectPackages(self, item):
223         """check if specified pacakges are in the list of inDeselectPackages
224         """
225
226         name = item.name()
227         for pkg in self.to_deselect:
228             startx = pkg.startswith("*")
229             endx = pkg.endswith("*")
230             ispattern = startx or endx
231             pkgname, pkgarch = self._splitPkgString(pkg)
232             if not ispattern:
233                 if pkgarch:
234                     if name == pkgname and str(item.arch()) == pkgarch:
235                         return True;
236                 else:
237                     if name == pkgname:
238                         return True;
239             else:
240                 if startx and name.endswith(pkg[1:]):
241                         return True;
242                 if endx and name.startswith(pkg[:-1]):
243                         return True;
244
245         return False;
246
247     def deselectPackage(self, pkg):
248         """collect packages should not be installed"""
249         self.to_deselect.append(pkg)
250
251     def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
252         if not self.Z:
253             self.__initialize_zypp()
254         found = False
255         q=zypp.PoolQuery()
256         q.addKind(zypp.ResKind.pattern)
257         for item in q.queryResults(self.Z.pool()):
258             summary = "%s" % item.summary()
259             name = "%s" % item.name()
260             if name == grp or summary == grp:
261                 found = True
262                 item.status().setToBeInstalled (zypp.ResStatus.USER)
263                 break
264
265         if found:
266             if include == ksparser.GROUP_REQUIRED:
267                 map(
268                     lambda p: self.deselectPackage(p),
269                     grp.default_packages.keys())
270
271             return None
272         else:
273             raise CreatorError("Unable to find pattern: %s" % (grp,))
274
275     def addRepository(self, name,
276                             url = None,
277                             mirrorlist = None,
278                             proxy = None,
279                             proxy_username = None,
280                             proxy_password = None,
281                             inc = None,
282                             exc = None,
283                             ssl_verify = True,
284                             cost=None,
285                             priority=None):
286         # TODO: Handle cost attribute for repos
287
288         if not self.repo_manager:
289             self.__initialize_repo_manager()
290
291         if not proxy and url:
292             proxy = get_proxy_for(url)
293
294         repo = RepositoryStub()
295         repo.name = name
296         repo.id = name
297         repo.proxy = proxy
298         repo.proxy_username = proxy_username
299         repo.proxy_password = proxy_password
300         repo.ssl_verify = ssl_verify
301         repo.baseurl.append(url)
302         if inc:
303             for pkg in inc:
304                 self.incpkgs[pkg] = name
305         if exc:
306             for pkg in exc:
307                 self.excpkgs[pkg] = name
308
309         # check LICENSE files
310         if not rpmmisc.checkRepositoryEULA(name, repo):
311             msger.warning('skip repo:%s for failed EULA confirmation' % name)
312             return None
313
314         if mirrorlist:
315             repo.mirrorlist = mirrorlist
316
317         # Enable gpg check for verifying corrupt packages
318         repo.gpgcheck = 1
319         if priority:
320             repo.priority = priority
321         self.repos.append(repo)
322
323         try:
324             repo_info = zypp.RepoInfo()
325             repo_info.setAlias(repo.name)
326             repo_info.setName(repo.name)
327             repo_info.setEnabled(repo.enabled)
328             repo_info.setAutorefresh(repo.autorefresh)
329             repo_info.setKeepPackages(repo.keeppackages)
330             baseurl = zypp.Url(repo.baseurl[0])
331             if not ssl_verify:
332                 baseurl.setQueryParam("ssl_verify", "no")
333             if proxy:
334                 scheme, host, path, parm, query, frag = urlparse.urlparse(proxy)
335
336                 proxyinfo = host.split(":")
337                 host = proxyinfo[0]
338
339                 port = "80"
340                 if len(proxyinfo) > 1:
341                     port = proxyinfo[1]
342
343                 if proxy.startswith("socks") and len(proxy.rsplit(':', 1)) == 2:
344                     host = proxy.rsplit(':', 1)[0]
345                     port = proxy.rsplit(':', 1)[1]
346
347                 baseurl.setQueryParam ("proxy", host)
348                 baseurl.setQueryParam ("proxyport", port)
349
350             repo_info.addBaseUrl(baseurl)
351
352             if repo.priority:
353                 repo_info.setPriority(repo.priority)
354
355             self.repo_manager.addRepository(repo_info)
356
357             self.__build_repo_cache(name)
358
359         except RuntimeError, e:
360             raise CreatorError(str(e))
361
362         msger.verbose('repo: %s was added' % name)
363         return repo
364
365     def installHasFile(self, file):
366         return False
367
368     def runInstall(self, checksize = 0):
369         os.environ["HOME"] = "/"
370         self.buildTransaction()
371
372         todo = zypp.GetResolvablesToInsDel(self.Z.pool())
373         installed_pkgs = todo._toInstall
374         dlpkgs = []
375         for item in installed_pkgs:
376             if not zypp.isKindPattern(item) and \
377                not self.inDeselectPackages(item):
378                 dlpkgs.append(item)
379
380         # record all pkg and the content
381         localpkgs = self.localpkgs.keys()
382         for pkg in dlpkgs:
383             license = ''
384             if pkg.name() in localpkgs:
385                 hdr = rpmmisc.readRpmHeader(self.ts, self.localpkgs[pkg.name()])
386                 pkg_long_name = misc.RPM_FMT % {
387                                     'name': hdr['name'],
388                                     'arch': hdr['arch'],
389                                     'ver_rel': '%s-%s' % (hdr['version'],
390                                                           hdr['release']),
391                                 }
392                 license = hdr['license']
393
394             else:
395                 pkg_long_name = misc.RPM_FMT % {
396                                     'name': pkg.name(),
397                                     'arch': pkg.arch(),
398                                     'ver_rel': pkg.edition(),
399                                 }
400
401                 package = zypp.asKindPackage(pkg)
402                 license = package.license()
403
404             self.__pkgs_content[pkg_long_name] = {} #TBD: to get file list
405
406             if license in self.__pkgs_license.keys():
407                 self.__pkgs_license[license].append(pkg_long_name)
408             else:
409                 self.__pkgs_license[license] = [pkg_long_name]
410
411         total_count = len(dlpkgs)
412         cached_count = 0
413         download_total_size = sum(map(lambda x: int(x.downloadSize()), dlpkgs))
414         localpkgs = self.localpkgs.keys()
415
416         msger.info("Checking packages cache and packages integrity ...")
417         for po in dlpkgs:
418             # Check if it is cached locally
419             if po.name() in localpkgs:
420                 cached_count += 1
421             else:
422                 local = self.getLocalPkgPath(po)
423                 if os.path.exists(local):
424                     if self.checkPkg(local) != 0:
425                         os.unlink(local)
426                     else:
427                         download_total_size -= int(po.downloadSize())
428                         cached_count += 1
429         cache_avail_size = misc.get_filesystem_avail(self.cachedir)
430         if cache_avail_size < download_total_size:
431             raise CreatorError("No enough space used for downloading.")
432
433         # record the total size of installed pkgs
434         install_total_size = sum(map(lambda x: int(x.installSize()), dlpkgs))
435         # check needed size before actually download and install
436
437         # FIXME: for multiple partitions for loop type, check fails
438         #        skip the check temporarily
439         #if checksize and install_total_size > checksize:
440         #    raise CreatorError("No enough space used for installing, "
441         #                       "please resize partition size in ks file")
442
443         download_count =  total_count - cached_count
444         msger.info("%d packages to be installed, "
445                    "%d packages gotten from cache, "
446                    "%d packages to be downloaded" \
447                    % (total_count, cached_count, download_count))
448
449         try:
450             if download_count > 0:
451                 msger.info("Downloading packages ...")
452             self.downloadPkgs(dlpkgs, download_count)
453
454             self.installPkgs(dlpkgs)
455
456         except RepoError, e:
457             raise CreatorError("Unable to download from repo : %s" % (e,))
458         except RpmError, e:
459             raise CreatorError("Unable to install: %s" % (e,))
460
461     def getAllContent(self):
462         return self.__pkgs_content
463
464     def getPkgsLicense(self):
465         return self.__pkgs_license
466
467     def __initialize_repo_manager(self):
468         if self.repo_manager:
469             return
470
471         # Clean up repo metadata
472         shutil.rmtree(self.cachedir + "/etc", ignore_errors = True)
473         shutil.rmtree(self.cachedir + "/solv", ignore_errors = True)
474         shutil.rmtree(self.cachedir + "/raw", ignore_errors = True)
475
476         zypp.KeyRing.setDefaultAccept( zypp.KeyRing.ACCEPT_UNSIGNED_FILE
477                                      | zypp.KeyRing.ACCEPT_VERIFICATION_FAILED
478                                      | zypp.KeyRing.ACCEPT_UNKNOWNKEY
479                                      | zypp.KeyRing.TRUST_KEY_TEMPORARILY
480                                      )
481
482         self.repo_manager_options = \
483                 zypp.RepoManagerOptions(zypp.Pathname(self.instroot))
484
485         self.repo_manager_options.knownReposPath = \
486                 zypp.Pathname(self.cachedir + "/etc/zypp/repos.d")
487
488         self.repo_manager_options.repoCachePath = \
489                 zypp.Pathname(self.cachedir)
490
491         self.repo_manager_options.repoRawCachePath = \
492                 zypp.Pathname(self.cachedir + "/raw")
493
494         self.repo_manager_options.repoSolvCachePath = \
495                 zypp.Pathname(self.cachedir + "/solv")
496
497         self.repo_manager_options.repoPackagesCachePath = \
498                 zypp.Pathname(self.cachedir + "/packages")
499
500         self.repo_manager = zypp.RepoManager(self.repo_manager_options)
501
502     def __build_repo_cache(self, name):
503         repo = self.repo_manager.getRepositoryInfo(name)
504         if self.repo_manager.isCached(repo) or not repo.enabled():
505             return
506
507         msger.info('Refreshing repository: %s ...' % name)
508         self.repo_manager.buildCache(repo, zypp.RepoManager.BuildIfNeeded)
509
510     def __initialize_zypp(self):
511         if self.Z:
512             return
513
514         zconfig = zypp.ZConfig_instance()
515
516         # Set system architecture
517         if self.target_arch:
518             zconfig.setSystemArchitecture(zypp.Arch(self.target_arch))
519
520         msger.info("zypp architecture is <%s>" % zconfig.systemArchitecture())
521
522         # repoPackagesCachePath is corrected by this
523         self.repo_manager = zypp.RepoManager(self.repo_manager_options)
524         repos = self.repo_manager.knownRepositories()
525         for repo in repos:
526             if not repo.enabled():
527                 continue
528             self.repo_manager.loadFromCache(repo)
529
530         self.Z = zypp.ZYppFactory_instance().getZYpp()
531         self.Z.initializeTarget(zypp.Pathname(self.instroot))
532         self.Z.target().load()
533
534     def buildTransaction(self):
535         if not self.Z.resolver().resolvePool():
536             probs = self.Z.resolver().problems()
537
538             for problem in probs:
539                 msger.warning("repo problem: %s, %s" \
540                               % (problem.description().decode("utf-8"),
541                                  problem.details().decode("utf-8")))
542
543             raise RepoError("found %d resolver problem, abort!" \
544                             % len(probs))
545
546     def getLocalPkgPath(self, po):
547         repoinfo = po.repoInfo()
548         cacheroot = repoinfo.packagesPath()
549         location= zypp.asKindPackage(po).location()
550         rpmpath = str(location.filename())
551         pkgpath = "%s/%s" % (cacheroot, os.path.basename(rpmpath))
552         return pkgpath
553
554     def installLocal(self, pkg, po=None, updateonly=False):
555         if not self.ts:
556             self.__initialize_transaction()
557
558         solvfile = "%s/.solv" % (self.cachedir)
559
560         rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"),
561                                   pkg])
562         if rc == 0:
563             f = open(solvfile, "w+")
564             f.write(out)
565             f.close()
566
567             warnmsg = self.repo_manager.loadSolvFile(solvfile,
568                                                      os.path.basename(pkg))
569             if warnmsg:
570                 msger.warning(warnmsg)
571
572             os.unlink(solvfile)
573         else:
574             msger.warning('Can not get %s solv data.' % pkg)
575
576         hdr = rpmmisc.readRpmHeader(self.ts, pkg)
577         arch = zypp.Arch(hdr['arch'])
578         sysarch = zypp.Arch(self.target_arch)
579
580         if arch.compatible_with (sysarch):
581             pkgname = hdr['name']
582             self.localpkgs[pkgname] = pkg
583             self.selectPackage(pkgname)
584             msger.info("Marking %s to be installed" % (pkg))
585
586         else:
587             msger.warning("Cannot add package %s to transaction. "
588                           "Not a compatible architecture: %s" \
589                           % (pkg, hdr['arch']))
590
591     def downloadPkgs(self, package_objects, count):
592         localpkgs = self.localpkgs.keys()
593         progress_obj = rpmmisc.TextProgress(count)
594
595         for po in package_objects:
596             if po.name() in localpkgs:
597                 continue
598
599             filename = self.getLocalPkgPath(po)
600             if os.path.exists(filename):
601                 if self.checkPkg(filename) == 0:
602                     continue
603
604             dirn = os.path.dirname(filename)
605             if not os.path.exists(dirn):
606                 os.makedirs(dirn)
607
608             baseurl = str(po.repoInfo().baseUrls()[0])
609             index = baseurl.find("?")
610             if index > -1:
611                 baseurl = baseurl[:index]
612
613             proxy = self.get_proxy(po.repoInfo())
614             proxies = {}
615             if proxy:
616                 proxies = {str(proxy.split(":")[0]):str(proxy)}
617
618             location = zypp.asKindPackage(po).location()
619             location = str(location.filename())
620             if location.startswith("./"):
621                 location = location[2:]
622
623             url = baseurl + "/%s" % location
624
625             try:
626                 filename = rpmmisc.myurlgrab(url,
627                                              filename,
628                                              proxies,
629                                              progress_obj)
630             except CreatorError:
631                 self.close()
632                 raise
633
634     def installPkgs(self, package_objects):
635         if not self.ts:
636             self.__initialize_transaction()
637
638         # Set filters
639         probfilter = 0
640         for flag in self.probFilterFlags:
641             probfilter |= flag
642         self.ts.setProbFilter(probfilter)
643
644         localpkgs = self.localpkgs.keys()
645
646         for po in package_objects:
647             pkgname = po.name()
648             if pkgname in localpkgs:
649                 rpmpath = self.localpkgs[pkgname]
650             else:
651                 rpmpath = self.getLocalPkgPath(po)
652
653             if not os.path.exists(rpmpath):
654                 # Maybe it is a local repo
655                 baseurl = str(po.repoInfo().baseUrls()[0])
656                 baseurl = baseurl.strip()
657
658                 location = zypp.asKindPackage(po).location()
659                 location = str(location.filename())
660
661                 if baseurl.startswith("file:/"):
662                     rpmpath = baseurl[5:] + "/%s" % (location)
663
664             if not os.path.exists(rpmpath):
665                 raise RpmError("Error: %s doesn't exist" % rpmpath)
666
667             h = rpmmisc.readRpmHeader(self.ts, rpmpath)
668             self.ts.addInstall(h, rpmpath, 'u')
669
670         unresolved_dependencies = self.ts.check()
671         if not unresolved_dependencies:
672             self.ts.order()
673             cb = rpmmisc.RPMInstallCallback(self.ts)
674             installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
675
676             # start to catch stderr output from librpm
677             msger.enable_logstderr(installlogfile)
678
679             errors = self.ts.run(cb.callback, '')
680             # stop catch
681             msger.disable_logstderr()
682             self.ts.closeDB()
683             self.ts = None
684
685             if errors is not None:
686                 if len(errors) == 0:
687                     msger.warning('scriptlet or other non-fatal errors occurred '
688                                   'during transaction.')
689
690                 else:
691                     for e in errors:
692                         msger.warning(e[0])
693                     raise RepoError('Could not run transaction.')
694
695         else:
696             for pkg, need, needflags, sense, key in unresolved_dependencies:
697                 package = '-'.join(pkg)
698
699                 if needflags == rpm.RPMSENSE_LESS:
700                     deppkg = ' < '.join(need)
701                 elif needflags == rpm.RPMSENSE_EQUAL:
702                     deppkg = ' = '.join(need)
703                 elif needflags == rpm.RPMSENSE_GREATER:
704                     deppkg = ' > '.join(need)
705                 else:
706                     deppkg = '-'.join(need)
707
708                 if sense == rpm.RPMDEP_SENSE_REQUIRES:
709                     msger.warning("[%s] Requires [%s], which is not provided" \
710                                   % (package, deppkg))
711
712                 elif sense == rpm.RPMDEP_SENSE_CONFLICTS:
713                     msger.warning("[%s] Conflicts with [%s]" %(package,deppkg))
714
715             raise RepoError("Unresolved dependencies, transaction failed.")
716
717     def __initialize_transaction(self):
718         if not self.ts:
719             self.ts = rpm.TransactionSet(self.instroot)
720             # Set to not verify DSA signatures.
721             self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
722
723     def checkPkg(self, pkg):
724         ret = 1
725         if not os.path.exists(pkg):
726             return ret
727         ret = rpmmisc.checkRpmIntegrity('rpm', pkg)
728         if ret != 0:
729             msger.warning("package %s is damaged: %s" \
730                           % (os.path.basename(pkg), pkg))
731
732         return ret
733
734     def _add_prob_flags(self, *flags):
735         for flag in flags:
736            if flag not in self.probFilterFlags:
737                self.probFilterFlags.append(flag)
738
739     def get_proxy(self, repoinfo):
740         proxy = None
741         reponame = "%s" % repoinfo.name()
742         for repo in self.repos:
743             if repo.name == reponame:
744                 proxy = repo.proxy
745                 break
746
747         if proxy:
748             return proxy
749         else:
750             repourl = str(repoinfo.baseUrls()[0])
751             return get_proxy_for(repourl)