3 # Copyright (c) 2009, 2010, 2011 Intel, Inc.
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation; version 2 of the License
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 from __future__ import with_statement
26 from mic.utils import errors, proxy, misc
27 from mic.utils.rpmmisc import readRpmHeader, RPMInstallCallback
28 from mic.chroot import cleanup_mounts, setup_chrootenv, cleanup_chrootenv
30 PATH_BOOTSTRAP = "/usr/sbin:/usr/bin:/sbin:/bin"
33 rpm.RPMTRANS_FLAG_ALLFILES,
34 rpm.RPMTRANS_FLAG_NOSCRIPTS,
35 rpm.RPMTRANS_FLAG_NOTRIGGERS,
39 rpm._RPMVSF_NOSIGNATURES,
44 rpm.RPMPROB_FILTER_OLDPACKAGE,
45 rpm.RPMPROB_FILTER_REPLACEPKG,
46 rpm.RPMPROB_FILTER_IGNOREARCH
49 class MiniBackend(object):
50 def __init__(self, rootdir, arch=None, repomd=None):
52 self.rootdir = os.path.abspath(rootdir)
60 self.scriptlets = False
70 self._ts = rpm.TransactionSet(self.rootdir)
71 self._ts.setFlags(reduce(lambda x, y: x|y, RPMTRANS_FLAGS))
72 self._ts.setVSFlags(reduce(lambda x, y: x|y, RPMVSF_FLAGS))
73 self._ts.setProbFilter(reduce(lambda x, y: x|y, RPMPROB_FLAGS))
82 ts = property(fget = lambda self: self.get_ts(),
83 fdel = lambda self: self.del_ts(),
84 doc="TransactionSet object")
86 def selectPackage(self, pkg):
87 if not pkg in self.dlpkgs:
88 self.dlpkgs.append(pkg)
95 if not self.scriptlets:
98 for pkg in self.preins.keys():
99 prog, script = self.preins[pkg]
100 self.run_pkg_script(pkg, prog, script, '0')
101 for pkg in self.postins.keys():
102 prog, script = self.postins[pkg]
103 self.run_pkg_script(pkg, prog, script, '1')
105 def downloadPkgs(self):
107 for pkg in self.dlpkgs:
108 localpth = misc.get_package(pkg, self.repomd, self.arch)
110 self.localpkgs[pkg] = localpth
111 elif pkg in self.optionals:
119 raise errors.BootstrapError("Can't get rpm binary: %s" %
122 def installPkgs(self):
123 for pkg in self.localpkgs.keys():
124 rpmpath = self.localpkgs[pkg]
126 hdr = readRpmHeader(self.ts, rpmpath)
128 # save prein and postin scripts
129 self.preins[pkg] = (hdr['PREINPROG'], hdr['PREIN'])
130 self.postins[pkg] = (hdr['POSTINPROG'], hdr['POSTIN'])
132 # mark pkg as install
133 self.ts.addInstall(hdr, rpmpath, 'u')
137 cb = RPMInstallCallback(self.ts)
138 errs = self.ts.run(cb.callback, '')
140 # ts.run() exit codes are, hmm, "creative": None means all ok, empty
141 # list means some errors happened in the transaction and non-empty
142 # list that there were errors preventing the ts from starting...
144 raise errors.BootstrapError("Transaction couldn't start: %s" % '\n'.join(errs))
146 def run_pkg_script(self, pkg, prog, script, arg):
147 mychroot = lambda: os.chroot(self.rootdir)
153 prog = "/usr/bin/lua"
155 tmpdir = os.path.join(self.rootdir, "tmp")
156 if not os.path.exists(tmpdir):
158 tmpfd, tmpfp = tempfile.mkstemp(dir=tmpdir, prefix="%s.pre-" % pkg)
159 script = script.replace('\r', '')
160 os.write(tmpfd, script)
162 os.chmod(tmpfp, 0700)
165 script_fp = os.path.join('/tmp', os.path.basename(tmpfp))
166 subprocess.call([prog, script_fp, arg], preexec_fn=mychroot)
167 except (OSError, IOError), err:
168 msger.warning(str(err))
172 class Bootstrap(object):
173 def __init__(self, rootdir, distro, arch=None):
174 self.rootdir = misc.mkdtemp(dir=rootdir, prefix=distro)
184 def get_rootdir(self):
185 if os.path.exists(self.rootdir):
186 shutil.rmtree(self.rootdir, ignore_errors=True)
187 os.makedirs(self.rootdir)
190 def dirsetup(self, rootdir=None):
191 _path = lambda pth: os.path.join(rootdir, pth.lstrip('/'))
194 rootdir = self.rootdir
197 # make /tmp and /etc path
198 tmpdir = _path('/tmp')
199 if not os.path.exists(tmpdir):
201 etcdir = _path('/etc')
202 if not os.path.exists(etcdir):
206 tzdist = _path('/etc/%s-release' % self.distro)
207 if not os.path.exists(tzdist):
208 with open(tzdist, 'w') as wf:
209 wf.write("bootstrap")
213 def create(self, repomd, pkglist, optlist=()):
215 pkgmgr = MiniBackend(self.get_rootdir())
216 pkgmgr.arch = self.arch
217 pkgmgr.repomd = repomd
218 pkgmgr.optionals = list(optlist)
219 map(pkgmgr.selectPackage, pkglist + list(optlist))
221 except (OSError, IOError, errors.CreatorError), err:
222 raise errors.BootstrapError("%s" % err)
224 def run(self, cmd, chdir, rootdir=None, bindmounts=None):
229 def sync_timesetting(rootdir):
231 # sync time and zone info to bootstrap
232 if os.path.exists(rootdir + "/etc/localtime"):
233 os.unlink(rootdir + "/etc/localtime")
234 shutil.copyfile("/etc/localtime", rootdir + "/etc/localtime")
238 def sync_passwdfile(rootdir):
240 # sync passwd file to bootstrap, saving the user info
241 if os.path.exists(rootdir + "/etc/passwd"):
242 os.unlink(rootdir + "/etc/passwd")
243 shutil.copyfile("/etc/passwd", rootdir + "/etc/passwd")
248 rootdir = self.rootdir
250 if isinstance(cmd, list):
256 env['PATH'] = "%s:%s" % (PATH_BOOTSTRAP, env['PATH'])
261 proxy.set_proxy_environ()
262 gloablmounts = setup_chrootenv(rootdir, bindmounts)
263 sync_timesetting(rootdir)
264 sync_passwdfile(rootdir)
265 retcode = subprocess.call(cmd, preexec_fn=mychroot, env=env, shell=shell)
266 except (OSError, IOError):
267 # add additional information to original exception
268 value, tb = sys.exc_info()[1:]
269 value = '%s: %s' % (value, ' '.join(cmd))
270 raise RuntimeError, value, tb
272 #if self.logfile and os.path.isfile(self.logfile):
273 # msger.log(file(self.logfile).read())
274 cleanup_chrootenv(rootdir, bindmounts, gloablmounts)
275 proxy.unset_proxy_environ()
281 cleanup_mounts(self.rootdir)
283 shutil.rmtree(self.rootdir, ignore_errors=True)