clean mounts in bootstrap.cleanup
[tools/mic.git] / mic / bootstrap.py
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
18 from __future__ import with_statement
19 import os
20 import sys
21 import tempfile
22 import shutil
23 import subprocess
24 import rpm
25 from mic import msger
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
29
30 RPMTRANS_FLAGS = [rpm.RPMTRANS_FLAG_ALLFILES,
31                   rpm.RPMTRANS_FLAG_NOSCRIPTS,
32                   rpm.RPMTRANS_FLAG_NOTRIGGERS,
33                   rpm.RPMTRANS_FLAG_NODOCS]
34
35 RPMVSF_FLAGS = [rpm._RPMVSF_NOSIGNATURES,
36                 rpm._RPMVSF_NODIGESTS]
37
38 class MiniBackend(object):
39     def __init__(self, rootdir, repomd=None):
40         self._ts = None
41         self.rootdir = os.path.abspath(rootdir)
42         self.repomd = repomd
43         self.dlpkgs = []
44         self.localpkgs = {}
45         self.preins = {}
46         self.postins = {}
47
48     def __del__(self):
49         try:
50             del self.ts
51         except:
52             pass
53
54     def get_ts(self):
55         if not self._ts:
56             self._ts = rpm.TransactionSet(self.rootdir)
57             self._ts.setFlags(reduce(lambda x, y: x|y, RPMTRANS_FLAGS))
58             self._ts.setVSFlags(reduce(lambda x, y: x|y, RPMVSF_FLAGS))
59         return self._ts
60
61     def del_ts(self):
62         if self._ts:
63             self._ts.closeDB()
64             self._ts = None
65
66     ts = property(fget = lambda self: self.get_ts(),
67                   fdel = lambda self: self.del_ts(),
68                   doc="TransactionSet object")
69
70     def selectPackage(self, pkg):
71         if not pkg in self.dlpkgs:
72             self.dlpkgs.append(pkg)
73
74     def runInstall(self):
75         # FIXME: check space
76         self.downloadPkgs()
77         self.installPkgs()
78
79         for pkg in self.preins.keys():
80             prog, script = self.preins[pkg]
81             self.run_pkg_script(pkg, prog, script, '0')
82         for pkg in self.postins.keys():
83             prog, script = self.postins[pkg]
84             self.run_pkg_script(pkg, prog, script, '1')
85
86     def downloadPkgs(self):
87         nonexist = []
88         for pkg in self.dlpkgs:
89             try:
90                 localpth = misc.get_package(pkg, self.repomd, None)
91                 if not localpth:
92                     # skip non-existent rpm
93                     nonexist.append(pkg)
94                     continue
95                 self.localpkgs[pkg] = localpth
96             except:
97                 raise
98
99         if nonexist:
100             msger.warning("\ncan't get rpm binary: %s" % ','.join(nonexist))
101
102     def installPkgs(self):
103         for pkg in self.localpkgs.keys():
104             rpmpath = self.localpkgs[pkg]
105
106             hdr = readRpmHeader(self.ts, rpmpath)
107
108             # save prein and postin scripts
109             self.preins[pkg] = (hdr['PREINPROG'], hdr['PREIN'])
110             self.postins[pkg] = (hdr['POSTINPROG'], hdr['POSTIN'])
111
112             # mark pkg as install
113             self.ts.addInstall(hdr, rpmpath, 'u')
114
115         # run transaction
116         self.ts.order()
117         cb = RPMInstallCallback(self.ts)
118         self.ts.run(cb.callback, '')
119
120     def run_pkg_script(self, pkg, prog, script, arg):
121         mychroot = lambda: os.chroot(self.rootdir)
122
123         if not script:
124             return
125
126         if prog == "<lua>":
127              prog = "/usr/bin/lua"
128
129         tmpdir = os.path.join(self.rootdir, "tmp")
130         if not os.path.exists(tmpdir):
131             os.makedirs(tmpdir)
132         tmpfd, tmpfp = tempfile.mkstemp(dir=tmpdir, prefix="%s.pre-" % pkg)
133         script = script.replace('\r', '')
134         os.write(tmpfd, script)
135         os.close(tmpfd)
136         os.chmod(tmpfp, 0700)
137
138         try:
139             script_fp = os.path.join('/tmp', os.path.basename(tmpfp))
140             subprocess.call([prog, script_fp, arg], preexec_fn=mychroot)
141         except (OSError, IOError), err:
142             raise RuntimeError(err)
143         finally:
144             os.unlink(tmpfp)
145
146 class Bootstrap(object):
147     def __init__(self, rootdir, distro):
148         self.rootdir = rootdir
149         self.distro = distro
150         self.pkgslist = []
151         self.repomd = None
152
153     def __del__(self):
154         self.cleanup()
155
156     def get_rootdir(self):
157         if os.path.exists(self.rootdir):
158             shutil.rmtree(self.rootdir, ignore_errors=True)
159         os.makedirs(self.rootdir)
160         return self.rootdir
161
162     def _path(self, pth):
163         return os.path.join(self.rootdir, pth.lstrip('/'))
164
165     def create(self, repomd, pkglist):
166         try:
167             pkgmgr = MiniBackend(self.get_rootdir())
168             pkgmgr.repomd = repomd
169             map(pkgmgr.selectPackage, pkglist)
170             pkgmgr.runInstall()
171
172             # make /tmp path
173             tmpdir = self._path('/tmp')
174             if not os.path.exists(tmpdir):
175                 os.makedirs(tmpdir)
176
177             # touch distro file
178             tzdist = self._path('/etc/%s-release' % self.distro)
179             if not os.path.exists(tzdist):
180                 with open(tzdist, 'w') as wf:
181                     wf.write("bootstrap")
182
183         except (OSError, IOError, errors.CreatorError), err:
184             raise errors.BootstrapError("%s" % err)
185
186     def run(self, cmd, chdir, bindmounts=None):
187         def mychroot():
188             os.chroot(self.rootdir)
189             os.chdir(chdir)
190
191         if isinstance(cmd, list):
192             shell = False
193         else:
194             shell = True
195
196         gloablmounts = None
197         try:
198             proxy.set_proxy_environ()
199             gloablmounts = setup_chrootenv(self.rootdir, bindmounts)
200             subprocess.call(cmd, preexec_fn = mychroot, shell=shell)
201         except (OSError, IOError), err:
202             raise RuntimeError(err)
203         finally:
204             cleanup_chrootenv(self.rootdir, bindmounts, gloablmounts)
205             proxy.unset_proxy_environ()
206
207     def cleanup(self):
208         try:
209             # clean mounts
210             cleanup_mounts(self.rootdir)
211             # remove rootdir
212             shutil.rmtree(self.rootdir, ignore_errors=True)
213         except:
214             pass