61b0bb25fa9f07f5525aafa0e47933638466b23c
[platform/upstream/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         if cropts['use_mic_in_bootstrap']:
108             msger.info("No copy host mic")
109         else:
110             msger.info("Copy host mic to bootstrap")
111             sync_mic(rootdir, plugin=cropts['plugin_dir'])
112
113         #FIXME: sync the ks file to bootstrap
114         if "/" == os.path.dirname(os.path.abspath(configmgr._ksconf)):
115             safecopy(configmgr._ksconf, rootdir)
116
117         msger.info("Start mic in bootstrap: %s\n" % rootdir)
118         bsarch = ELF_arch(rootdir)
119         if bsarch in personality_defs:
120             condPersonality(bsarch)
121         bindmounts = get_bindmounts(cropts)
122         ret = bsenv.run(argv, cwd, rootdir, bindmounts)
123
124     except errors.BootstrapError, err:
125         raise errors.CreatorError("Failed to download/install bootstrap package " \
126                                   "or the package is in bad format: %s" % err)
127     except RuntimeError, err:
128         #change exception type but keep the trace back
129         value, tb = sys.exc_info()[1:]
130         raise errors.CreatorError, value, tb
131     else:
132         sys.exit(ret)
133     finally:
134         bsenv.cleanup()
135
136 def get_bindmounts(cropts):
137     binddirs =  [
138                   os.getcwd(),
139                   cropts['tmpdir'],
140                   cropts['cachedir'],
141                   cropts['outdir'],
142                   cropts['local_pkgs_path'],
143                 ]
144     bindfiles = [
145                   cropts['logfile'],
146                   configmgr._ksconf,
147                 ]
148
149     for lrepo in cropts['localrepos']:
150         binddirs.append(lrepo)
151     for ltpkrepo in cropts['localtpkrepos']:
152         binddirs.append(ltpkrepo)
153
154     bindlist = map(expath, filter(None, binddirs))
155     bindlist += map(os.path.dirname, map(expath, filter(None, bindfiles)))
156     bindlist = sorted(set(bindlist))
157     bindmounts = ';'.join(bindlist)
158     return bindmounts
159
160
161 def get_mic_binpath():
162     fp = None
163     try:
164         import pkg_resources # depends on 'setuptools'
165         dist = pkg_resources.get_distribution('mic')
166         # the real script is under EGG_INFO/scripts
167         if dist.has_metadata('scripts/mic'):
168             fp = os.path.join(dist.egg_info, "scripts/mic")
169     except ImportError:
170         pass
171     except pkg_resources.DistributionNotFound:
172         pass
173
174
175     if fp:
176         return fp
177
178     # not found script if 'flat' egg installed
179     try:
180         return find_binary_path('mic')
181     except errors.CreatorError:
182         raise errors.BootstrapError("Can't find mic binary in host OS")
183
184
185 def get_mic_modpath():
186     try:
187         import mic
188     except ImportError:
189         raise errors.BootstrapError("Can't find mic module in host OS")
190     path = os.path.abspath(mic.__file__)
191     return os.path.dirname(path)
192
193 def get_mic_libpath():
194     return os.getenv("MIC_LIBRARY_PATH", "/usr/lib/mic")
195
196 # the hard code path is prepared for bootstrap
197 def sync_mic(bootstrap, binpth = '/usr/bin/mic',
198              libpth='/usr/lib',
199              plugin='/usr/lib/mic/plugins',
200              pylib = '/usr/lib/python2.7/site-packages',
201              conf = '/etc/mic/mic.conf'):
202     _path = lambda p: os.path.join(bootstrap, p.lstrip('/'))
203
204     micpaths = {
205                  'binpth': get_mic_binpath(),
206                  'libpth': get_mic_libpath(),
207                  'plugin': plugin,
208                  'pylib': get_mic_modpath(),
209                  'conf': '/etc/mic/mic.conf',
210                }
211
212     if not os.path.exists(_path(pylib)):
213         pyptn = '/usr/lib/python?.?/site-packages'
214         pylibs = glob.glob(_path(pyptn))
215         if pylibs:
216             pylib = pylibs[0].replace(bootstrap, '')
217         else:
218             raise errors.BootstrapError("Can't find python site dir in: %s" %
219                                         bootstrap)
220
221     for key, value in micpaths.items():
222         try:
223             safecopy(value, _path(eval(key)), False, ["*.pyc", "*.pyo"])
224         except (OSError, IOError), err:
225             raise errors.BootstrapError(err)
226
227     # auto select backend
228     conf_str = file(_path(conf)).read()
229     conf_str = re.sub("pkgmgr\s*=\s*.*", "pkgmgr=auto", conf_str)
230     with open(_path(conf), 'w') as wf:
231         wf.write(conf_str)
232
233     # chmod +x /usr/bin/mic
234     os.chmod(_path(binpth), 0777)
235
236     # correct python interpreter
237     mic_cont = file(_path(binpth)).read()
238     mic_cont = "#!/usr/bin/python\n" + mic_cont
239     with open(_path(binpth), 'w') as wf:
240         wf.write(mic_cont)
241
242
243 def safecopy(src, dst, symlinks=False, ignore_ptns=()):
244     if os.path.isdir(src):
245         if os.path.isdir(dst):
246             dst = os.path.join(dst, os.path.basename(src))
247         if os.path.exists(dst):
248             shutil.rmtree(dst, ignore_errors=True)
249
250         src = src.rstrip('/')
251         # check common prefix to ignore copying itself
252         if dst.startswith(src + '/'):
253             ignore_ptns = list(ignore_ptns) + [ os.path.basename(src) ]
254
255         ignores = shutil.ignore_patterns(*ignore_ptns)
256         try:
257             shutil.copytree(src, dst, symlinks, ignores)
258         except (OSError, IOError):
259             shutil.rmtree(dst, ignore_errors=True)
260             raise
261     else:
262         if not os.path.isdir(dst):
263             makedirs(os.path.dirname(dst))
264
265         shutil.copy2(src, dst)