fixed wrong usage of rstrip in loop.py
[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, sys
20 import pickle
21 import shutil
22 import subprocess
23 import rpm
24
25 from mic import msger
26 from mic import chroot
27 from mic.plugin import pluginmgr
28 from mic.utils import proxy
29 from mic.utils import misc
30 from mic.utils import errors
31
32 minibase_grps = [ "tizen-bootstrap" ]
33 minibase_pkgs = [ ]
34 required_pkgs = [ "syslinux", "syslinux-extlinux", "satsolver-tools",
35                   "libzypp", "python-zypp", "qemu-arm-static", "mic" ]
36
37
38 def query_package_rpmdb(root='/', tag='name', pattern=None):
39     name = pattern
40     version = None
41     ts = rpm.TransactionSet(root)
42     mi = ts.dbMatch(tag, pattern)
43     for hdr in mi:
44         version = hdr['version']
45     return (name, version)
46
47 def query_package_metadat(root='/', tag='name', pattern=None):
48     name = pattern
49     version = None
50     try:
51         with open(root + '/.metadata', 'r') as f:
52             metadata = pickle.load(f)
53         f.close()
54     except:
55         raise errors.BootstrapError("Load %s/.metadata error" % root)
56     else:
57         for pkg in metadata.keys():
58             m = misc.RPM_RE.match(pkg)
59             if m:
60                 (n, a, v, r) = m.groups()
61             else:
62                 raise errors.BootstrapError("Wrong Format .metadata in %s"
63                                             % root)
64             if n == pattern:
65                 version = v
66     return (name, version)
67
68 class Bootstrap(object):
69     def __init__(self, homedir='/var/mic/bootstrap', **kwargs):
70         self._pkgmgr = None
71         self._rootdir = None
72         self._bootstraps = []
73         self.homedir = homedir
74
75         if not os.path.exists(self.homedir):
76             os.makedirs(self.homedir)
77
78         self.__dict__.update(**kwargs)
79
80     def _setRootdir(self, name):
81         self._rootdir = os.path.join(self.homedir, name)
82
83     def _getRootdir(self):
84         if not os.path.exists(self._rootdir):
85             raise errors.BootstrapError("dir: %s not exist" % self._rootdir)
86         return self._rootdir
87
88     rootdir = property(fget = lambda self: self._getRootdir(),
89                        fset = lambda self, name: self._setRootdir(name),
90                        doc = 'root directory')
91
92     def _setPkgmgr(self, name):
93         backend_plugins = pluginmgr.get_plugins('backend')
94         for (key, cls) in backend_plugins.iteritems():
95             if key == name:
96                 self._pkgmgr = cls
97         if not self._pkgmgr:
98             raise errors.BootstrapError("Backend: %s can't be loaded correctly"\
99                                         % name)
100
101     pkgmgr = property(fget = lambda self: self._pkgmgr,
102                       fset = lambda self, name: self._setPkgmgr(name),
103                       doc = 'package manager')
104
105     @property
106     def bootstraps(self):
107         if self._bootstraps:
108             return self._bootstraps
109         for dir in os.listdir(self.homedir):
110             metadata_fp = os.path.join(self.homedir, dir, '.metadata')
111             if os.path.exists(metadata_fp) \
112                 and 0 != os.path.getsize(metadata_fp):
113                 self._bootstraps.append(dir)
114         return self._bootstraps
115
116     def run(self, name, cmd, chdir='/', bindmounts=None):
117         self.rootdir = name
118         def mychroot():
119             os.chroot(self.rootdir)
120             os.chdir(chdir)
121
122         if isinstance(cmd, list):
123             cmd = ' '.join(cmd)
124
125         lvl = msger.get_loglevel()
126         msger.set_loglevel('quiet')
127         globalmounts = chroot.setup_chrootenv(self.rootdir, bindmounts)
128         try:
129             proxy.set_proxy_environ()
130             subprocess.call(cmd, preexec_fn=mychroot, shell=True)
131             proxy.unset_proxy_environ()
132         except:
133             raise errors.BootstrapError("Run in bootstrap fail")
134         finally:
135             chroot.cleanup_chrootenv(self.rootdir, bindmounts, globalmounts)
136
137         msger.set_loglevel(lvl)
138
139     def list(self, **kwargs):
140         bslist = []
141         for binst in self.bootstraps:
142             (mver, kver, rver) = self.status(binst)
143             bsinfo = {'name':binst, 'meego':mver, 'kernel':kver, 'rpm': rver}
144             bslist.append(bsinfo)
145
146         return bslist
147
148     def status(self, name):
149         self.rootdir = name
150         if os.path.exists(self.rootdir + '/.metadata'):
151             query_package = query_package_metadat
152         else:
153             query_package = query_package_rpmdb
154
155         name, mver = query_package(self.rootdir, 'name', 'meego-release')
156         msger.debug("MeeGo Release: %s" % mver)
157
158         name, kver = query_package(self.rootdir, 'name', 'kernel')
159         msger.debug("Kernel Version: %s" % kver)
160
161         name, rver = query_package(self.rootdir, 'name', 'rpm')
162         msger.debug("RPM Version: %s" % rver)
163
164         return (mver, kver, rver)
165
166     def create(self, name, repolist, **kwargs):
167         self.name = name
168         self.pkgmgr = 'zypp'
169         self.arch = 'i686'
170         self.rootdir = name
171         self.cachedir = '/var/tmp/mic/cache' # TBD from conf, do NOT hardcode
172
173         if 'arch' in kwargs:
174             self.arch = kwargs['arch']
175         if 'cachedir' in kwargs:
176             self.cachedir = kwargs['cachedir']
177
178         if os.path.exists(self._rootdir):
179             metadata_fp = os.path.join(self._rootdir, '.metadata')
180             if os.path.exists(metadata_fp) and \
181                0 != os.path.getsize(metadata_fp):
182                 msger.warning("bootstrap already exists") # TBD more details
183                 return
184             else:
185                 shutil.rmtree(self._rootdir)
186
187         if not os.path.exists(self._rootdir):
188             os.makedirs(self._rootdir)
189
190         pkg_manager = self.pkgmgr(self.arch, self.rootdir, self.cachedir)
191         pkg_manager.setup()
192
193         for repo in repolist:
194             if 'proxy' in repo:
195                 pkg_manager.addRepository(repo['name'], repo['baseurl'],
196                                           proxy=repo['proxy'])
197             else:
198                 pkg_manager.addRepository(repo['name'], repo['baseurl'])
199
200         rpm.addMacro("_dbpath", "/var/lib/rpm")
201         rpm.addMacro("__file_context_path", "%{nil}")
202
203         for grp in minibase_grps:
204             pkg_manager.selectGroup(grp)
205         for pkg in minibase_pkgs:
206             pkg_manager.selectPackage(pkg)
207         for pkg in required_pkgs:
208             pkg_manager.selectPackage(pkg)
209
210         try:
211             pkg_manager.runInstall(512 * 1024L * 1024L)
212         except:
213             raise errors.BootstrapError("Create bootstrap fail")
214         else:
215             metadata = pkg_manager.getAllContent()
216             metadata_fp = os.path.join(self.rootdir, '.metadata')
217             with open(metadata_fp, 'w') as f:
218                 pickle.dump(metadata, f)
219             f.close()
220         finally:
221             pkg_manager.closeRpmDB()
222             pkg_manager.close()
223
224         # Copy bootstrap repo files
225         srcdir = "%s/etc/zypp/repos.d/" % self.cachedir
226         destdir= "%s/etc/zypp/repos.d/" % os.path.abspath(self.rootdir)
227         shutil.rmtree(destdir, ignore_errors = True)
228         shutil.copytree(srcdir, destdir)
229         # create '/tmp' in chroot
230         _tmpdir = os.path.join(os.path.abspath(self.rootdir), "tmp")
231         if not os.path.exists(_tmpdir):
232             os.makedirs(_tmpdir)
233
234         msger.info("Bootstrap created.")
235
236     def rebuild(self):
237         pass
238
239     def update(self, name):
240         self.rootdir = name
241         chrootdir = self.rootdir
242
243         def mychroot():
244             os.chroot(chrootdir)
245
246         shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
247         try:
248             subprocess.call("zypper -n --no-gpg-checks update",
249                             preexec_fn=mychroot, shell=True)
250         except OSError, err:
251             raise errors.BootstrapError("Bootstrap: %s update failed" %\
252                                         chrootdir)
253
254     def cleanup(self, name):
255         self.rootdir = name
256         try:
257             chroot.cleanup_mounts(self.rootdir)
258             shutil.rmtree(self.rootdir, ignore_errors=True)
259         except:
260             raise errors.BootstrapError("Bootstrap: %s clean up failed" %\
261                                         self.rootdir)