2 # yum.py : yum utilities
4 # Copyright 2007, Red Hat Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Library General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 import pykickstart.parser
34 from micng.utils.errors import *
35 from micng.utils.fs_related import *
36 from micng.imager.BaseImageCreator import ImageCreator
38 class MyYumRepository(yum.yumRepo.YumRepository):
39 def __init__(self, repoid):
40 yum.yumRepo.YumRepository.__init__(self, repoid)
41 self.sslverify = False
44 self.sslverify = False
45 yum.yumRepo.YumRepository._setupGrab(self)
50 class Yum(yum.YumBase):
51 def __init__(self, creator = None, recording_pkgs=None):
52 if not isinstance(creator, ImageCreator):
53 raise CreatorError("Invalid argument: creator")
54 yum.YumBase.__init__(self)
56 self.creator = creator
58 if self.creator.target_arch:
59 if rpmUtils.arch.arches.has_key(self.creator.target_arch):
60 self.arch.setup_arch(self.creator.target_arch)
62 raise CreatorError("Invalid target arch: %s" % self.creator.target_arch)
64 self.__recording_pkgs = recording_pkgs
65 self.__pkgs_content = {}
67 def doFileLogSetup(self, uid, logfile):
68 # don't do the file log for the livecd as it can lead to open fds
69 # being left and an inability to clean up after ourself
74 os.unlink(self.conf.installroot + "/yum.conf")
78 yum.YumBase.close(self)
82 if not os.path.exists("/etc/fedora-release") and not os.path.exists("/etc/meego-release"):
83 for i in range(3, os.sysconf("SC_OPEN_MAX")):
92 def _writeConf(self, confpath, installroot):
94 conf += "installroot=%s\n" % installroot
95 conf += "cachedir=/var/cache/yum\n"
98 conf += "failovermethod=priority\n"
99 conf += "http_caching=packages\n"
100 conf += "sslverify=0\n"
102 f = file(confpath, "w+")
106 os.chmod(confpath, 0644)
108 def _cleanupRpmdbLocks(self, installroot):
109 # cleans up temporary files left by bdb so that differing
110 # versions of rpm don't cause problems
111 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
114 def setup(self, confpath, installroot):
115 self._writeConf(confpath, installroot)
116 self._cleanupRpmdbLocks(installroot)
117 self.doConfigSetup(fn = confpath, root = installroot)
124 def selectPackage(self, pkg):
125 """Select a given package. Can be specified with name.arch or name*"""
127 self.install(pattern = pkg)
129 except yum.Errors.InstallError, e:
131 except yum.Errors.RepoError, e:
132 raise CreatorError("Unable to download from repo : %s" % (e,))
133 except yum.Errors.YumBaseError, e:
134 raise CreatorError("Unable to install: %s" % (e,))
136 def deselectPackage(self, pkg):
137 """Deselect package. Can be specified as name.arch or name*"""
138 sp = pkg.rsplit(".", 2)
141 txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1])
144 exact, match, unmatch = yum.packages.parsePackages(self.pkgSack.returnPackages(), [pkg], casematch=1)
145 for p in exact + match:
150 self.tsInfo.remove(x.pkgtup)
151 # we also need to remove from the conditionals
152 # dict so that things don't get pulled back in as a result
153 # of them. yes, this is ugly. conditionals should die.
154 for req, pkgs in self.tsInfo.conditionals.iteritems():
157 self.tsInfo.conditionals[req] = pkgs
159 logging.warn("No such package %s to remove" %(pkg,))
161 def selectGroup(self, grp, include = pykickstart.parser.GROUP_DEFAULT):
163 yum.YumBase.selectGroup(self, grp)
164 if include == pykickstart.parser.GROUP_REQUIRED:
165 map(lambda p: self.deselectPackage(p), grp.default_packages.keys())
166 elif include == pykickstart.parser.GROUP_ALL:
167 map(lambda p: self.selectPackage(p), grp.optional_packages.keys())
169 except (yum.Errors.InstallError, yum.Errors.GroupsError), e:
171 except yum.Errors.RepoError, e:
172 raise CreatorError("Unable to download from repo : %s" % (e,))
173 except yum.Errors.YumBaseError, e:
174 raise CreatorError("Unable to install: %s" % (e,))
176 def __checkAndDownloadURL(self, u2opener, url, savepath):
179 f = u2opener.open(url)
182 except u2.HTTPError, httperror:
183 if httperror.code in (404, 503):
186 raise CreatorError(httperror)
187 except OSError, oserr:
191 raise CreatorError(oserr)
192 except IOError, oserr:
193 if hasattr(oserr, "reason") and oserr.reason.errno == 2:
196 raise CreatorError(oserr)
197 except u2.URLError, err:
198 raise CreatorError(err)
201 licf = open(savepath, "w")
208 def __pagerFile(self, savepath):
209 if os.path.splitext(savepath)[1].upper() in ('.HTM', '.HTML'):
210 pagers = ('w3m', 'links', 'lynx', 'less', 'more')
212 pagers = ('less', 'more')
217 subprocess.call([pager, savepath])
227 raw_input('press <ENTER> to continue...')
229 def checkRepositoryEULA(self, name, repo):
230 """ This function is to check the LICENSE file if provided. """
232 # when proxy needed, make urllib2 follow it
234 proxy_username = repo.proxy_username
235 proxy_password = repo.proxy_password
238 auth_handler = u2.HTTPBasicAuthHandler(u2.HTTPPasswordMgrWithDefaultRealm())
242 proxy_netloc = urlparse.urlsplit(proxy).netloc
244 proxy_url = 'http://%s:%s@%s' % (proxy_username, proxy_password, proxy_netloc)
246 proxy_url = 'http://%s@%s' % (proxy_username, proxy_netloc)
250 proxy_support = u2.ProxyHandler({'http': proxy_url,
252 handlers.append(proxy_support)
254 # download all remote files to one temp dir
256 repo_lic_dir = tempfile.mkdtemp(prefix = 'repolic')
258 for url in repo.baseurl:
259 if not url.endswith('/'):
261 tmphandlers = handlers
262 (scheme, host, path, parm, query, frag) = urlparse.urlparse(url)
263 if scheme not in ("http", "https", "ftp", "ftps", "file"):
264 raise CreatorError("Error: invalid url %s" % url)
267 user_pass, host = host.split('@', 1)
269 user, password = user_pass.split(':', 1)
270 except ValueError, e:
271 raise CreatorError('Bad URL: %s' % url)
272 print "adding HTTP auth: %s, %s" %(user, password)
273 auth_handler.add_password(None, host, user, password)
274 tmphandlers.append(auth_handler)
275 url = scheme + "://" + host + path + parm + query + frag
276 if len(tmphandlers) != 0:
277 u2opener = u2.build_opener(*tmphandlers)
279 repo_eula_url = urlparse.urljoin(url, "LICENSE.txt")
280 repo_eula_path = self.__checkAndDownloadURL(
283 os.path.join(repo_lic_dir, repo.id + '_LICENSE.txt'))
292 # show the license file
293 print 'For the software packages in this yum repo:'
294 print ' %s: %s' % (name, baseurl)
295 print 'There is an "End User License Agreement" file that need to be checked.'
296 print 'Please read the terms and conditions outlined in it and answer the followed qustions.'
297 raw_input('press <ENTER> to continue...')
299 self.__pagerFile(repo_eula_path)
301 # Asking for the "Accept/Decline"
304 input_accept = raw_input('Would you agree to the terms and conditions outlined in the above End User License Agreement? (Yes/No): ')
305 if input_accept.upper() in ('YES', 'Y'):
307 elif input_accept.upper() in ('NO', 'N'):
309 print 'Will not install pkgs from this repo.'
313 shutil.rmtree(repo_lic_dir)
316 # try to find support_info.html for extra infomation
317 repo_info_url = urlparse.urljoin(baseurl, "support_info.html")
318 repo_info_path = self.__checkAndDownloadURL(
321 os.path.join(repo_lic_dir, repo.id + '_support_info.html'))
323 print 'There is one more file in the repo for additional support information, please read it'
324 raw_input('press <ENTER> to continue...')
325 self.__pagerFile(repo_info_path)
328 shutil.rmtree(repo_lic_dir)
331 def addRepository(self, name, url = None, mirrorlist = None, proxy = None, proxy_username = None, proxy_password = None, inc = None, exc = None):
332 def _varSubstitute(option):
333 # takes a variable and substitutes like yum configs do
334 option = option.replace("$basearch", rpmUtils.arch.getBaseArch())
335 option = option.replace("$arch", rpmUtils.arch.getCanonArch())
338 repo = MyYumRepository(name)
339 repo.sslverify = False
343 repo.proxy_username = proxy_username
344 repo.proxy_password = proxy_password
347 repo.baseurl.append(_varSubstitute(url))
349 # check LICENSE files
350 if not self.checkRepositoryEULA(name, repo):
354 repo.mirrorlist = _varSubstitute(mirrorlist)
355 conf = yum.config.RepoConf()
356 for k, v in conf.iteritems():
357 if v or not hasattr(repo, k):
358 repo.setAttribute(k, v)
359 repo.basecachedir = self.conf.cachedir
360 repo.failovermethod = "priority"
361 repo.metadata_expire = 0
362 # Enable gpg check for verifying corrupt packages
366 repo.setCallback(TextProgress())
370 def installHasFile(self, file):
371 provides_pkg = self.whatProvides(file, None, None)
372 dlpkgs = map(lambda x: x.po, filter(lambda txmbr: txmbr.ts_state in ("i", "u"), self.tsInfo.getMembers()))
374 for q in provides_pkg:
379 def runInstall(self, checksize = 0):
380 os.environ["HOME"] = "/"
382 (res, resmsg) = self.buildTransaction()
383 except yum.Errors.RepoError, e:
384 raise CreatorError("Unable to download from repo : %s" %(e,))
386 raise CreatorError("Failed to build transaction : %s" % str.join("\n", resmsg))
388 dlpkgs = map(lambda x: x.po, filter(lambda txmbr: txmbr.ts_state in ("i", "u"), self.tsInfo.getMembers()))
390 # record the total size of installed pkgs
391 pkgs_total_size = sum(map(lambda x: int(x.size), dlpkgs))
393 # check needed size before actually download and install
394 if checksize and pkgs_total_size > checksize:
395 raise CreatorError("Size of specified root partition in kickstart file is too small to install all selected packages.")
397 if self.__recording_pkgs:
398 # record all pkg and the content
400 pkg_long_name = "%s-%s.%s.rpm" % (pkg.name, pkg.printVer(), pkg.arch)
401 self.__pkgs_content[pkg_long_name] = pkg.files
403 total_count = len(dlpkgs)
405 print "Checking packages cache and packages integrity..."
407 local = po.localPkg()
408 if not os.path.exists(local):
410 if not self.verifyPkg(local, po, False):
411 print "Package %s is damaged: %s" % (os.path.basename(local), local)
414 print "%d packages to be installed, %d packages gotten from cache, %d packages to be downloaded" % (total_count, cached_count, total_count - cached_count)
416 self.downloadPkgs(dlpkgs)
420 self.populateTs(keepold=0)
421 deps = self.ts.check()
423 """ This isn't fatal, Ubuntu has this issue but it is ok. """
425 logging.warn("Dependency check failed!")
428 raise CreatorError("ordering packages for installation failed!")
430 # FIXME: callback should be refactored a little in yum
431 sys.path.append('/usr/share/yum-cli')
433 cb = callback.RPMInstallCallback()
434 cb.tsInfo = self.tsInfo
436 ret = self.runTransaction(cb)
438 self._cleanupRpmdbLocks(self.conf.installroot)
440 except yum.Errors.RepoError, e:
441 raise CreatorError("Unable to download from repo : %s" % (e,))
442 except yum.Errors.YumBaseError, e:
443 raise CreatorError("Unable to install: %s" % (e,))
445 def getAllContent(self):
446 return self.__pkgs_content
448 _pkgmgr = ["yum", Yum]