Another method of install tpk.
[tools/mic.git] / mic / rt_util.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 glob
22 import re
23 import shutil
24 import subprocess
25 import ctypes
26
27 from mic import bootstrap, msger
28 from mic.conf import configmgr
29 from mic.utils import errors, proxy
30 from mic.utils.fs_related import find_binary_path, makedirs
31 from mic.chroot import setup_chrootenv, cleanup_chrootenv, ELF_arch
32
33 _libc = ctypes.cdll.LoadLibrary(None)
34 _errno = ctypes.c_int.in_dll(_libc, "errno")
35 _libc.personality.argtypes = [ctypes.c_ulong]
36 _libc.personality.restype = ctypes.c_int
37 _libc.unshare.argtypes = [ctypes.c_int,]
38 _libc.unshare.restype = ctypes.c_int
39
40 expath = lambda p: os.path.abspath(os.path.expanduser(p))
41
42 PER_LINUX32=0x0008
43 PER_LINUX=0x0000
44 personality_defs = {
45     'x86_64': PER_LINUX, 'ppc64': PER_LINUX, 'sparc64': PER_LINUX,
46     'i386': PER_LINUX32, 'i586': PER_LINUX32, 'i686': PER_LINUX32,
47     'ppc': PER_LINUX32, 'sparc': PER_LINUX32, 'sparcv9': PER_LINUX32,
48     'ia64' : PER_LINUX, 'alpha' : PER_LINUX,
49     's390' : PER_LINUX32, 's390x' : PER_LINUX,
50 }
51
52 def condPersonality(per=None):
53     if per is None or per in ('noarch',):
54         return
55     if personality_defs.get(per, None) is None:
56         return
57     res = _libc.personality(personality_defs[per])
58     if res == -1:
59         raise OSError(_errno.value, os.strerror(_errno.value))
60
61 def inbootstrap():
62     if os.path.exists(os.path.join("/", ".chroot.lock")):
63         return True
64     return (os.stat("/").st_ino != 2)
65
66 def bootstrap_mic(argv=None):
67
68
69     def mychroot():
70         os.chroot(rootdir)
71         os.chdir(cwd)
72
73     # by default, sys.argv is used to run mic in bootstrap
74     if not argv:
75         argv = sys.argv
76     if argv[0] not in ('/usr/bin/mic', 'mic'):
77         argv[0] = '/usr/bin/mic'
78
79     cropts = configmgr.create
80     bsopts = configmgr.bootstrap
81     distro = bsopts['distro_name'].lower()
82
83     rootdir = bsopts['rootdir']
84     pkglist = bsopts['packages']
85     cwd = os.getcwd()
86
87     # create bootstrap and run mic in bootstrap
88     bsenv = bootstrap.Bootstrap(rootdir, distro, cropts['arch'])
89     bsenv.logfile = cropts['logfile']
90     # rootdir is regenerated as a temp dir
91     rootdir = bsenv.rootdir
92
93     if 'optional' in bsopts:
94         optlist = bsopts['optional']
95     else:
96         optlist = []
97
98     try:
99         msger.info("Creating %s bootstrap ..." % distro)
100         bsenv.create(cropts['repomd'], pkglist, optlist)
101
102         # bootstrap is relocated under "bootstrap"
103         if os.path.exists(os.path.join(rootdir, "bootstrap")):
104             rootdir = os.path.join(rootdir, "bootstrap")
105
106         bsenv.dirsetup(rootdir)
107         sync_mic(rootdir, plugin=cropts['plugin_dir'])
108
109         #FIXME: sync the ks file to bootstrap
110         if "/" == os.path.dirname(os.path.abspath(configmgr._ksconf)):
111             safecopy(configmgr._ksconf, rootdir)
112
113         msger.info("Start mic in bootstrap: %s\n" % rootdir)
114         bsarch = ELF_arch(rootdir)
115         if bsarch in personality_defs:
116             condPersonality(bsarch)
117         bindmounts = get_bindmounts(cropts)
118         ret = bsenv.run(argv, cwd, rootdir, bindmounts)
119
120     except errors.BootstrapError, err:
121         raise errors.CreatorError("Failed to download/install bootstrap package " \
122                                   "or the package is in bad format: %s" % err)
123     except RuntimeError, err:
124         #change exception type but keep the trace back
125         value, tb = sys.exc_info()[1:]
126         raise errors.CreatorError, value, tb
127     else:
128         sys.exit(ret)
129     finally:
130         bsenv.cleanup()
131
132 def get_bindmounts(cropts):
133     binddirs =  [
134                   os.getcwd(),
135                   cropts['tmpdir'],
136                   cropts['cachedir'],
137                   cropts['outdir'],
138                   cropts['local_pkgs_path'],
139                 ]
140     bindfiles = [
141                   cropts['logfile'],
142                   configmgr._ksconf,
143                 ]
144
145     for lrepo in cropts['localrepos']:
146         binddirs.append(lrepo)
147     for ltpkrepo in cropts['localtpkrepos']:
148         binddirs.append(ltpkrepo)
149
150     bindlist = map(expath, filter(None, binddirs))
151     bindlist += map(os.path.dirname, map(expath, filter(None, bindfiles)))
152     bindlist = sorted(set(bindlist))
153     bindmounts = ';'.join(bindlist)
154     return bindmounts
155
156
157 def get_mic_binpath():
158     fp = None
159     try:
160         import pkg_resources # depends on 'setuptools'
161         dist = pkg_resources.get_distribution('mic')
162         # the real script is under EGG_INFO/scripts
163         if dist.has_metadata('scripts/mic'):
164             fp = os.path.join(dist.egg_info, "scripts/mic")
165     except ImportError:
166         pass
167     except pkg_resources.DistributionNotFound:
168         pass
169
170
171     if fp:
172         return fp
173
174     # not found script if 'flat' egg installed
175     try:
176         return find_binary_path('mic')
177     except errors.CreatorError:
178         raise errors.BootstrapError("Can't find mic binary in host OS")
179
180
181 def get_mic_modpath():
182     try:
183         import mic
184     except ImportError:
185         raise errors.BootstrapError("Can't find mic module in host OS")
186     path = os.path.abspath(mic.__file__)
187     return os.path.dirname(path)
188
189 def get_mic_libpath():
190     return os.getenv("MIC_LIBRARY_PATH", "/usr/lib/mic")
191
192 # the hard code path is prepared for bootstrap
193 def sync_mic(bootstrap, binpth = '/usr/bin/mic',
194              libpth='/usr/lib',
195              plugin='/usr/lib/mic/plugins',
196              pylib = '/usr/lib/python2.7/site-packages',
197              conf = '/etc/mic/mic.conf'):
198     _path = lambda p: os.path.join(bootstrap, p.lstrip('/'))
199
200     micpaths = {
201                  'binpth': get_mic_binpath(),
202                  'libpth': get_mic_libpath(),
203                  'plugin': plugin,
204                  'pylib': get_mic_modpath(),
205                  'conf': '/etc/mic/mic.conf',
206                }
207
208     if not os.path.exists(_path(pylib)):
209         pyptn = '/usr/lib/python?.?/site-packages'
210         pylibs = glob.glob(_path(pyptn))
211         if pylibs:
212             pylib = pylibs[0].replace(bootstrap, '')
213         else:
214             raise errors.BootstrapError("Can't find python site dir in: %s" %
215                                         bootstrap)
216
217     for key, value in micpaths.items():
218         try:
219             safecopy(value, _path(eval(key)), False, ["*.pyc", "*.pyo"])
220         except (OSError, IOError), err:
221             raise errors.BootstrapError(err)
222
223     # auto select backend
224     conf_str = file(_path(conf)).read()
225     conf_str = re.sub("pkgmgr\s*=\s*.*", "pkgmgr=auto", conf_str)
226     with open(_path(conf), 'w') as wf:
227         wf.write(conf_str)
228
229     # chmod +x /usr/bin/mic
230     os.chmod(_path(binpth), 0777)
231
232     # correct python interpreter
233     mic_cont = file(_path(binpth)).read()
234     mic_cont = "#!/usr/bin/python\n" + mic_cont
235     with open(_path(binpth), 'w') as wf:
236         wf.write(mic_cont)
237
238
239 def safecopy(src, dst, symlinks=False, ignore_ptns=()):
240     if os.path.isdir(src):
241         if os.path.isdir(dst):
242             dst = os.path.join(dst, os.path.basename(src))
243         if os.path.exists(dst):
244             shutil.rmtree(dst, ignore_errors=True)
245
246         src = src.rstrip('/')
247         # check common prefix to ignore copying itself
248         if dst.startswith(src + '/'):
249             ignore_ptns = list(ignore_ptns) + [ os.path.basename(src) ]
250
251         ignores = shutil.ignore_patterns(*ignore_ptns)
252         try:
253             shutil.copytree(src, dst, symlinks, ignores)
254         except (OSError, IOError):
255             shutil.rmtree(dst, ignore_errors=True)
256             raise
257     else:
258         if not os.path.isdir(dst):
259             makedirs(os.path.dirname(dst))
260
261         shutil.copy2(src, dst)