summaryrefslogtreecommitdiffstats
path: root/scripts/lib
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib')
-rw-r--r--scripts/lib/mic/plugins/backend/yumpkgmgr.py490
-rwxr-xr-xscripts/lib/mic/plugins/backend/zypppkgmgr.py973
2 files changed, 0 insertions, 1463 deletions
diff --git a/scripts/lib/mic/plugins/backend/yumpkgmgr.py b/scripts/lib/mic/plugins/backend/yumpkgmgr.py
deleted file mode 100644
index 955f813109..0000000000
--- a/scripts/lib/mic/plugins/backend/yumpkgmgr.py
+++ /dev/null
@@ -1,490 +0,0 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2007 Red Hat Inc.
4# Copyright (c) 2010, 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19import os, sys
20import re
21import tempfile
22import glob
23from string import Template
24
25import rpmUtils
26import yum
27
28from mic import msger
29from mic.kickstart import ksparser
30from mic.utils import misc, rpmmisc
31from mic.utils.grabber import TextProgress
32from mic.utils.proxy import get_proxy_for
33from mic.utils.errors import CreatorError
34from mic.imager.baseimager import BaseImageCreator
35
36YUMCONF_TEMP = """[main]
37installroot=$installroot
38cachedir=/var/cache/yum
39persistdir=/var/lib/yum
40plugins=0
41reposdir=
42failovermethod=priority
43http_caching=packages
44sslverify=1
45"""
46
47class MyYumRepository(yum.yumRepo.YumRepository):
48 def __del__(self):
49 pass
50
51 def dirSetup(self):
52 super(MyYumRepository, self).dirSetup()
53 # relocate package dir
54 pkgdir = os.path.join(self.basecachedir, 'packages', self.id)
55 self.setAttribute('_dir_setup_pkgdir', pkgdir)
56 self._dirSetupMkdir_p(self.pkgdir)
57
58 def _getFile(self, url=None,
59 relative=None,
60 local=None,
61 start=None,
62 end=None,
63 copy_local=None,
64 checkfunc=None,
65 text=None,
66 reget='simple',
67 cache=True,
68 size=None):
69
70 m2c_connection = None
71 if not self.sslverify:
72 try:
73 import M2Crypto
74 m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck
75 M2Crypto.SSL.Connection.clientPostConnectionCheck = None
76 except ImportError, err:
77 raise CreatorError("%s, please try to install python-m2crypto" % str(err))
78
79 proxy = None
80 if url:
81 proxy = get_proxy_for(url)
82 else:
83 proxy = get_proxy_for(self.urls[0])
84
85 if proxy:
86 self.proxy = str(proxy)
87
88 size = int(size) if size else None
89 rvalue = super(MyYumRepository, self)._getFile(url,
90 relative,
91 local,
92 start,
93 end,
94 copy_local,
95 checkfunc,
96 text,
97 reget,
98 cache,
99 size)
100
101 if m2c_connection and \
102 not M2Crypto.SSL.Connection.clientPostConnectionCheck:
103 M2Crypto.SSL.Connection.clientPostConnectionCheck = m2c_connection
104
105 return rvalue
106
107from mic.pluginbase import BackendPlugin
108class Yum(BackendPlugin, yum.YumBase):
109 name = 'yum'
110
111 def __init__(self, target_arch, instroot, cachedir):
112 yum.YumBase.__init__(self)
113
114 self.cachedir = cachedir
115 self.instroot = instroot
116 self.target_arch = target_arch
117
118 if self.target_arch:
119 if not rpmUtils.arch.arches.has_key(self.target_arch):
120 rpmUtils.arch.arches["armv7hl"] = "noarch"
121 rpmUtils.arch.arches["armv7tnhl"] = "armv7nhl"
122 rpmUtils.arch.arches["armv7tnhl"] = "armv7thl"
123 rpmUtils.arch.arches["armv7thl"] = "armv7hl"
124 rpmUtils.arch.arches["armv7nhl"] = "armv7hl"
125 self.arch.setup_arch(self.target_arch)
126
127 self.__pkgs_license = {}
128 self.__pkgs_content = {}
129 self.__pkgs_vcsinfo = {}
130
131 self.install_debuginfo = False
132
133 def doFileLogSetup(self, uid, logfile):
134 # don't do the file log for the livecd as it can lead to open fds
135 # being left and an inability to clean up after ourself
136 pass
137
138 def close(self):
139 try:
140 os.unlink(self.confpath)
141 os.unlink(self.conf.installroot + "/yum.conf")
142 except:
143 pass
144
145 if self.ts:
146 self.ts.close()
147 self._delRepos()
148 self._delSacks()
149 yum.YumBase.close(self)
150 self.closeRpmDB()
151
152 if not os.path.exists("/etc/fedora-release") and \
153 not os.path.exists("/etc/meego-release"):
154 for i in range(3, os.sysconf("SC_OPEN_MAX")):
155 try:
156 os.close(i)
157 except:
158 pass
159
160 def __del__(self):
161 pass
162
163 def _writeConf(self, confpath, installroot):
164 conf = Template(YUMCONF_TEMP).safe_substitute(installroot=installroot)
165
166 f = file(confpath, "w+")
167 f.write(conf)
168 f.close()
169
170 os.chmod(confpath, 0644)
171
172 def _cleanupRpmdbLocks(self, installroot):
173 # cleans up temporary files left by bdb so that differing
174 # versions of rpm don't cause problems
175 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
176 os.unlink(f)
177
178 def setup(self):
179 # create yum.conf
180 (fn, self.confpath) = tempfile.mkstemp(dir=self.cachedir,
181 prefix='yum.conf-')
182 os.close(fn)
183 self._writeConf(self.confpath, self.instroot)
184 self._cleanupRpmdbLocks(self.instroot)
185 # do setup
186 self.doConfigSetup(fn = self.confpath, root = self.instroot)
187 self.conf.cache = 0
188 self.doTsSetup()
189 self.doRpmDBSetup()
190 self.doRepoSetup()
191 self.doSackSetup()
192
193 def preInstall(self, pkg):
194 # FIXME: handle pre-install package
195 return None
196
197 def selectPackage(self, pkg):
198 """Select a given package.
199 Can be specified with name.arch or name*
200 """
201
202 try:
203 self.install(pattern = pkg)
204 return None
205 except yum.Errors.InstallError:
206 return "No package(s) available to install"
207 except yum.Errors.RepoError, e:
208 raise CreatorError("Unable to download from repo : %s" % (e,))
209 except yum.Errors.YumBaseError, e:
210 raise CreatorError("Unable to install: %s" % (e,))
211
212 def deselectPackage(self, pkg):
213 """Deselect package. Can be specified as name.arch or name*
214 """
215
216 sp = pkg.rsplit(".", 2)
217 txmbrs = []
218 if len(sp) == 2:
219 txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1])
220
221 if len(txmbrs) == 0:
222 exact, match, unmatch = yum.packages.parsePackages(
223 self.pkgSack.returnPackages(),
224 [pkg],
225 casematch=1)
226 for p in exact + match:
227 txmbrs.append(p)
228
229 if len(txmbrs) > 0:
230 for x in txmbrs:
231 self.tsInfo.remove(x.pkgtup)
232 # we also need to remove from the conditionals
233 # dict so that things don't get pulled back in as a result
234 # of them. yes, this is ugly. conditionals should die.
235 for req, pkgs in self.tsInfo.conditionals.iteritems():
236 if x in pkgs:
237 pkgs.remove(x)
238 self.tsInfo.conditionals[req] = pkgs
239 else:
240 msger.warning("No such package %s to remove" %(pkg,))
241
242 def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
243 try:
244 yum.YumBase.selectGroup(self, grp)
245 if include == ksparser.GROUP_REQUIRED:
246 for p in grp.default_packages.keys():
247 self.deselectPackage(p)
248
249 elif include == ksparser.GROUP_ALL:
250 for p in grp.optional_packages.keys():
251 self.selectPackage(p)
252
253 return None
254 except (yum.Errors.InstallError, yum.Errors.GroupsError), e:
255 return e
256 except yum.Errors.RepoError, e:
257 raise CreatorError("Unable to download from repo : %s" % (e,))
258 except yum.Errors.YumBaseError, e:
259 raise CreatorError("Unable to install: %s" % (e,))
260
261 def addRepository(self, name, url = None, mirrorlist = None, proxy = None,
262 proxy_username = None, proxy_password = None,
263 inc = None, exc = None, ssl_verify=True, nocache=False,
264 cost = None, priority=None):
265 # TODO: Handle priority attribute for repos
266 def _varSubstitute(option):
267 # takes a variable and substitutes like yum configs do
268 option = option.replace("$basearch", rpmUtils.arch.getBaseArch())
269 option = option.replace("$arch", rpmUtils.arch.getCanonArch())
270 return option
271
272 repo = MyYumRepository(name)
273
274 # Set proxy
275 repo.proxy = proxy
276 repo.proxy_username = proxy_username
277 repo.proxy_password = proxy_password
278
279 if url:
280 repo.baseurl.append(_varSubstitute(url))
281
282 # check LICENSE files
283 if not rpmmisc.checkRepositoryEULA(name, repo):
284 msger.warning('skip repo:%s for failed EULA confirmation' % name)
285 return None
286
287 if mirrorlist:
288 repo.mirrorlist = _varSubstitute(mirrorlist)
289
290 conf = yum.config.RepoConf()
291 for k, v in conf.iteritems():
292 if v or not hasattr(repo, k):
293 repo.setAttribute(k, v)
294
295 repo.sslverify = ssl_verify
296 repo.cache = not nocache
297
298 repo.basecachedir = self.cachedir
299 repo.base_persistdir = self.conf.persistdir
300 repo.failovermethod = "priority"
301 repo.metadata_expire = 0
302 # Enable gpg check for verifying corrupt packages
303 repo.gpgcheck = 1
304 repo.enable()
305 repo.setup(0)
306 self.repos.add(repo)
307 if cost:
308 repo.cost = cost
309
310 msger.verbose('repo: %s was added' % name)
311 return repo
312
313 def installLocal(self, pkg, po=None, updateonly=False):
314 ts = rpmUtils.transaction.initReadOnlyTransaction()
315 try:
316 hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg)
317 except rpmUtils.RpmUtilsError, e:
318 raise yum.Errors.MiscError, \
319 'Could not open local rpm file: %s: %s' % (pkg, e)
320
321 self.deselectPackage(hdr['name'])
322 yum.YumBase.installLocal(self, pkg, po, updateonly)
323
324 def installHasFile(self, file):
325 provides_pkg = self.whatProvides(file, None, None)
326 dlpkgs = map(
327 lambda x: x.po,
328 filter(
329 lambda txmbr: txmbr.ts_state in ("i", "u"),
330 self.tsInfo.getMembers()))
331
332 for p in dlpkgs:
333 for q in provides_pkg:
334 if (p == q):
335 return True
336
337 return False
338
339 def runInstall(self, checksize = 0):
340 os.environ["HOME"] = "/"
341 os.environ["LD_PRELOAD"] = ""
342 try:
343 (res, resmsg) = self.buildTransaction()
344 except yum.Errors.RepoError, e:
345 raise CreatorError("Unable to download from repo : %s" %(e,))
346
347 if res != 2:
348 raise CreatorError("Failed to build transaction : %s" \
349 % str.join("\n", resmsg))
350
351 dlpkgs = map(
352 lambda x: x.po,
353 filter(
354 lambda txmbr: txmbr.ts_state in ("i", "u"),
355 self.tsInfo.getMembers()))
356
357 # record all pkg and the content
358 for pkg in dlpkgs:
359 pkg_long_name = misc.RPM_FMT % {
360 'name': pkg.name,
361 'arch': pkg.arch,
362 'version': pkg.version,
363 'release': pkg.release
364 }
365 self.__pkgs_content[pkg_long_name] = pkg.files
366 license = pkg.license
367 if license in self.__pkgs_license.keys():
368 self.__pkgs_license[license].append(pkg_long_name)
369 else:
370 self.__pkgs_license[license] = [pkg_long_name]
371
372 total_count = len(dlpkgs)
373 cached_count = 0
374 download_total_size = sum(map(lambda x: int(x.packagesize), dlpkgs))
375
376 msger.info("\nChecking packages cached ...")
377 for po in dlpkgs:
378 local = po.localPkg()
379 repo = filter(lambda r: r.id == po.repoid, self.repos.listEnabled())[0]
380 if not repo.cache and os.path.exists(local):
381 os.unlink(local)
382 if not os.path.exists(local):
383 continue
384 if not self.verifyPkg(local, po, False):
385 msger.warning("Package %s is damaged: %s" \
386 % (os.path.basename(local), local))
387 else:
388 download_total_size -= int(po.packagesize)
389 cached_count +=1
390
391 cache_avail_size = misc.get_filesystem_avail(self.cachedir)
392 if cache_avail_size < download_total_size:
393 raise CreatorError("No enough space used for downloading.")
394
395 # record the total size of installed pkgs
396 pkgs_total_size = 0L
397 for x in dlpkgs:
398 if hasattr(x, 'installedsize'):
399 pkgs_total_size += int(x.installedsize)
400 else:
401 pkgs_total_size += int(x.size)
402
403 # check needed size before actually download and install
404 if checksize and pkgs_total_size > checksize:
405 raise CreatorError("No enough space used for installing, "
406 "please resize partition size in ks file")
407
408 msger.info("Packages: %d Total, %d Cached, %d Missed" \
409 % (total_count, cached_count, total_count - cached_count))
410
411 try:
412 repos = self.repos.listEnabled()
413 for repo in repos:
414 repo.setCallback(TextProgress(total_count - cached_count))
415
416 self.downloadPkgs(dlpkgs)
417 # FIXME: sigcheck?
418
419 self.initActionTs()
420 self.populateTs(keepold=0)
421
422 deps = self.ts.check()
423 if len(deps) != 0:
424 # This isn't fatal, Ubuntu has this issue but it is ok.
425 msger.debug(deps)
426 msger.warning("Dependency check failed!")
427
428 rc = self.ts.order()
429 if rc != 0:
430 raise CreatorError("ordering packages for installation failed")
431
432 # FIXME: callback should be refactored a little in yum
433 cb = rpmmisc.RPMInstallCallback(self.ts)
434 cb.tsInfo = self.tsInfo
435 cb.filelog = False
436
437 msger.warning('\nCaution, do NOT interrupt the installation, '
438 'else mic cannot finish the cleanup.')
439
440 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
441 msger.enable_logstderr(installlogfile)
442 self.runTransaction(cb)
443 self._cleanupRpmdbLocks(self.conf.installroot)
444
445 except rpmUtils.RpmUtilsError, e:
446 raise CreatorError("mic does NOT support delta rpm: %s" % e)
447 except yum.Errors.RepoError, e:
448 raise CreatorError("Unable to download from repo : %s" % e)
449 except yum.Errors.YumBaseError, e:
450 raise CreatorError("Unable to install: %s" % e)
451 finally:
452 msger.disable_logstderr()
453
454 def getVcsInfo(self):
455 return self.__pkgs_vcsinfo
456
457 def getAllContent(self):
458 return self.__pkgs_content
459
460 def getPkgsLicense(self):
461 return self.__pkgs_license
462
463 def getFilelist(self, pkgname):
464 if not pkgname:
465 return None
466
467 pkg = filter(lambda txmbr: txmbr.po.name == pkgname, self.tsInfo.getMembers())
468 if not pkg:
469 return None
470 return pkg[0].po.filelist
471
472 def package_url(self, pkgname):
473 pkgs = self.pkgSack.searchNevra(name=pkgname)
474 if pkgs:
475 proxy = None
476 proxies = None
477 url = pkgs[0].remote_url
478 repoid = pkgs[0].repoid
479 repos = filter(lambda r: r.id == repoid, self.repos.listEnabled())
480
481 if repos:
482 proxy = repos[0].proxy
483 if not proxy:
484 proxy = get_proxy_for(url)
485 if proxy:
486 proxies = {str(url.split(':')[0]): str(proxy)}
487
488 return (url, proxies)
489
490 return (None, None)
diff --git a/scripts/lib/mic/plugins/backend/zypppkgmgr.py b/scripts/lib/mic/plugins/backend/zypppkgmgr.py
deleted file mode 100755
index c760859832..0000000000
--- a/scripts/lib/mic/plugins/backend/zypppkgmgr.py
+++ /dev/null
@@ -1,973 +0,0 @@
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
18import os
19import shutil
20import urlparse
21import rpm
22
23import zypp
24if 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 download.tizen.org/tools")
29
30from mic import msger
31from mic.kickstart import ksparser
32from mic.utils import misc, rpmmisc, runner, fs_related
33from mic.utils.grabber import myurlgrab, TextProgress
34from mic.utils.proxy import get_proxy_for
35from mic.utils.errors import CreatorError, RepoError, RpmError
36from mic.imager.baseimager import BaseImageCreator
37
38class RepositoryStub:
39 def __init__(self):
40 self.name = None
41 self.baseurl = []
42 self.mirrorlist = None
43 self.proxy = None
44 self.proxy_username = None
45 self.proxy_password = None
46 self.nocache = False
47
48 self.enabled = True
49 self.autorefresh = True
50 self.keeppackages = True
51 self.priority = None
52
53from mic.pluginbase import BackendPlugin
54class Zypp(BackendPlugin):
55 name = 'zypp'
56
57 def __init__(self, target_arch, instroot, cachedir):
58 self.cachedir = cachedir
59 self.instroot = instroot
60 self.target_arch = target_arch
61
62 self.__pkgs_license = {}
63 self.__pkgs_content = {}
64 self.__pkgs_vcsinfo = {}
65 self.repos = []
66 self.to_deselect = []
67 self.localpkgs = {}
68 self.repo_manager = None
69 self.repo_manager_options = None
70 self.Z = None
71 self.ts = None
72 self.ts_pre = None
73 self.incpkgs = {}
74 self.excpkgs = {}
75 self.pre_pkgs = []
76 self.probFilterFlags = [ rpm.RPMPROB_FILTER_OLDPACKAGE,
77 rpm.RPMPROB_FILTER_REPLACEPKG ]
78
79 self.has_prov_query = True
80 self.install_debuginfo = False
81
82 def doFileLogSetup(self, uid, logfile):
83 # don't do the file log for the livecd as it can lead to open fds
84 # being left and an inability to clean up after ourself
85 pass
86
87 def closeRpmDB(self):
88 pass
89
90 def close(self):
91 if self.ts:
92 self.ts.closeDB()
93 self.ts = None
94
95 if self.ts_pre:
96 self.ts_pre.closeDB()
97 self.ts = None
98
99 self.closeRpmDB()
100
101 if not os.path.exists("/etc/fedora-release") and \
102 not os.path.exists("/etc/meego-release"):
103 for i in range(3, os.sysconf("SC_OPEN_MAX")):
104 try:
105 os.close(i)
106 except:
107 pass
108
109 def __del__(self):
110 self.close()
111
112 def _cleanupRpmdbLocks(self, installroot):
113 # cleans up temporary files left by bdb so that differing
114 # versions of rpm don't cause problems
115 import glob
116 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
117 os.unlink(f)
118
119 def _cleanupZyppJunk(self, installroot):
120 try:
121 shutil.rmtree(os.path.join(installroot, '.zypp'))
122 except:
123 pass
124
125 def setup(self):
126 self._cleanupRpmdbLocks(self.instroot)
127
128 def whatObsolete(self, pkg):
129 query = zypp.PoolQuery()
130 query.addKind(zypp.ResKind.package)
131 query.addAttribute(zypp.SolvAttr.obsoletes, pkg)
132 query.setMatchExact()
133 for pi in query.queryResults(self.Z.pool()):
134 return pi
135 return None
136
137 def _zyppQueryPackage(self, pkg):
138 query = zypp.PoolQuery()
139 query.addKind(zypp.ResKind.package)
140 query.addAttribute(zypp.SolvAttr.name,pkg)
141 query.setMatchExact()
142 for pi in query.queryResults(self.Z.pool()):
143 return pi
144 return None
145
146 def _splitPkgString(self, pkg):
147 sp = pkg.rsplit(".",1)
148 name = sp[0]
149 arch = None
150 if len(sp) == 2:
151 arch = sp[1]
152 sysarch = zypp.Arch(self.target_arch)
153 if not zypp.Arch(arch).compatible_with (sysarch):
154 arch = None
155 name = ".".join(sp)
156 return name, arch
157
158 def selectPackage(self, pkg):
159 """Select a given package or package pattern, can be specified
160 with name.arch or name* or *name
161 """
162
163 if not self.Z:
164 self.__initialize_zypp()
165
166 def markPoolItem(obs, pi):
167 if obs == None:
168 pi.status().setToBeInstalled (zypp.ResStatus.USER)
169 else:
170 obs.status().setToBeInstalled (zypp.ResStatus.USER)
171
172 def cmpEVR(p1, p2):
173 # compare criterion: arch compatibility first, then repo
174 # priority, and version last
175 a1 = p1.arch()
176 a2 = p2.arch()
177 if str(a1) != str(a2):
178 if a1.compatible_with(a2):
179 return -1
180 else:
181 return 1
182 # Priority of a repository is an integer value between 0 (the
183 # highest priority) and 99 (the lowest priority)
184 pr1 = int(p1.repoInfo().priority())
185 pr2 = int(p2.repoInfo().priority())
186 if pr1 > pr2:
187 return -1
188 elif pr1 < pr2:
189 return 1
190
191 ed1 = p1.edition()
192 ed2 = p2.edition()
193 (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
194 (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
195 return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
196
197 found = False
198 startx = pkg.startswith("*")
199 endx = pkg.endswith("*")
200 ispattern = startx or endx
201 name, arch = self._splitPkgString(pkg)
202
203 q = zypp.PoolQuery()
204 q.addKind(zypp.ResKind.package)
205
206 if ispattern:
207 if startx and not endx:
208 pattern = '%s$' % (pkg[1:])
209 if endx and not startx:
210 pattern = '^%s' % (pkg[0:-1])
211 if endx and startx:
212 pattern = '%s' % (pkg[1:-1])
213 q.setMatchRegex()
214 q.addAttribute(zypp.SolvAttr.name,pattern)
215
216 elif arch:
217 q.setMatchExact()
218 q.addAttribute(zypp.SolvAttr.name,name)
219
220 else:
221 q.setMatchExact()
222 q.addAttribute(zypp.SolvAttr.name,pkg)
223
224 for pitem in sorted(
225 q.queryResults(self.Z.pool()),
226 cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
227 reverse=True):
228 item = zypp.asKindPackage(pitem)
229 if item.name() in self.excpkgs.keys() and \
230 self.excpkgs[item.name()] == item.repoInfo().name():
231 continue
232 if item.name() in self.incpkgs.keys() and \
233 self.incpkgs[item.name()] != item.repoInfo().name():
234 continue
235
236 found = True
237 obspkg = self.whatObsolete(item.name())
238 if arch:
239 if arch == str(item.arch()):
240 item.status().setToBeInstalled (zypp.ResStatus.USER)
241 else:
242 markPoolItem(obspkg, pitem)
243 if not ispattern:
244 break
245
246 # Can't match using package name, then search from packge
247 # provides infomation
248 if found == False and not ispattern:
249 q.addAttribute(zypp.SolvAttr.provides, pkg)
250 q.addAttribute(zypp.SolvAttr.name,'')
251
252 for pitem in sorted(
253 q.queryResults(self.Z.pool()),
254 cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
255 reverse=True):
256 item = zypp.asKindPackage(pitem)
257 if item.name() in self.excpkgs.keys() and \
258 self.excpkgs[item.name()] == item.repoInfo().name():
259 continue
260 if item.name() in self.incpkgs.keys() and \
261 self.incpkgs[item.name()] != item.repoInfo().name():
262 continue
263
264 found = True
265 obspkg = self.whatObsolete(item.name())
266 markPoolItem(obspkg, pitem)
267 break
268
269 if found:
270 return None
271 else:
272 raise CreatorError("Unable to find package: %s" % (pkg,))
273
274 def inDeselectPackages(self, pitem):
275 """check if specified pacakges are in the list of inDeselectPackages
276 """
277 item = zypp.asKindPackage(pitem)
278 name = item.name()
279 for pkg in self.to_deselect:
280 startx = pkg.startswith("*")
281 endx = pkg.endswith("*")
282 ispattern = startx or endx
283 pkgname, pkgarch = self._splitPkgString(pkg)
284 if not ispattern:
285 if pkgarch:
286 if name == pkgname and str(item.arch()) == pkgarch:
287 return True;
288 else:
289 if name == pkgname:
290 return True;
291 else:
292 if startx and name.endswith(pkg[1:]):
293 return True;
294 if endx and name.startswith(pkg[:-1]):
295 return True;
296
297 return False;
298
299 def deselectPackage(self, pkg):
300 """collect packages should not be installed"""
301 self.to_deselect.append(pkg)
302
303 def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
304 if not self.Z:
305 self.__initialize_zypp()
306 found = False
307 q=zypp.PoolQuery()
308 q.addKind(zypp.ResKind.pattern)
309 for pitem in q.queryResults(self.Z.pool()):
310 item = zypp.asKindPattern(pitem)
311 summary = "%s" % item.summary()
312 name = "%s" % item.name()
313 if name == grp or summary == grp:
314 found = True
315 pitem.status().setToBeInstalled (zypp.ResStatus.USER)
316 break
317
318 if found:
319 if include == ksparser.GROUP_REQUIRED:
320 map(
321 lambda p: self.deselectPackage(p),
322 grp.default_packages.keys())
323
324 return None
325 else:
326 raise CreatorError("Unable to find pattern: %s" % (grp,))
327
328 def addRepository(self, name,
329 url = None,
330 mirrorlist = None,
331 proxy = None,
332 proxy_username = None,
333 proxy_password = None,
334 inc = None,
335 exc = None,
336 ssl_verify = True,
337 nocache = False,
338 cost=None,
339 priority=None):
340 # TODO: Handle cost attribute for repos
341
342 if not self.repo_manager:
343 self.__initialize_repo_manager()
344
345 if not proxy and url:
346 proxy = get_proxy_for(url)
347
348 repo = RepositoryStub()
349 repo.name = name
350 repo.id = name
351 repo.proxy = proxy
352 repo.proxy_username = proxy_username
353 repo.proxy_password = proxy_password
354 repo.ssl_verify = ssl_verify
355 repo.nocache = nocache
356 repo.baseurl.append(url)
357 if inc:
358 for pkg in inc:
359 self.incpkgs[pkg] = name
360 if exc:
361 for pkg in exc:
362 self.excpkgs[pkg] = name
363
364 # check LICENSE files
365 if not rpmmisc.checkRepositoryEULA(name, repo):
366 msger.warning('skip repo:%s for failed EULA confirmation' % name)
367 return None
368
369 if mirrorlist:
370 repo.mirrorlist = mirrorlist
371
372 # Enable gpg check for verifying corrupt packages
373 repo.gpgcheck = 1
374 if priority is not None:
375 # priority 0 has issue in RepoInfo.setPriority
376 repo.priority = priority + 1
377
378 try:
379 repo_info = zypp.RepoInfo()
380 repo_info.setAlias(repo.name)
381 repo_info.setName(repo.name)
382 repo_info.setEnabled(repo.enabled)
383 repo_info.setAutorefresh(repo.autorefresh)
384 repo_info.setKeepPackages(repo.keeppackages)
385 baseurl = zypp.Url(repo.baseurl[0])
386 if not ssl_verify:
387 baseurl.setQueryParam("ssl_verify", "no")
388 if proxy:
389 scheme, host, path, parm, query, frag = urlparse.urlparse(proxy)
390
391 proxyinfo = host.split(":")
392 host = proxyinfo[0]
393
394 port = "80"
395 if len(proxyinfo) > 1:
396 port = proxyinfo[1]
397
398 if proxy.startswith("socks") and len(proxy.rsplit(':', 1)) == 2:
399 host = proxy.rsplit(':', 1)[0]
400 port = proxy.rsplit(':', 1)[1]
401
402 baseurl.setQueryParam ("proxy", host)
403 baseurl.setQueryParam ("proxyport", port)
404
405 repo.baseurl[0] = baseurl.asCompleteString()
406 self.repos.append(repo)
407
408 repo_info.addBaseUrl(baseurl)
409
410 if repo.priority is not None:
411 repo_info.setPriority(repo.priority)
412
413 # this hack is used to change zypp credential file location
414 # the default one is $HOME/.zypp, which cause conflicts when
415 # installing some basic packages, and the location doesn't
416 # have any interface actually, so use a tricky way anyway
417 homedir = None
418 if 'HOME' in os.environ:
419 homedir = os.environ['HOME']
420 os.environ['HOME'] = '/'
421 else:
422 os.environ['HOME'] = '/'
423
424 self.repo_manager.addRepository(repo_info)
425
426 # save back the $HOME env
427 if homedir:
428 os.environ['HOME'] = homedir
429 else:
430 del os.environ['HOME']
431
432 self.__build_repo_cache(name)
433
434 except RuntimeError, e:
435 raise CreatorError(str(e))
436
437 msger.verbose('repo: %s was added' % name)
438 return repo
439
440 def installHasFile(self, file):
441 return False
442
443 def preInstall(self, pkg):
444 self.pre_pkgs.append(pkg)
445
446 def runInstall(self, checksize = 0):
447 os.environ["HOME"] = "/"
448 os.environ["LD_PRELOAD"] = ""
449 self.buildTransaction()
450
451 todo = zypp.GetResolvablesToInsDel(self.Z.pool())
452 installed_pkgs = todo._toInstall
453 dlpkgs = []
454 for pitem in installed_pkgs:
455 if not zypp.isKindPattern(pitem) and \
456 not self.inDeselectPackages(pitem):
457 item = zypp.asKindPackage(pitem)
458 dlpkgs.append(item)
459
460 if not self.install_debuginfo or str(item.arch()) == "noarch":
461 continue
462
463 dipkg = self._zyppQueryPackage("%s-debuginfo" % item.name())
464 if dipkg:
465 ditem = zypp.asKindPackage(dipkg)
466 dlpkgs.append(ditem)
467 else:
468 msger.warning("No debuginfo rpm found for: %s" \
469 % item.name())
470
471 # record all pkg and the content
472 localpkgs = self.localpkgs.keys()
473 for pkg in dlpkgs:
474 license = ''
475 if pkg.name() in localpkgs:
476 hdr = rpmmisc.readRpmHeader(self.ts, self.localpkgs[pkg.name()])
477 pkg_long_name = misc.RPM_FMT % {
478 'name': hdr['name'],
479 'arch': hdr['arch'],
480 'version': hdr['version'],
481 'release': hdr['release']
482 }
483 license = hdr['license']
484
485 else:
486 pkg_long_name = misc.RPM_FMT % {
487 'name': pkg.name(),
488 'arch': pkg.arch(),
489 'version': pkg.edition().version(),
490 'release': pkg.edition().release()
491 }
492
493 license = pkg.license()
494
495 if license in self.__pkgs_license.keys():
496 self.__pkgs_license[license].append(pkg_long_name)
497 else:
498 self.__pkgs_license[license] = [pkg_long_name]
499
500 total_count = len(dlpkgs)
501 cached_count = 0
502 download_total_size = sum(map(lambda x: int(x.downloadSize()), dlpkgs))
503 localpkgs = self.localpkgs.keys()
504
505 msger.info("Checking packages cached ...")
506 for po in dlpkgs:
507 # Check if it is cached locally
508 if po.name() in localpkgs:
509 cached_count += 1
510 else:
511 local = self.getLocalPkgPath(po)
512 name = str(po.repoInfo().name())
513 try:
514 repo = filter(lambda r: r.name == name, self.repos)[0]
515 except IndexError:
516 repo = None
517 nocache = repo.nocache if repo else False
518
519 if os.path.exists(local):
520 if nocache or self.checkPkg(local) !=0:
521 os.unlink(local)
522 else:
523 download_total_size -= int(po.downloadSize())
524 cached_count += 1
525 cache_avail_size = misc.get_filesystem_avail(self.cachedir)
526 if cache_avail_size < download_total_size:
527 raise CreatorError("No enough space used for downloading.")
528
529 # record the total size of installed pkgs
530 install_total_size = sum(map(lambda x: int(x.installSize()), dlpkgs))
531 # check needed size before actually download and install
532
533 # FIXME: for multiple partitions for loop type, check fails
534 # skip the check temporarily
535 #if checksize and install_total_size > checksize:
536 # raise CreatorError("No enough space used for installing, "
537 # "please resize partition size in ks file")
538
539 download_count = total_count - cached_count
540 msger.info("Packages: %d Total, %d Cached, %d Missed" \
541 % (total_count, cached_count, download_count))
542
543 try:
544 if download_count > 0:
545 msger.info("Downloading packages ...")
546 self.downloadPkgs(dlpkgs, download_count)
547
548 self.installPkgs(dlpkgs)
549
550 except (RepoError, RpmError):
551 raise
552 except Exception, e:
553 raise CreatorError("Package installation failed: %s" % (e,))
554
555 def getVcsInfo(self):
556 if self.__pkgs_vcsinfo:
557 return
558
559 if not self.ts:
560 self.__initialize_transaction()
561
562 mi = self.ts.dbMatch()
563 for hdr in mi:
564 lname = misc.RPM_FMT % {
565 'name': hdr['name'],
566 'arch': hdr['arch'],
567 'version': hdr['version'],
568 'release': hdr['release']
569 }
570 self.__pkgs_vcsinfo[lname] = hdr['VCS']
571
572 return self.__pkgs_vcsinfo
573
574 def getAllContent(self):
575 if self.__pkgs_content:
576 return self.__pkgs_content
577
578 if not self.ts:
579 self.__initialize_transaction()
580
581 mi = self.ts.dbMatch()
582 for hdr in mi:
583 lname = misc.RPM_FMT % {
584 'name': hdr['name'],
585 'arch': hdr['arch'],
586 'version': hdr['version'],
587 'release': hdr['release']
588 }
589 self.__pkgs_content[lname] = hdr['FILENAMES']
590
591 return self.__pkgs_content
592
593 def getPkgsLicense(self):
594 return self.__pkgs_license
595
596 def getFilelist(self, pkgname):
597 if not pkgname:
598 return None
599
600 if not self.ts:
601 self.__initialize_transaction()
602
603 mi = self.ts.dbMatch('name', pkgname)
604 for header in mi:
605 return header['FILENAMES']
606
607 def __initialize_repo_manager(self):
608 if self.repo_manager:
609 return
610
611 # Clean up repo metadata
612 shutil.rmtree(self.cachedir + "/etc", ignore_errors = True)
613 shutil.rmtree(self.cachedir + "/solv", ignore_errors = True)
614 shutil.rmtree(self.cachedir + "/raw", ignore_errors = True)
615
616 zypp.KeyRing.setDefaultAccept( zypp.KeyRing.ACCEPT_UNSIGNED_FILE
617 | zypp.KeyRing.ACCEPT_VERIFICATION_FAILED
618 | zypp.KeyRing.ACCEPT_UNKNOWNKEY
619 | zypp.KeyRing.TRUST_KEY_TEMPORARILY
620 )
621
622 self.repo_manager_options = \
623 zypp.RepoManagerOptions(zypp.Pathname(self.instroot))
624
625 self.repo_manager_options.knownReposPath = \
626 zypp.Pathname(self.cachedir + "/etc/zypp/repos.d")
627
628 self.repo_manager_options.repoCachePath = \
629 zypp.Pathname(self.cachedir)
630
631 self.repo_manager_options.repoRawCachePath = \
632 zypp.Pathname(self.cachedir + "/raw")
633
634 self.repo_manager_options.repoSolvCachePath = \
635 zypp.Pathname(self.cachedir + "/solv")
636
637 self.repo_manager_options.repoPackagesCachePath = \
638 zypp.Pathname(self.cachedir + "/packages")
639
640 self.repo_manager = zypp.RepoManager(self.repo_manager_options)
641
642 def __build_repo_cache(self, name):
643 repo = self.repo_manager.getRepositoryInfo(name)
644 if self.repo_manager.isCached(repo) or not repo.enabled():
645 return
646
647 msger.info('Refreshing repository: %s ...' % name)
648 self.repo_manager.buildCache(repo, zypp.RepoManager.BuildIfNeeded)
649
650 def __initialize_zypp(self):
651 if self.Z:
652 return
653
654 zconfig = zypp.ZConfig_instance()
655
656 # Set system architecture
657 if self.target_arch:
658 zconfig.setSystemArchitecture(zypp.Arch(self.target_arch))
659
660 msger.info("zypp architecture is <%s>" % zconfig.systemArchitecture())
661
662 # repoPackagesCachePath is corrected by this
663 self.repo_manager = zypp.RepoManager(self.repo_manager_options)
664 repos = self.repo_manager.knownRepositories()
665 for repo in repos:
666 if not repo.enabled():
667 continue
668 self.repo_manager.loadFromCache(repo)
669
670 self.Z = zypp.ZYppFactory_instance().getZYpp()
671 self.Z.initializeTarget(zypp.Pathname(self.instroot))
672 self.Z.target().load()
673
674 def buildTransaction(self):
675 if not self.Z.resolver().resolvePool():
676 probs = self.Z.resolver().problems()
677
678 for problem in probs:
679 msger.warning("repo problem: %s, %s" \
680 % (problem.description().decode("utf-8"),
681 problem.details().decode("utf-8")))
682
683 raise RepoError("found %d resolver problem, abort!" \
684 % len(probs))
685
686 def getLocalPkgPath(self, po):
687 repoinfo = po.repoInfo()
688 cacheroot = repoinfo.packagesPath()
689 location= po.location()
690 rpmpath = str(location.filename())
691 pkgpath = "%s/%s" % (cacheroot, os.path.basename(rpmpath))
692 return pkgpath
693
694 def installLocal(self, pkg, po=None, updateonly=False):
695 if not self.ts:
696 self.__initialize_transaction()
697
698 solvfile = "%s/.solv" % (self.cachedir)
699
700 rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"),
701 pkg])
702 if rc == 0:
703 f = open(solvfile, "w+")
704 f.write(out)
705 f.close()
706
707 warnmsg = self.repo_manager.loadSolvFile(solvfile,
708 os.path.basename(pkg))
709 if warnmsg:
710 msger.warning(warnmsg)
711
712 os.unlink(solvfile)
713 else:
714 msger.warning('Can not get %s solv data.' % pkg)
715
716 hdr = rpmmisc.readRpmHeader(self.ts, pkg)
717 arch = zypp.Arch(hdr['arch'])
718 sysarch = zypp.Arch(self.target_arch)
719
720 if arch.compatible_with (sysarch):
721 pkgname = hdr['name']
722 self.localpkgs[pkgname] = pkg
723 self.selectPackage(pkgname)
724 msger.info("Marking %s to be installed" % (pkg))
725
726 else:
727 msger.warning("Cannot add package %s to transaction. "
728 "Not a compatible architecture: %s" \
729 % (pkg, hdr['arch']))
730
731 def downloadPkgs(self, package_objects, count):
732 localpkgs = self.localpkgs.keys()
733 progress_obj = TextProgress(count)
734
735 for po in package_objects:
736 if po.name() in localpkgs:
737 continue
738
739 filename = self.getLocalPkgPath(po)
740 if os.path.exists(filename):
741 if self.checkPkg(filename) == 0:
742 continue
743
744 dirn = os.path.dirname(filename)
745 if not os.path.exists(dirn):
746 os.makedirs(dirn)
747
748 url = self.get_url(po)
749 proxies = self.get_proxies(po)
750
751 try:
752 filename = myurlgrab(url, filename, proxies, progress_obj)
753 except CreatorError:
754 self.close()
755 raise
756
757 def preinstallPkgs(self):
758 if not self.ts_pre:
759 self.__initialize_transaction()
760
761 self.ts_pre.order()
762 cb = rpmmisc.RPMInstallCallback(self.ts_pre)
763 cb.headmsg = "Preinstall"
764 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
765
766 # start to catch stderr output from librpm
767 msger.enable_logstderr(installlogfile)
768
769 errors = self.ts_pre.run(cb.callback, '')
770 # stop catch
771 msger.disable_logstderr()
772 self.ts_pre.closeDB()
773 self.ts_pre = None
774
775 if errors is not None:
776 if len(errors) == 0:
777 msger.warning('scriptlet or other non-fatal errors occurred '
778 'during transaction.')
779
780 else:
781 for e in errors:
782 msger.warning(e[0])
783 raise RepoError('Could not run transaction.')
784
785 def installPkgs(self, package_objects):
786 if not self.ts:
787 self.__initialize_transaction()
788
789 # clean rpm lock
790 self._cleanupRpmdbLocks(self.instroot)
791 self._cleanupZyppJunk(self.instroot)
792 # Set filters
793 probfilter = 0
794 for flag in self.probFilterFlags:
795 probfilter |= flag
796 self.ts.setProbFilter(probfilter)
797 self.ts_pre.setProbFilter(probfilter)
798
799 localpkgs = self.localpkgs.keys()
800
801 for po in package_objects:
802 pkgname = po.name()
803 if pkgname in localpkgs:
804 rpmpath = self.localpkgs[pkgname]
805 else:
806 rpmpath = self.getLocalPkgPath(po)
807
808 if not os.path.exists(rpmpath):
809 # Maybe it is a local repo
810 rpmuri = self.get_url(po)
811 if rpmuri.startswith("file:/"):
812 rpmpath = rpmuri[5:]
813
814 if not os.path.exists(rpmpath):
815 raise RpmError("Error: %s doesn't exist" % rpmpath)
816
817 h = rpmmisc.readRpmHeader(self.ts, rpmpath)
818
819 if pkgname in self.pre_pkgs:
820 msger.verbose("pre-install package added: %s" % pkgname)
821 self.ts_pre.addInstall(h, rpmpath, 'u')
822
823 self.ts.addInstall(h, rpmpath, 'u')
824
825 unresolved_dependencies = self.ts.check()
826 if not unresolved_dependencies:
827 if self.pre_pkgs:
828 self.preinstallPkgs()
829
830 self.ts.order()
831 cb = rpmmisc.RPMInstallCallback(self.ts)
832 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
833
834 # start to catch stderr output from librpm
835 msger.enable_logstderr(installlogfile)
836
837 errors = self.ts.run(cb.callback, '')
838 # stop catch
839 msger.disable_logstderr()
840 self.ts.closeDB()
841 self.ts = None
842
843 if errors is not None:
844 if len(errors) == 0:
845 msger.warning('scriptlet or other non-fatal errors occurred '
846 'during transaction.')
847
848 else:
849 for e in errors:
850 msger.warning(e[0])
851 raise RepoError('Could not run transaction.')
852
853 else:
854 for pkg, need, needflags, sense, key in unresolved_dependencies:
855 package = '-'.join(pkg)
856
857 if needflags == rpm.RPMSENSE_LESS:
858 deppkg = ' < '.join(need)
859 elif needflags == rpm.RPMSENSE_EQUAL:
860 deppkg = ' = '.join(need)
861 elif needflags == rpm.RPMSENSE_GREATER:
862 deppkg = ' > '.join(need)
863 else:
864 deppkg = '-'.join(need)
865
866 if sense == rpm.RPMDEP_SENSE_REQUIRES:
867 msger.warning("[%s] Requires [%s], which is not provided" \
868 % (package, deppkg))
869
870 elif sense == rpm.RPMDEP_SENSE_CONFLICTS:
871 msger.warning("[%s] Conflicts with [%s]" %(package,deppkg))
872
873 raise RepoError("Unresolved dependencies, transaction failed.")
874
875 def __initialize_transaction(self):
876 if not self.ts:
877 self.ts = rpm.TransactionSet(self.instroot)
878 # Set to not verify DSA signatures.
879 self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
880
881 if not self.ts_pre:
882 self.ts_pre = rpm.TransactionSet(self.instroot)
883 # Just unpack the files, don't run scripts
884 self.ts_pre.setFlags(rpm.RPMTRANS_FLAG_ALLFILES | rpm.RPMTRANS_FLAG_NOSCRIPTS)
885 # Set to not verify DSA signatures.
886 self.ts_pre.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
887
888 def checkPkg(self, pkg):
889 ret = 1
890 if not os.path.exists(pkg):
891 return ret
892 ret = rpmmisc.checkRpmIntegrity('rpm', pkg)
893 if ret != 0:
894 msger.warning("package %s is damaged: %s" \
895 % (os.path.basename(pkg), pkg))
896
897 return ret
898
899 def _add_prob_flags(self, *flags):
900 for flag in flags:
901 if flag not in self.probFilterFlags:
902 self.probFilterFlags.append(flag)
903
904 def get_proxies(self, pobj):
905 if not pobj:
906 return None
907
908 proxy = None
909 proxies = None
910 repoinfo = pobj.repoInfo()
911 reponame = "%s" % repoinfo.name()
912 repos = filter(lambda r: r.name == reponame, self.repos)
913 repourl = str(repoinfo.baseUrls()[0])
914
915 if repos:
916 proxy = repos[0].proxy
917 if not proxy:
918 proxy = get_proxy_for(repourl)
919 if proxy:
920 proxies = {str(repourl.split(':')[0]): str(proxy)}
921
922 return proxies
923
924 def get_url(self, pobj):
925 if not pobj:
926 return None
927
928 name = str(pobj.repoInfo().name())
929 try:
930 repo = filter(lambda r: r.name == name, self.repos)[0]
931 except IndexError:
932 return None
933
934 baseurl = repo.baseurl[0]
935
936 index = baseurl.find("?")
937 if index > -1:
938 baseurl = baseurl[:index]
939
940 location = pobj.location()
941 location = str(location.filename())
942 if location.startswith("./"):
943 location = location[2:]
944
945 return os.path.join(baseurl, location)
946
947 def package_url(self, pkgname):
948
949 def cmpEVR(p1, p2):
950 ed1 = p1.edition()
951 ed2 = p2.edition()
952 (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
953 (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
954 return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
955
956 if not self.Z:
957 self.__initialize_zypp()
958
959 q = zypp.PoolQuery()
960 q.addKind(zypp.ResKind.package)
961 q.setMatchExact()
962 q.addAttribute(zypp.SolvAttr.name, pkgname)
963 items = sorted(q.queryResults(self.Z.pool()),
964 cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
965 reverse=True)
966
967 if items:
968 item = zypp.asKindPackage(items[0])
969 url = self.get_url(item)
970 proxies = self.get_proxies(item)
971 return (url, proxies)
972
973 return (None, None)