supported tizen bootstrap included mic
[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("Root 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" % name)
99
100     pkgmgr = property(fget = lambda self: self._pkgmgr,
101                       fset = lambda self, name: self._setPkgmgr(name),
102                       doc = 'package manager')
103
104     @property
105     def bootstraps(self):
106         if self._bootstraps:
107             return self._bootstraps
108         for dir in os.listdir(self.homedir):
109             metadata_fp = os.path.join(self.homedir, dir, '.metadata')
110             if os.path.exists(metadata_fp) \
111                 and 0 != os.path.getsize(metadata_fp):
112                 self._bootstraps.append(dir)
113         return self._bootstraps
114
115     def run(self, name, cmd, chdir='/', bindmounts=None):
116         self.rootdir = name
117         def mychroot():
118             os.chroot(self.rootdir)
119             os.chdir(chdir)
120
121         if isinstance(cmd, list):
122             cmd = ' '.join(cmd)
123
124         lvl = msger.get_loglevel()
125         msger.set_loglevel('quiet')
126         globalmounts = chroot.setup_chrootenv(self.rootdir, bindmounts)
127         try:
128             proxy.set_proxy_environ()
129             subprocess.call(cmd, preexec_fn=mychroot, shell=True)
130             proxy.unset_proxy_environ()
131         except:
132             raise errors.BootstrapError("Run in bootstrap fail")
133         finally:
134             chroot.cleanup_chrootenv(self.rootdir, bindmounts, globalmounts)
135
136         msger.set_loglevel(lvl)
137
138     def list(self, **kwargs):
139         bslist = []
140         for binst in self.bootstraps:
141             (mver, kver, rver) = self.status(binst)
142             bsinfo = {'name':binst, 'meego':mver, 'kernel':kver, 'rpm': rver}
143             bslist.append(bsinfo)
144
145         return bslist
146
147     def status(self, name):
148         self.rootdir = name
149         if os.path.exists(self.rootdir + '/.metadata'):
150             query_package = query_package_metadat
151         else:
152             query_package = query_package_rpmdb
153
154         name, mver = query_package(self.rootdir, 'name', 'meego-release')
155         msger.debug("MeeGo Release: %s" % mver)
156
157         name, kver = query_package(self.rootdir, 'name', 'kernel')
158         msger.debug("Kernel Version: %s" % kver)
159
160         name, rver = query_package(self.rootdir, 'name', 'rpm')
161         msger.debug("RPM Version: %s" % rver)
162
163         return (mver, kver, rver)
164
165     def create(self, name, repolist, **kwargs):
166         self.name = name
167         self.pkgmgr = 'zypp'
168         self.arch = 'i686'
169         self.rootdir = name
170         self.cachedir = '/var/tmp/mic/cache' # TBD from conf, do NOT hardcode
171
172         if 'arch' in kwargs:
173             self.arch = kwargs['arch']
174         if 'cachedir' in kwargs:
175             self.cachedir = kwargs['cachedir']
176
177         if os.path.exists(self._rootdir):
178             metadata_fp = os.path.join(self._rootdir, '.metadata')
179             if os.path.exists(metadata_fp) and \
180                0 != os.path.getsize(metadata_fp):
181                 msger.warning("bootstrap already exists") # TBD more details
182                 return
183             else:
184                 shutil.rmtree(self._rootdir)
185
186         if not os.path.exists(self._rootdir):
187             os.makedirs(self._rootdir)
188
189         pkg_manager = self.pkgmgr(self.arch, self.rootdir, self.cachedir)
190         pkg_manager.setup()
191
192         for repo in repolist:
193             if 'proxy' in repo.keys():
194                 pkg_manager.addRepository(repo['name'], repo['baseurl'], proxy = repo['proxy'])
195             else:
196                 pkg_manager.addRepository(repo['name'], repo['baseurl'])
197
198         rpm.addMacro("_dbpath", "/var/lib/rpm")
199         rpm.addMacro("__file_context_path", "%{nil}")
200
201         for grp in minibase_grps:
202             pkg_manager.selectGroup(grp)
203         for pkg in minibase_pkgs:
204             pkg_manager.selectPackage(pkg)
205         for pkg in required_pkgs:
206             pkg_manager.selectPackage(pkg)
207
208         try:
209             pkg_manager.runInstall(512 * 1024L * 1024L)
210         except:
211             raise errors.BootstrapError("Create bootstrap fail")
212         else:
213             metadata = pkg_manager.getAllContent()
214             metadata_fp = os.path.join(self.rootdir, '.metadata')
215             with open(metadata_fp, 'w') as f:
216                 pickle.dump(metadata, f)
217             f.close()
218         finally:
219             pkg_manager.closeRpmDB()
220             pkg_manager.close()
221
222         # Copy bootstrap repo files
223         srcdir = "%s/etc/zypp/repos.d/" % self.cachedir
224         destdir= "%s/etc/zypp/repos.d/" % os.path.abspath(self.rootdir)
225         shutil.rmtree(destdir, ignore_errors = True)
226         shutil.copytree(srcdir, destdir)
227         # create '/tmp' in chroot
228         if not os.path.exists(os.path.join(os.path.abspath(self.rootdir), "tmp")):
229             os.makedirs(os.path.join(os.path.abspath(self.rootdir), "tmp"))
230
231         msger.info("Bootstrap created.")
232
233     def rebuild(self):
234         pass
235
236     def update(self, name):
237         self.rootdir = name
238         chrootdir = self.rootdir
239
240         def mychroot():
241             os.chroot(chrootdir)
242
243         shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
244         try:
245             subprocess.call("zypper -n --no-gpg-checks update", preexec_fn=mychroot, shell=True)
246         except OSError, err:
247             raise errors.BootstrapError("Bootstrap: %s Update fail" % chrootdir)
248
249     def cleanup(self, name):
250         self.rootdir = name
251         try:
252             chroot.cleanup_mounts(self.rootdir)
253             shutil.rmtree(self.rootdir, ignore_errors=True)
254         except:
255             raise errors.BootstrapError("Bootstrap: %s clean up fail " % self.rootdir)