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