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.
23 from string import Template
29 from mic.kickstart import ksparser
30 from mic.utils import misc, rpmmisc
31 from mic.utils.errors import CreatorError
32 from mic.imager.baseimager import BaseImageCreator
34 YUMCONF_TEMP = """[main]
35 installroot=$installroot
36 cachedir=/var/cache/yum
37 persistdir=/var/lib/yum
40 failovermethod=priority
45 class MyYumRepository(yum.yumRepo.YumRepository):
50 super(MyYumRepository, self).dirSetup()
51 # relocate package dir
52 pkgdir = os.path.join(self.basecachedir, 'packages', self.id)
53 self.setAttribute('_dir_setup_pkgdir', pkgdir)
54 self._dirSetupMkdir_p(self.pkgdir)
56 def _getFile(self, url=None,
69 if not self.sslverify:
71 m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck
72 M2Crypto.SSL.Connection.clientPostConnectionCheck = None
74 size = int(size) if size else None
75 rvalue = super(MyYumRepository, self)._getFile(url,
87 if m2c_connection and \
88 not M2Crypto.SSL.Connection.clientPostConnectionCheck:
89 M2Crypto.SSL.Connection.clientPostConnectionCheck = m2c_connection
93 from mic.pluginbase import BackendPlugin
94 class Yum(BackendPlugin, yum.YumBase):
97 def __init__(self, target_arch, instroot, cachedir):
98 yum.YumBase.__init__(self)
100 self.cachedir = cachedir
101 self.instroot = instroot
102 self.target_arch = target_arch
105 if not rpmUtils.arch.arches.has_key(self.target_arch):
106 rpmUtils.arch.arches["armv7hl"] = "noarch"
107 rpmUtils.arch.arches["armv7tnhl"] = "armv7nhl"
108 rpmUtils.arch.arches["armv7tnhl"] = "armv7thl"
109 rpmUtils.arch.arches["armv7thl"] = "armv7hl"
110 rpmUtils.arch.arches["armv7nhl"] = "armv7hl"
111 self.arch.setup_arch(self.target_arch)
113 self.__pkgs_license = {}
114 self.__pkgs_content = {}
116 def doFileLogSetup(self, uid, logfile):
117 # don't do the file log for the livecd as it can lead to open fds
118 # being left and an inability to clean up after ourself
123 os.unlink(self.confpath)
124 os.unlink(self.conf.installroot + "/yum.conf")
132 yum.YumBase.close(self)
135 if not os.path.exists("/etc/fedora-release") and \
136 not os.path.exists("/etc/meego-release"):
137 for i in range(3, os.sysconf("SC_OPEN_MAX")):
146 def _writeConf(self, confpath, installroot):
147 conf = Template(YUMCONF_TEMP).safe_substitute(installroot=installroot)
149 f = file(confpath, "w+")
153 os.chmod(confpath, 0644)
155 def _cleanupRpmdbLocks(self, installroot):
156 # cleans up temporary files left by bdb so that differing
157 # versions of rpm don't cause problems
158 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
163 (fn, self.confpath) = tempfile.mkstemp(dir=self.cachedir,
166 self._writeConf(self.confpath, self.instroot)
167 self._cleanupRpmdbLocks(self.instroot)
169 self.doConfigSetup(fn = self.confpath, root = self.instroot)
176 def preInstall(self, pkg):
177 # FIXME: handle pre-install package
180 def selectPackage(self, pkg):
181 """Select a given package.
182 Can be specified with name.arch or name*
186 self.install(pattern = pkg)
188 except yum.Errors.InstallError:
189 return "No package(s) available to install"
190 except yum.Errors.RepoError, e:
191 raise CreatorError("Unable to download from repo : %s" % (e,))
192 except yum.Errors.YumBaseError, e:
193 raise CreatorError("Unable to install: %s" % (e,))
195 def deselectPackage(self, pkg):
196 """Deselect package. Can be specified as name.arch or name*
199 sp = pkg.rsplit(".", 2)
202 txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1])
205 exact, match, unmatch = yum.packages.parsePackages(
206 self.pkgSack.returnPackages(),
209 for p in exact + match:
214 self.tsInfo.remove(x.pkgtup)
215 # we also need to remove from the conditionals
216 # dict so that things don't get pulled back in as a result
217 # of them. yes, this is ugly. conditionals should die.
218 for req, pkgs in self.tsInfo.conditionals.iteritems():
221 self.tsInfo.conditionals[req] = pkgs
223 msger.warning("No such package %s to remove" %(pkg,))
225 def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
227 yum.YumBase.selectGroup(self, grp)
228 if include == ksparser.GROUP_REQUIRED:
229 for p in grp.default_packages.keys():
230 self.deselectPackage(p)
232 elif include == ksparser.GROUP_ALL:
233 for p in grp.optional_packages.keys():
234 self.selectPackage(p)
237 except (yum.Errors.InstallError, yum.Errors.GroupsError), e:
239 except yum.Errors.RepoError, e:
240 raise CreatorError("Unable to download from repo : %s" % (e,))
241 except yum.Errors.YumBaseError, e:
242 raise CreatorError("Unable to install: %s" % (e,))
244 def addRepository(self, name, url = None, mirrorlist = None, proxy = None,
245 proxy_username = None, proxy_password = None,
246 inc = None, exc = None, ssl_verify=True, cost = None,
248 # TODO: Handle priority attribute for repos
249 def _varSubstitute(option):
250 # takes a variable and substitutes like yum configs do
251 option = option.replace("$basearch", rpmUtils.arch.getBaseArch())
252 option = option.replace("$arch", rpmUtils.arch.getCanonArch())
255 repo = MyYumRepository(name)
259 repo.proxy_username = proxy_username
260 repo.proxy_password = proxy_password
263 repo.baseurl.append(_varSubstitute(url))
265 # check LICENSE files
266 if not rpmmisc.checkRepositoryEULA(name, repo):
267 msger.warning('skip repo:%s for failed EULA confirmation' % name)
271 repo.mirrorlist = _varSubstitute(mirrorlist)
273 conf = yum.config.RepoConf()
274 for k, v in conf.iteritems():
275 if v or not hasattr(repo, k):
276 repo.setAttribute(k, v)
278 repo.sslverify = ssl_verify
280 repo.basecachedir = self.cachedir
281 repo.base_persistdir = self.conf.persistdir
282 repo.failovermethod = "priority"
283 repo.metadata_expire = 0
284 # Enable gpg check for verifying corrupt packages
292 msger.verbose('repo: %s was added' % name)
295 def installLocal(self, pkg, po=None, updateonly=False):
296 ts = rpmUtils.transaction.initReadOnlyTransaction()
298 hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg)
299 except rpmUtils.RpmUtilsError, e:
300 raise yum.Errors.MiscError, \
301 'Could not open local rpm file: %s: %s' % (pkg, e)
303 self.deselectPackage(hdr['name'])
304 yum.YumBase.installLocal(self, pkg, po, updateonly)
306 def installHasFile(self, file):
307 provides_pkg = self.whatProvides(file, None, None)
311 lambda txmbr: txmbr.ts_state in ("i", "u"),
312 self.tsInfo.getMembers()))
315 for q in provides_pkg:
321 def runInstall(self, checksize = 0):
322 os.environ["HOME"] = "/"
324 (res, resmsg) = self.buildTransaction()
325 except yum.Errors.RepoError, e:
326 raise CreatorError("Unable to download from repo : %s" %(e,))
329 raise CreatorError("Failed to build transaction : %s" \
330 % str.join("\n", resmsg))
335 lambda txmbr: txmbr.ts_state in ("i", "u"),
336 self.tsInfo.getMembers()))
338 # record all pkg and the content
340 pkg_long_name = misc.RPM_FMT % {
343 'ver_rel': pkg.printVer(),
345 self.__pkgs_content[pkg_long_name] = pkg.files
346 license = pkg.license
347 if license in self.__pkgs_license.keys():
348 self.__pkgs_license[license].append(pkg_long_name)
350 self.__pkgs_license[license] = [pkg_long_name]
352 total_count = len(dlpkgs)
354 download_total_size = sum(map(lambda x: int(x.packagesize), dlpkgs))
356 msger.info("\nChecking packages cache and packages integrity ...")
358 local = po.localPkg()
359 if not os.path.exists(local):
361 if not self.verifyPkg(local, po, False):
362 msger.warning("Package %s is damaged: %s" \
363 % (os.path.basename(local), local))
365 download_total_size -= int(po.packagesize)
368 cache_avail_size = misc.get_filesystem_avail(self.cachedir)
369 if cache_avail_size < download_total_size:
370 raise CreatorError("No enough space used for downloading.")
372 # record the total size of installed pkgs
375 if hasattr(x, 'installedsize'):
376 pkgs_total_size += int(x.installedsize)
378 pkgs_total_size += int(x.size)
380 # check needed size before actually download and install
381 if checksize and pkgs_total_size > checksize:
382 raise CreatorError("No enough space used for installing, "
383 "please resize partition size in ks file")
385 msger.info("%d packages to be installed, "
386 "%d packages gotten from cache, "
387 "%d packages to be downloaded" \
388 % (total_count, cached_count, total_count - cached_count))
391 repos = self.repos.listEnabled()
394 rpmmisc.TextProgress(total_count - cached_count))
396 self.downloadPkgs(dlpkgs)
400 self.populateTs(keepold=0)
402 deps = self.ts.check()
404 # This isn't fatal, Ubuntu has this issue but it is ok.
406 msger.warning("Dependency check failed!")
410 raise CreatorError("ordering packages for installation failed")
412 # FIXME: callback should be refactored a little in yum
413 cb = rpmmisc.RPMInstallCallback(self.ts)
414 cb.tsInfo = self.tsInfo
417 msger.warning('\nCaution, do NOT interrupt the installation, '
418 'else mic cannot finish the cleanup.')
420 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
421 msger.enable_logstderr(installlogfile)
422 self.runTransaction(cb)
423 self._cleanupRpmdbLocks(self.conf.installroot)
425 except rpmUtils.RpmUtilsError, e:
426 raise CreatorError("mic does NOT support delta rpm: %s" % e)
427 except yum.Errors.RepoError, e:
428 raise CreatorError("Unable to download from repo : %s" % e)
429 except yum.Errors.YumBaseError, e:
430 raise CreatorError("Unable to install: %s" % e)
432 msger.disable_logstderr()
434 def getAllContent(self):
435 return self.__pkgs_content
437 def getPkgsLicense(self):
438 return self.__pkgs_license
440 def getFilelist(self, pkgname):
444 pkg = filter(lambda txmbr: txmbr.po.name == pkgname, self.tsInfo.getMembers())
447 return pkg[0].po.filelist