summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic/bootstrap.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/mic/bootstrap.py')
-rw-r--r--scripts/lib/mic/bootstrap.py279
1 files changed, 279 insertions, 0 deletions
diff --git a/scripts/lib/mic/bootstrap.py b/scripts/lib/mic/bootstrap.py
new file mode 100644
index 0000000000..66c291b0a8
--- /dev/null
+++ b/scripts/lib/mic/bootstrap.py
@@ -0,0 +1,279 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2009, 2010, 2011 Intel, Inc.
4#
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
8#
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
12# for more details.
13#
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.
17
18from __future__ import with_statement
19import os
20import sys
21import tempfile
22import shutil
23import subprocess
24import rpm
25from mic import msger
26from mic.utils import errors, proxy, misc
27from mic.utils.rpmmisc import readRpmHeader, RPMInstallCallback
28from mic.chroot import cleanup_mounts, setup_chrootenv, cleanup_chrootenv
29
30PATH_BOOTSTRAP = "/usr/sbin:/usr/bin:/sbin:/bin"
31
32RPMTRANS_FLAGS = [
33 rpm.RPMTRANS_FLAG_ALLFILES,
34 rpm.RPMTRANS_FLAG_NOSCRIPTS,
35 rpm.RPMTRANS_FLAG_NOTRIGGERS,
36 ]
37
38RPMVSF_FLAGS = [
39 rpm._RPMVSF_NOSIGNATURES,
40 rpm._RPMVSF_NODIGESTS
41 ]
42
43RPMPROB_FLAGS = [
44 rpm.RPMPROB_FILTER_OLDPACKAGE,
45 rpm.RPMPROB_FILTER_REPLACEPKG,
46 rpm.RPMPROB_FILTER_IGNOREARCH
47 ]
48
49class MiniBackend(object):
50 def __init__(self, rootdir, arch=None, repomd=None):
51 self._ts = None
52 self.rootdir = os.path.abspath(rootdir)
53 self.arch = arch
54 self.repomd = repomd
55 self.dlpkgs = []
56 self.localpkgs = {}
57 self.optionals = []
58 self.preins = {}
59 self.postins = {}
60 self.scriptlets = False
61
62 def __del__(self):
63 try:
64 del self.ts
65 except:
66 pass
67
68 def get_ts(self):
69 if not self._ts:
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))
74
75 return self._ts
76
77 def del_ts(self):
78 if self._ts:
79 self._ts.closeDB()
80 self._ts = None
81
82 ts = property(fget = lambda self: self.get_ts(),
83 fdel = lambda self: self.del_ts(),
84 doc="TransactionSet object")
85
86 def selectPackage(self, pkg):
87 if not pkg in self.dlpkgs:
88 self.dlpkgs.append(pkg)
89
90 def runInstall(self):
91 # FIXME: check space
92 self.downloadPkgs()
93 self.installPkgs()
94
95 if not self.scriptlets:
96 return
97
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')
104
105 def downloadPkgs(self):
106 nonexist = []
107 for pkg in self.dlpkgs:
108 localpth = misc.get_package(pkg, self.repomd, self.arch)
109 if localpth:
110 self.localpkgs[pkg] = localpth
111 elif pkg in self.optionals:
112 # skip optional rpm
113 continue
114 else:
115 # mark nonexist rpm
116 nonexist.append(pkg)
117
118 if nonexist:
119 raise errors.BootstrapError("Can't get rpm binary: %s" %
120 ','.join(nonexist))
121
122 def installPkgs(self):
123 for pkg in self.localpkgs.keys():
124 rpmpath = self.localpkgs[pkg]
125
126 hdr = readRpmHeader(self.ts, rpmpath)
127
128 # save prein and postin scripts
129 self.preins[pkg] = (hdr['PREINPROG'], hdr['PREIN'])
130 self.postins[pkg] = (hdr['POSTINPROG'], hdr['POSTIN'])
131
132 # mark pkg as install
133 self.ts.addInstall(hdr, rpmpath, 'u')
134
135 # run transaction
136 self.ts.order()
137 cb = RPMInstallCallback(self.ts)
138 self.ts.run(cb.callback, '')
139
140 def run_pkg_script(self, pkg, prog, script, arg):
141 mychroot = lambda: os.chroot(self.rootdir)
142
143 if not script:
144 return
145
146 if prog == "<lua>":
147 prog = "/usr/bin/lua"
148
149 tmpdir = os.path.join(self.rootdir, "tmp")
150 if not os.path.exists(tmpdir):
151 os.makedirs(tmpdir)
152 tmpfd, tmpfp = tempfile.mkstemp(dir=tmpdir, prefix="%s.pre-" % pkg)
153 script = script.replace('\r', '')
154 os.write(tmpfd, script)
155 os.close(tmpfd)
156 os.chmod(tmpfp, 0700)
157
158 try:
159 script_fp = os.path.join('/tmp', os.path.basename(tmpfp))
160 subprocess.call([prog, script_fp, arg], preexec_fn=mychroot)
161 except (OSError, IOError), err:
162 msger.warning(str(err))
163 finally:
164 os.unlink(tmpfp)
165
166class Bootstrap(object):
167 def __init__(self, rootdir, distro, arch=None):
168 self.rootdir = misc.mkdtemp(dir=rootdir, prefix=distro)
169 self.distro = distro
170 self.arch = arch
171 self.logfile = None
172 self.pkgslist = []
173 self.repomd = None
174
175 def __del__(self):
176 self.cleanup()
177
178 def get_rootdir(self):
179 if os.path.exists(self.rootdir):
180 shutil.rmtree(self.rootdir, ignore_errors=True)
181 os.makedirs(self.rootdir)
182 return self.rootdir
183
184 def dirsetup(self, rootdir=None):
185 _path = lambda pth: os.path.join(rootdir, pth.lstrip('/'))
186
187 if not rootdir:
188 rootdir = self.rootdir
189
190 try:
191 # make /tmp and /etc path
192 tmpdir = _path('/tmp')
193 if not os.path.exists(tmpdir):
194 os.makedirs(tmpdir)
195 etcdir = _path('/etc')
196 if not os.path.exists(etcdir):
197 os.makedirs(etcdir)
198
199 # touch distro file
200 tzdist = _path('/etc/%s-release' % self.distro)
201 if not os.path.exists(tzdist):
202 with open(tzdist, 'w') as wf:
203 wf.write("bootstrap")
204 except:
205 pass
206
207 def create(self, repomd, pkglist, optlist=()):
208 try:
209 pkgmgr = MiniBackend(self.get_rootdir())
210 pkgmgr.arch = self.arch
211 pkgmgr.repomd = repomd
212 pkgmgr.optionals = list(optlist)
213 map(pkgmgr.selectPackage, pkglist + list(optlist))
214 pkgmgr.runInstall()
215 except (OSError, IOError, errors.CreatorError), err:
216 raise errors.BootstrapError("%s" % err)
217
218 def run(self, cmd, chdir, rootdir=None, bindmounts=None):
219 def mychroot():
220 os.chroot(rootdir)
221 os.chdir(chdir)
222
223 def sync_timesetting(rootdir):
224 try:
225 # sync time and zone info to bootstrap
226 if os.path.exists(rootdir + "/etc/localtime"):
227 os.unlink(rootdir + "/etc/localtime")
228 shutil.copyfile("/etc/localtime", rootdir + "/etc/localtime")
229 except:
230 pass
231
232 def sync_passwdfile(rootdir):
233 try:
234 # sync passwd file to bootstrap, saving the user info
235 if os.path.exists(rootdir + "/etc/passwd"):
236 os.unlink(rootdir + "/etc/passwd")
237 shutil.copyfile("/etc/passwd", rootdir + "/etc/passwd")
238 except:
239 pass
240
241 if not rootdir:
242 rootdir = self.rootdir
243
244 if isinstance(cmd, list):
245 shell = False
246 else:
247 shell = True
248
249 env = os.environ
250 env['PATH'] = "%s:%s" % (PATH_BOOTSTRAP, env['PATH'])
251
252 retcode = 0
253 gloablmounts = None
254 try:
255 proxy.set_proxy_environ()
256 gloablmounts = setup_chrootenv(rootdir, bindmounts, False)
257 sync_timesetting(rootdir)
258 sync_passwdfile(rootdir)
259 retcode = subprocess.call(cmd, preexec_fn=mychroot, env=env, shell=shell)
260 except (OSError, IOError):
261 # add additional information to original exception
262 value, tb = sys.exc_info()[1:]
263 value = '%s: %s' % (value, ' '.join(cmd))
264 raise RuntimeError, value, tb
265 finally:
266 if self.logfile and os.path.isfile(self.logfile):
267 msger.log(file(self.logfile).read())
268 cleanup_chrootenv(rootdir, bindmounts, gloablmounts)
269 proxy.unset_proxy_environ()
270 return retcode
271
272 def cleanup(self):
273 try:
274 # clean mounts
275 cleanup_mounts(self.rootdir)
276 # remove rootdir
277 shutil.rmtree(self.rootdir, ignore_errors=True)
278 except:
279 pass