summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic/plugins/backend/yumpkgmgr.py
diff options
context:
space:
mode:
authorTom Zanussi <tom.zanussi@linux.intel.com>2013-08-24 15:31:34 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-10-01 22:56:03 +0100
commit9fc88f96d40b17c90bac53b90045a87b2d2cff84 (patch)
tree63010e5aabf895697655baf89bd668d6752b3f97 /scripts/lib/mic/plugins/backend/yumpkgmgr.py
parent53a1d9a788fd9f970af980da2ab975cca60685c4 (diff)
downloadpoky-9fc88f96d40b17c90bac53b90045a87b2d2cff84.tar.gz
wic: Add mic w/pykickstart
This is the starting point for the implemention described in [YOCTO 3847] which came to the conclusion that it would make sense to use kickstart syntax to implement image creation in OpenEmbedded. I subsequently realized that there was an existing tool that already implemented image creation using kickstart syntax, the Tizen/Meego mic tool. As such, it made sense to use that as a starting point - this commit essentially just copies the relevant Python code from the MIC tool to the scripts/lib dir, where it can be accessed by the previously created wic tool. Most of this will be removed or renamed by later commits, since we're initially focusing on partitioning only. Care should be taken so that we can easily add back any additional functionality should we decide later to expand the tool, though (we may also want to contribute our local changes to the mic tool to the Tizen project if it makes sense, and therefore should avoid gratuitous changes to the original code if possible). Added the /mic subdir from Tizen mic repo as a starting point: git clone git://review.tizen.org/tools/mic.git For reference, the top commit: commit 20164175ddc234a17b8a12c33d04b012347b1530 Author: Gui Chen <gui.chen@intel.com> Date: Sun Jun 30 22:32:16 2013 -0400 bump up to 0.19.2 Also added the /plugins subdir, moved to under the /mic subdir (to match the default plugin_dir location in mic.conf.in, which was renamed to yocto-image.conf (moved and renamed by later patches) and put into /scripts. (From OE-Core rev: 31f0360f1fd4ebc9dfcaed42d1c50d2448b4632e) Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Saul Wold <sgw@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
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)