3 # Copyright (c) 2007 Red Hat Inc.
4 # Copyright (c) 2010, 2011 Intel, Inc.
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
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
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.
22 from string import Template
28 from mic.kickstart import ksparser
29 from mic.utils import misc, rpmmisc
30 from mic.utils.grabber import TextProgress
31 from mic.utils.proxy import get_proxy_for
32 from mic.utils.errors import CreatorError
33 from mic.utils.safeurl import SafeURL
36 YUMCONF_TEMP = """[main]
37 installroot=$installroot
38 cachedir=/var/cache/yum
39 persistdir=/var/lib/yum
42 failovermethod=priority
47 class MyYumRepository(yum.yumRepo.YumRepository):
48 def __init__(self, repoid, nocache):
49 super(MyYumRepository, self).__init__(repoid)
50 self.nocache = nocache
56 super(MyYumRepository, self).dirSetup()
57 # relocate package dir
58 pkgdir = os.path.join(self.basecachedir, 'packages', self.id)
59 self.setAttribute('_dir_setup_pkgdir', pkgdir)
60 self._dirSetupMkdir_p(self.pkgdir)
62 def _getFile(self, url=None,
75 if not self.sslverify:
78 m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck
79 M2Crypto.SSL.Connection.clientPostConnectionCheck = None
80 except ImportError, err:
81 raise CreatorError("%s, please try to install python-m2crypto" % str(err))
85 proxy = get_proxy_for(url)
87 proxy = get_proxy_for(self.urls[0])
90 self.proxy = str(proxy)
92 size = int(size) if size else None
93 rvalue = super(MyYumRepository, self)._getFile(url,
105 if m2c_connection and \
106 not M2Crypto.SSL.Connection.clientPostConnectionCheck:
107 M2Crypto.SSL.Connection.clientPostConnectionCheck = m2c_connection
111 from mic.pluginbase import BackendPlugin
112 class Yum(BackendPlugin, yum.YumBase):
115 def __init__(self, target_arch, instroot, cachedir):
116 yum.YumBase.__init__(self)
118 self.cachedir = cachedir
119 self.instroot = instroot
120 self.target_arch = target_arch
123 if not rpmUtils.arch.arches.has_key(self.target_arch):
124 rpmUtils.arch.arches["armv7hl"] = "noarch"
125 rpmUtils.arch.arches["armv7tnhl"] = "armv7nhl"
126 rpmUtils.arch.arches["armv7tnhl"] = "armv7thl"
127 rpmUtils.arch.arches["armv7thl"] = "armv7hl"
128 rpmUtils.arch.arches["armv7nhl"] = "armv7hl"
129 self.arch.setup_arch(self.target_arch)
131 self.__pkgs_license = {}
132 self.__pkgs_content = {}
133 self.__pkgs_vcsinfo = {}
136 self.install_debuginfo = False
138 def doFileLogSetup(self, uid, logfile):
139 # don't do the file log for the livecd as it can lead to open fds
140 # being left and an inability to clean up after ourself
145 os.unlink(self.confpath)
146 os.unlink(self.conf.installroot + "/yum.conf")
154 yum.YumBase.close(self)
160 def _writeConf(self, confpath, installroot):
161 conf = Template(YUMCONF_TEMP).safe_substitute(installroot=installroot)
163 f = file(confpath, "w+")
167 os.chmod(confpath, 0644)
169 def _cleanupRpmdbLocks(self, installroot):
170 # cleans up temporary files left by bdb so that differing
171 # versions of rpm don't cause problems
172 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
177 (fn, self.confpath) = tempfile.mkstemp(dir=self.cachedir,
180 self._writeConf(self.confpath, self.instroot)
181 self._cleanupRpmdbLocks(self.instroot)
183 self.doConfigSetup(fn = self.confpath, root = self.instroot)
190 def preInstall(self, pkg):
191 # FIXME: handle pre-install package
194 def checkPackage(self, pkg):
195 self.check_pkgs.append(pkg)
197 def selectPackage(self, pkg):
198 """Select a given package.
199 Can be specified with name.arch or name*
203 self.install(pattern = pkg)
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,))
212 def deselectPackage(self, pkg):
213 """Deselect package. Can be specified as name.arch or name*
216 sp = pkg.rsplit(".", 2)
219 txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1])
222 exact, match, unmatch = yum.packages.parsePackages(
223 self.pkgSack.returnPackages(),
226 for p in exact + match:
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():
238 self.tsInfo.conditionals[req] = pkgs
240 msger.warning("No such package %s to remove" %(pkg,))
242 def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
244 yum.YumBase.selectGroup(self, grp)
245 if include == ksparser.GROUP_REQUIRED:
246 for p in grp.default_packages.keys():
247 self.deselectPackage(p)
249 elif include == ksparser.GROUP_ALL:
250 for p in grp.optional_packages.keys():
251 self.selectPackage(p)
254 except (yum.Errors.InstallError, yum.Errors.GroupsError), 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,))
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())
271 repo = MyYumRepository(name, nocache)
275 repo.proxy_username = proxy_username
276 repo.proxy_password = proxy_password
279 repo.baseurl.append(_varSubstitute(url.full))
282 repo.mirrorlist = _varSubstitute(mirrorlist)
284 conf = yum.config.RepoConf()
285 for k, v in conf.iteritems():
286 if v or not hasattr(repo, k):
287 repo.setAttribute(k, v)
289 repo.sslverify = ssl_verify
291 repo.basecachedir = self.cachedir
292 repo.base_persistdir = self.conf.persistdir
293 repo.failovermethod = "priority"
294 repo.metadata_expire = 0
295 # Enable gpg check for verifying corrupt packages
303 msger.verbose('repo: %s was added' % name)
306 def installLocal(self, pkg, po=None, updateonly=False):
307 ts = rpmUtils.transaction.initReadOnlyTransaction()
309 hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg)
310 except rpmUtils.RpmUtilsError, e:
311 raise yum.Errors.MiscError, \
312 'Could not open local rpm file: %s: %s' % (pkg, e)
314 self.deselectPackage(hdr['name'])
315 yum.YumBase.installLocal(self, pkg, po, updateonly)
317 def installHasFile(self, file):
318 provides_pkg = self.whatProvides(file, None, None)
322 lambda txmbr: txmbr.ts_state in ("i", "u"),
323 self.tsInfo.getMembers()))
326 for q in provides_pkg:
332 def runInstall(self, checksize = 0):
333 os.environ["HOME"] = "/"
334 os.environ["LD_PRELOAD"] = ""
336 (res, resmsg) = self.buildTransaction()
337 except yum.Errors.RepoError, e:
338 raise CreatorError("Unable to download from repo : %s" %(e,))
341 raise CreatorError("Failed to build transaction : %s" \
342 % str.join("\n", resmsg))
347 lambda txmbr: txmbr.ts_state in ("i", "u"),
348 self.tsInfo.getMembers()))
350 # record all pkg and the content
352 pkg_long_name = misc.RPM_FMT % {
355 'version': pkg.version,
356 'release': pkg.release
358 self.__pkgs_content[pkg_long_name] = pkg.files
359 license = pkg.license
360 if license in self.__pkgs_license.keys():
361 self.__pkgs_license[license].append(pkg_long_name)
363 self.__pkgs_license[license] = [pkg_long_name]
365 if pkg.name in self.check_pkgs:
366 self.check_pkgs.remove(pkg.name)
369 raise CreatorError('Packages absent in image: %s' % ','.join(self.check_pkgs))
371 total_count = len(dlpkgs)
373 download_total_size = sum(map(lambda x: int(x.packagesize), dlpkgs))
375 msger.info("\nChecking packages cached ...")
377 local = po.localPkg()
378 repo = filter(lambda r: r.id == po.repoid, self.repos.listEnabled())[0]
379 if repo.nocache and os.path.exists(local):
381 if not os.path.exists(local):
383 if not self.verifyPkg(local, po, False):
384 msger.warning("Package %s is damaged: %s" \
385 % (os.path.basename(local), local))
387 download_total_size -= int(po.packagesize)
390 cache_avail_size = misc.get_filesystem_avail(self.cachedir)
391 if cache_avail_size < download_total_size:
392 raise CreatorError("No enough space used for downloading.")
394 # record the total size of installed pkgs
397 if hasattr(x, 'installedsize'):
398 pkgs_total_size += int(x.installedsize)
400 pkgs_total_size += int(x.size)
402 # check needed size before actually download and install
403 if checksize and pkgs_total_size > checksize:
404 raise CreatorError("No enough space used for installing, "
405 "please resize partition size in ks file")
407 msger.info("Packages: %d Total, %d Cached, %d Missed" \
408 % (total_count, cached_count, total_count - cached_count))
411 repos = self.repos.listEnabled()
413 repo.setCallback(TextProgress(total_count - cached_count))
415 self.downloadPkgs(dlpkgs)
419 self.populateTs(keepold=0)
421 deps = self.ts.check()
423 # This isn't fatal, Ubuntu has this issue but it is ok.
425 msger.warning("Dependency check failed!")
429 raise CreatorError("ordering packages for installation failed")
431 # FIXME: callback should be refactored a little in yum
432 cb = rpmmisc.RPMInstallCallback(self.ts)
433 cb.tsInfo = self.tsInfo
436 msger.warning('\nCaution, do NOT interrupt the installation, '
437 'else mic cannot finish the cleanup.')
439 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
440 msger.enable_logstderr(installlogfile)
441 self.runTransaction(cb)
442 self._cleanupRpmdbLocks(self.conf.installroot)
444 except rpmUtils.RpmUtilsError, e:
445 raise CreatorError("mic does NOT support delta rpm: %s" % e)
446 except yum.Errors.RepoError, e:
447 raise CreatorError("Unable to download from repo : %s" % e)
448 except yum.Errors.YumBaseError, e:
449 raise CreatorError("Unable to install: %s" % e)
451 msger.disable_logstderr()
453 def getVcsInfo(self):
454 return self.__pkgs_vcsinfo
456 def getAllContent(self):
457 return self.__pkgs_content
459 def getPkgsLicense(self):
460 return self.__pkgs_license
462 def getFilelist(self, pkgname):
466 pkg = filter(lambda txmbr: txmbr.po.name == pkgname, self.tsInfo.getMembers())
469 return pkg[0].po.filelist
471 def package_url(self, pkgname):
472 pkgs = self.pkgSack.searchNevra(name=pkgname)
477 url = SafeURL(repo.baseurl[0]).join(pkg.remote_path)
481 proxy = get_proxy_for(url)
483 proxies = {str(url.split(':')[0]): str(proxy)}
487 return (url, proxies)