summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic/plugins/backend/yumpkgmgr.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/mic/plugins/backend/yumpkgmgr.py')
-rw-r--r--scripts/lib/mic/plugins/backend/yumpkgmgr.py490
1 files changed, 490 insertions, 0 deletions
diff --git a/scripts/lib/mic/plugins/backend/yumpkgmgr.py b/scripts/lib/mic/plugins/backend/yumpkgmgr.py
new file mode 100644
index 0000000000..955f813109
--- /dev/null
+++ b/scripts/lib/mic/plugins/backend/yumpkgmgr.py
@@ -0,0 +1,490 @@
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)