summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic/utils/misc.py
diff options
context:
space:
mode:
authorTom Zanussi <tom.zanussi@linux.intel.com>2013-08-24 15:31:34 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-10-01 22:56:03 +0100
commit9fc88f96d40b17c90bac53b90045a87b2d2cff84 (patch)
tree63010e5aabf895697655baf89bd668d6752b3f97 /scripts/lib/mic/utils/misc.py
parent53a1d9a788fd9f970af980da2ab975cca60685c4 (diff)
downloadpoky-9fc88f96d40b17c90bac53b90045a87b2d2cff84.tar.gz
wic: Add mic w/pykickstart
This is the starting point for the implemention described in [YOCTO 3847] which came to the conclusion that it would make sense to use kickstart syntax to implement image creation in OpenEmbedded. I subsequently realized that there was an existing tool that already implemented image creation using kickstart syntax, the Tizen/Meego mic tool. As such, it made sense to use that as a starting point - this commit essentially just copies the relevant Python code from the MIC tool to the scripts/lib dir, where it can be accessed by the previously created wic tool. Most of this will be removed or renamed by later commits, since we're initially focusing on partitioning only. Care should be taken so that we can easily add back any additional functionality should we decide later to expand the tool, though (we may also want to contribute our local changes to the mic tool to the Tizen project if it makes sense, and therefore should avoid gratuitous changes to the original code if possible). Added the /mic subdir from Tizen mic repo as a starting point: git clone git://review.tizen.org/tools/mic.git For reference, the top commit: commit 20164175ddc234a17b8a12c33d04b012347b1530 Author: Gui Chen <gui.chen@intel.com> Date: Sun Jun 30 22:32:16 2013 -0400 bump up to 0.19.2 Also added the /plugins subdir, moved to under the /mic subdir (to match the default plugin_dir location in mic.conf.in, which was renamed to yocto-image.conf (moved and renamed by later patches) and put into /scripts. (From OE-Core rev: 31f0360f1fd4ebc9dfcaed42d1c50d2448b4632e) Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Saul Wold <sgw@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib/mic/utils/misc.py')
-rw-r--r--scripts/lib/mic/utils/misc.py1067
1 files changed, 1067 insertions, 0 deletions
diff --git a/scripts/lib/mic/utils/misc.py b/scripts/lib/mic/utils/misc.py
new file mode 100644
index 0000000000..63024346a9
--- /dev/null
+++ b/scripts/lib/mic/utils/misc.py
@@ -0,0 +1,1067 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 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
18import os
19import sys
20import time
21import tempfile
22import re
23import shutil
24import glob
25import hashlib
26import subprocess
27import platform
28import traceback
29
30
31try:
32 import sqlite3 as sqlite
33except ImportError:
34 import sqlite
35
36try:
37 from xml.etree import cElementTree
38except ImportError:
39 import cElementTree
40xmlparse = cElementTree.parse
41
42from mic import msger
43from mic.utils.errors import CreatorError, SquashfsError
44from mic.utils.fs_related import find_binary_path, makedirs
45from mic.utils.grabber import myurlgrab
46from mic.utils.proxy import get_proxy_for
47from mic.utils import runner
48from mic.utils import rpmmisc
49
50
51RPM_RE = re.compile("(.*)\.(.*) (.*)-(.*)")
52RPM_FMT = "%(name)s.%(arch)s %(version)s-%(release)s"
53SRPM_RE = re.compile("(.*)-(\d+.*)-(\d+\.\d+).src.rpm")
54
55
56def build_name(kscfg, release=None, prefix = None, suffix = None):
57 """Construct and return an image name string.
58
59 This is a utility function to help create sensible name and fslabel
60 strings. The name is constructed using the sans-prefix-and-extension
61 kickstart filename and the supplied prefix and suffix.
62
63 kscfg -- a path to a kickstart file
64 release -- a replacement to suffix for image release
65 prefix -- a prefix to prepend to the name; defaults to None, which causes
66 no prefix to be used
67 suffix -- a suffix to append to the name; defaults to None, which causes
68 a YYYYMMDDHHMM suffix to be used
69
70 Note, if maxlen is less then the len(suffix), you get to keep both pieces.
71
72 """
73 name = os.path.basename(kscfg)
74 idx = name.rfind('.')
75 if idx >= 0:
76 name = name[:idx]
77
78 if release is not None:
79 suffix = ""
80 if prefix is None:
81 prefix = ""
82 if suffix is None:
83 suffix = time.strftime("%Y%m%d%H%M")
84
85 if name.startswith(prefix):
86 name = name[len(prefix):]
87
88 prefix = "%s-" % prefix if prefix else ""
89 suffix = "-%s" % suffix if suffix else ""
90
91 ret = prefix + name + suffix
92 return ret
93
94def get_distro():
95 """Detect linux distribution, support "meego"
96 """
97
98 support_dists = ('SuSE',
99 'debian',
100 'fedora',
101 'redhat',
102 'centos',
103 'meego',
104 'moblin',
105 'tizen')
106 try:
107 (dist, ver, id) = platform.linux_distribution( \
108 supported_dists = support_dists)
109 except:
110 (dist, ver, id) = platform.dist( \
111 supported_dists = support_dists)
112
113 return (dist, ver, id)
114
115def get_distro_str():
116 """Get composited string for current linux distribution
117 """
118 (dist, ver, id) = get_distro()
119
120 if not dist:
121 return 'Unknown Linux Distro'
122 else:
123 distro_str = ' '.join(map(str.strip, (dist, ver, id)))
124 return distro_str.strip()
125
126_LOOP_RULE_PTH = None
127
128def hide_loopdev_presentation():
129 udev_rules = "80-prevent-loop-present.rules"
130 udev_rules_dir = [
131 '/usr/lib/udev/rules.d/',
132 '/lib/udev/rules.d/',
133 '/etc/udev/rules.d/'
134 ]
135
136 global _LOOP_RULE_PTH
137
138 for rdir in udev_rules_dir:
139 if os.path.exists(rdir):
140 _LOOP_RULE_PTH = os.path.join(rdir, udev_rules)
141
142 if not _LOOP_RULE_PTH:
143 return
144
145 try:
146 with open(_LOOP_RULE_PTH, 'w') as wf:
147 wf.write('KERNEL=="loop*", ENV{UDISKS_PRESENTATION_HIDE}="1"')
148
149 runner.quiet('udevadm trigger')
150 except:
151 pass
152
153def unhide_loopdev_presentation():
154 global _LOOP_RULE_PTH
155
156 if not _LOOP_RULE_PTH:
157 return
158
159 try:
160 os.unlink(_LOOP_RULE_PTH)
161 runner.quiet('udevadm trigger')
162 except:
163 pass
164
165def extract_rpm(rpmfile, targetdir):
166 rpm2cpio = find_binary_path("rpm2cpio")
167 cpio = find_binary_path("cpio")
168
169 olddir = os.getcwd()
170 os.chdir(targetdir)
171
172 msger.verbose("Extract rpm file with cpio: %s" % rpmfile)
173 p1 = subprocess.Popen([rpm2cpio, rpmfile], stdout=subprocess.PIPE)
174 p2 = subprocess.Popen([cpio, "-idv"], stdin=p1.stdout,
175 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
176 (sout, serr) = p2.communicate()
177 msger.verbose(sout or serr)
178
179 os.chdir(olddir)
180
181def compressing(fpath, method):
182 comp_map = {
183 "gz": "gzip",
184 "bz2": "bzip2"
185 }
186 if method not in comp_map:
187 raise CreatorError("Unsupport compress format: %s, valid values: %s"
188 % (method, ','.join(comp_map.keys())))
189 cmd = find_binary_path(comp_map[method])
190 rc = runner.show([cmd, "-f", fpath])
191 if rc:
192 raise CreatorError("Failed to %s file: %s" % (comp_map[method], fpath))
193
194def taring(dstfile, target):
195 import tarfile
196 basen, ext = os.path.splitext(dstfile)
197 comp = {".tar": None,
198 ".gz": "gz", # for .tar.gz
199 ".bz2": "bz2", # for .tar.bz2
200 ".tgz": "gz",
201 ".tbz": "bz2"}[ext]
202
203 # specify tarball file path
204 if not comp:
205 tarpath = dstfile
206 elif basen.endswith(".tar"):
207 tarpath = basen
208 else:
209 tarpath = basen + ".tar"
210 wf = tarfile.open(tarpath, 'w')
211
212 if os.path.isdir(target):
213 for item in os.listdir(target):
214 wf.add(os.path.join(target, item), item)
215 else:
216 wf.add(target, os.path.basename(target))
217 wf.close()
218
219 if comp:
220 compressing(tarpath, comp)
221 # when dstfile ext is ".tgz" and ".tbz", should rename
222 if not basen.endswith(".tar"):
223 shutil.move("%s.%s" % (tarpath, comp), dstfile)
224
225def ziping(dstfile, target):
226 import zipfile
227 wf = zipfile.ZipFile(dstfile, 'w', compression=zipfile.ZIP_DEFLATED)
228 if os.path.isdir(target):
229 for item in os.listdir(target):
230 fpath = os.path.join(target, item)
231 if not os.path.isfile(fpath):
232 continue
233 wf.write(fpath, item, zipfile.ZIP_DEFLATED)
234 else:
235 wf.write(target, os.path.basename(target), zipfile.ZIP_DEFLATED)
236 wf.close()
237
238pack_formats = {
239 ".tar": taring,
240 ".tar.gz": taring,
241 ".tar.bz2": taring,
242 ".tgz": taring,
243 ".tbz": taring,
244 ".zip": ziping,
245}
246
247def packing(dstfile, target):
248 (base, ext) = os.path.splitext(dstfile)
249 if ext in (".gz", ".bz2") and base.endswith(".tar"):
250 ext = ".tar" + ext
251 if ext not in pack_formats:
252 raise CreatorError("Unsupport pack format: %s, valid values: %s"
253 % (ext, ','.join(pack_formats.keys())))
254 func = pack_formats[ext]
255 # func should be callable
256 func(dstfile, target)
257
258def human_size(size):
259 """Return human readable string for Bytes size
260 """
261
262 if size <= 0:
263 return "0M"
264 import math
265 measure = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
266 expo = int(math.log(size, 1024))
267 mant = float(size/math.pow(1024, expo))
268 return "{0:.1f}{1:s}".format(mant, measure[expo])
269
270def get_block_size(file_obj):
271 """ Returns block size for file object 'file_obj'. Errors are indicated by
272 the 'IOError' exception. """
273
274 from fcntl import ioctl
275 import struct
276
277 # Get the block size of the host file-system for the image file by calling
278 # the FIGETBSZ ioctl (number 2).
279 binary_data = ioctl(file_obj, 2, struct.pack('I', 0))
280 return struct.unpack('I', binary_data)[0]
281
282def check_space_pre_cp(src, dst):
283 """Check whether disk space is enough before 'cp' like
284 operations, else exception will be raised.
285 """
286
287 srcsize = get_file_size(src) * 1024 * 1024
288 freesize = get_filesystem_avail(dst)
289 if srcsize > freesize:
290 raise CreatorError("space on %s(%s) is not enough for about %s files"
291 % (dst, human_size(freesize), human_size(srcsize)))
292
293def calc_hashes(file_path, hash_names, start = 0, end = None):
294 """ Calculate hashes for a file. The 'file_path' argument is the file
295 to calculate hash functions for, 'start' and 'end' are the starting and
296 ending file offset to calculate the has functions for. The 'hash_names'
297 argument is a list of hash names to calculate. Returns the the list
298 of calculated hash values in the hexadecimal form in the same order
299 as 'hash_names'.
300 """
301 if end == None:
302 end = os.path.getsize(file_path)
303
304 chunk_size = 65536
305 to_read = end - start
306 read = 0
307
308 hashes = []
309 for hash_name in hash_names:
310 hashes.append(hashlib.new(hash_name))
311
312 with open(file_path, "rb") as f:
313 f.seek(start)
314
315 while read < to_read:
316 if read + chunk_size > to_read:
317 chunk_size = to_read - read
318 chunk = f.read(chunk_size)
319 for hash_obj in hashes:
320 hash_obj.update(chunk)
321 read += chunk_size
322
323 result = []
324 for hash_obj in hashes:
325 result.append(hash_obj.hexdigest())
326
327 return result
328
329def get_md5sum(fpath):
330 return calc_hashes(fpath, ('md5', ))[0]
331
332
333def normalize_ksfile(ksconf, release, arch):
334 '''
335 Return the name of a normalized ks file in which macro variables
336 @BUILD_ID@ and @ARCH@ are replace with real values.
337
338 The original ks file is returned if no special macro is used, otherwise
339 a temp file is created and returned, which will be deleted when program
340 exits normally.
341 '''
342
343 if not release:
344 release = "latest"
345 if not arch or re.match(r'i.86', arch):
346 arch = "ia32"
347
348 with open(ksconf) as f:
349 ksc = f.read()
350
351 if "@ARCH@" not in ksc and "@BUILD_ID@" not in ksc:
352 return ksconf
353
354 msger.info("Substitute macro variable @BUILD_ID@/@ARCH@ in ks: %s" % ksconf)
355 ksc = ksc.replace("@ARCH@", arch)
356 ksc = ksc.replace("@BUILD_ID@", release)
357
358 fd, ksconf = tempfile.mkstemp(prefix=os.path.basename(ksconf))
359 os.write(fd, ksc)
360 os.close(fd)
361
362 msger.debug('normalized ks file:%s' % ksconf)
363
364 def remove_temp_ks():
365 try:
366 os.unlink(ksconf)
367 except OSError, err:
368 msger.warning('Failed to remove temp ks file:%s:%s' % (ksconf, err))
369
370 import atexit
371 atexit.register(remove_temp_ks)
372
373 return ksconf
374
375
376def _check_mic_chroot(rootdir):
377 def _path(path):
378 return rootdir.rstrip('/') + path
379
380 release_files = map(_path, [ "/etc/moblin-release",
381 "/etc/meego-release",
382 "/etc/tizen-release"])
383
384 if not any(map(os.path.exists, release_files)):
385 msger.warning("Dir %s is not a MeeGo/Tizen chroot env" % rootdir)
386
387 if not glob.glob(rootdir + "/boot/vmlinuz-*"):
388 msger.warning("Failed to find kernel module under %s" % rootdir)
389
390 return
391
392def selinux_check(arch, fstypes):
393 try:
394 getenforce = find_binary_path('getenforce')
395 except CreatorError:
396 return
397
398 selinux_status = runner.outs([getenforce])
399 if arch and arch.startswith("arm") and selinux_status == "Enforcing":
400 raise CreatorError("Can't create arm image if selinux is enabled, "
401 "please run 'setenforce 0' to disable selinux")
402
403 use_btrfs = filter(lambda typ: typ == 'btrfs', fstypes)
404 if use_btrfs and selinux_status == "Enforcing":
405 raise CreatorError("Can't create btrfs image if selinux is enabled,"
406 " please run 'setenforce 0' to disable selinux")
407
408def get_image_type(path):
409 def _get_extension_name(path):
410 match = re.search("(?<=\.)\w+$", path)
411 if match:
412 return match.group(0)
413 else:
414 return None
415
416 if os.path.isdir(path):
417 _check_mic_chroot(path)
418 return "fs"
419
420 maptab = {
421 "tar": "loop",
422 "raw":"raw",
423 "vmdk":"vmdk",
424 "vdi":"vdi",
425 "iso":"livecd",
426 "usbimg":"liveusb",
427 }
428
429 extension = _get_extension_name(path)
430 if extension in maptab:
431 return maptab[extension]
432
433 fd = open(path, "rb")
434 file_header = fd.read(1024)
435 fd.close()
436 vdi_flag = "<<< Sun VirtualBox Disk Image >>>"
437 if file_header[0:len(vdi_flag)] == vdi_flag:
438 return maptab["vdi"]
439
440 output = runner.outs(['file', path])
441 isoptn = re.compile(r".*ISO 9660 CD-ROM filesystem.*(bootable).*")
442 usbimgptn = re.compile(r".*x86 boot sector.*active.*")
443 rawptn = re.compile(r".*x86 boot sector.*")
444 vmdkptn = re.compile(r".*VMware. disk image.*")
445 ext3fsimgptn = re.compile(r".*Linux.*ext3 filesystem data.*")
446 ext4fsimgptn = re.compile(r".*Linux.*ext4 filesystem data.*")
447 btrfsimgptn = re.compile(r".*BTRFS.*")
448 if isoptn.match(output):
449 return maptab["iso"]
450 elif usbimgptn.match(output):
451 return maptab["usbimg"]
452 elif rawptn.match(output):
453 return maptab["raw"]
454 elif vmdkptn.match(output):
455 return maptab["vmdk"]
456 elif ext3fsimgptn.match(output):
457 return "ext3fsimg"
458 elif ext4fsimgptn.match(output):
459 return "ext4fsimg"
460 elif btrfsimgptn.match(output):
461 return "btrfsimg"
462 else:
463 raise CreatorError("Cannot detect the type of image: %s" % path)
464
465
466def get_file_size(filename):
467 """ Return size in MB unit """
468 cmd = ['du', "-s", "-b", "-B", "1M", filename]
469 rc, duOutput = runner.runtool(cmd)
470 if rc != 0:
471 raise CreatorError("Failed to run: %s" % ' '.join(cmd))
472 size1 = int(duOutput.split()[0])
473
474 cmd = ['du', "-s", "-B", "1M", filename]
475 rc, duOutput = runner.runtool(cmd)
476 if rc != 0:
477 raise CreatorError("Failed to run: %s" % ' '.join(cmd))
478
479 size2 = int(duOutput.split()[0])
480 return max(size1, size2)
481
482
483def get_filesystem_avail(fs):
484 vfstat = os.statvfs(fs)
485 return vfstat.f_bavail * vfstat.f_bsize
486
487def convert_image(srcimg, srcfmt, dstimg, dstfmt):
488 #convert disk format
489 if dstfmt != "raw":
490 raise CreatorError("Invalid destination image format: %s" % dstfmt)
491 msger.debug("converting %s image to %s" % (srcimg, dstimg))
492 if srcfmt == "vmdk":
493 path = find_binary_path("qemu-img")
494 argv = [path, "convert", "-f", "vmdk", srcimg, "-O", dstfmt, dstimg]
495 elif srcfmt == "vdi":
496 path = find_binary_path("VBoxManage")
497 argv = [path, "internalcommands", "converttoraw", srcimg, dstimg]
498 else:
499 raise CreatorError("Invalid soure image format: %s" % srcfmt)
500
501 rc = runner.show(argv)
502 if rc == 0:
503 msger.debug("convert successful")
504 if rc != 0:
505 raise CreatorError("Unable to convert disk to %s" % dstfmt)
506
507def uncompress_squashfs(squashfsimg, outdir):
508 """Uncompress file system from squshfs image"""
509 unsquashfs = find_binary_path("unsquashfs")
510 args = [ unsquashfs, "-d", outdir, squashfsimg ]
511 rc = runner.show(args)
512 if (rc != 0):
513 raise SquashfsError("Failed to uncompress %s." % squashfsimg)
514
515def mkdtemp(dir = "/var/tmp", prefix = "mic-tmp-"):
516 """ FIXME: use the dir in mic.conf instead """
517
518 makedirs(dir)
519 return tempfile.mkdtemp(dir = dir, prefix = prefix)
520
521def get_repostrs_from_ks(ks):
522 def _get_temp_reponame(baseurl):
523 md5obj = hashlib.md5(baseurl)
524 tmpreponame = "%s" % md5obj.hexdigest()
525 return tmpreponame
526
527 kickstart_repos = []
528
529 for repodata in ks.handler.repo.repoList:
530 repo = {}
531 for attr in ('name',
532 'baseurl',
533 'mirrorlist',
534 'includepkgs', # val is list
535 'excludepkgs', # val is list
536 'cost', # int
537 'priority',# int
538 'save',
539 'proxy',
540 'proxyuser',
541 'proxypasswd',
542 'proxypasswd',
543 'debuginfo',
544 'source',
545 'gpgkey',
546 'ssl_verify'):
547 if hasattr(repodata, attr) and getattr(repodata, attr):
548 repo[attr] = getattr(repodata, attr)
549
550 if 'name' not in repo:
551 repo['name'] = _get_temp_reponame(repodata.baseurl)
552
553 kickstart_repos.append(repo)
554
555 return kickstart_repos
556
557def _get_uncompressed_data_from_url(url, filename, proxies):
558 filename = myurlgrab(url, filename, proxies)
559 suffix = None
560 if filename.endswith(".gz"):
561 suffix = ".gz"
562 runner.quiet(['gunzip', "-f", filename])
563 elif filename.endswith(".bz2"):
564 suffix = ".bz2"
565 runner.quiet(['bunzip2', "-f", filename])
566 if suffix:
567 filename = filename.replace(suffix, "")
568 return filename
569
570def _get_metadata_from_repo(baseurl, proxies, cachedir, reponame, filename,
571 sumtype=None, checksum=None):
572 url = os.path.join(baseurl, filename)
573 filename_tmp = str("%s/%s/%s" % (cachedir, reponame, os.path.basename(filename)))
574 if os.path.splitext(filename_tmp)[1] in (".gz", ".bz2"):
575 filename = os.path.splitext(filename_tmp)[0]
576 else:
577 filename = filename_tmp
578 if sumtype and checksum and os.path.exists(filename):
579 try:
580 sumcmd = find_binary_path("%ssum" % sumtype)
581 except:
582 file_checksum = None
583 else:
584 file_checksum = runner.outs([sumcmd, filename]).split()[0]
585
586 if file_checksum and file_checksum == checksum:
587 return filename
588
589 return _get_uncompressed_data_from_url(url,filename_tmp,proxies)
590
591def get_metadata_from_repos(repos, cachedir):
592 my_repo_metadata = []
593 for repo in repos:
594 reponame = repo['name']
595 baseurl = repo['baseurl']
596
597
598 if 'proxy' in repo:
599 proxy = repo['proxy']
600 else:
601 proxy = get_proxy_for(baseurl)
602
603 proxies = None
604 if proxy:
605 proxies = {str(baseurl.split(":")[0]):str(proxy)}
606
607 makedirs(os.path.join(cachedir, reponame))
608 url = os.path.join(baseurl, "repodata/repomd.xml")
609 filename = os.path.join(cachedir, reponame, 'repomd.xml')
610 repomd = myurlgrab(url, filename, proxies)
611 try:
612 root = xmlparse(repomd)
613 except SyntaxError:
614 raise CreatorError("repomd.xml syntax error.")
615
616 ns = root.getroot().tag
617 ns = ns[0:ns.rindex("}")+1]
618
619 filepaths = {}
620 checksums = {}
621 sumtypes = {}
622
623 for elm in root.getiterator("%sdata" % ns):
624 if elm.attrib["type"] == "patterns":
625 filepaths['patterns'] = elm.find("%slocation" % ns).attrib['href']
626 checksums['patterns'] = elm.find("%sopen-checksum" % ns).text
627 sumtypes['patterns'] = elm.find("%sopen-checksum" % ns).attrib['type']
628 break
629
630 for elm in root.getiterator("%sdata" % ns):
631 if elm.attrib["type"] in ("group_gz", "group"):
632 filepaths['comps'] = elm.find("%slocation" % ns).attrib['href']
633 checksums['comps'] = elm.find("%sopen-checksum" % ns).text
634 sumtypes['comps'] = elm.find("%sopen-checksum" % ns).attrib['type']
635 break
636
637 primary_type = None
638 for elm in root.getiterator("%sdata" % ns):
639 if elm.attrib["type"] in ("primary_db", "primary"):
640 primary_type = elm.attrib["type"]
641 filepaths['primary'] = elm.find("%slocation" % ns).attrib['href']
642 checksums['primary'] = elm.find("%sopen-checksum" % ns).text
643 sumtypes['primary'] = elm.find("%sopen-checksum" % ns).attrib['type']
644 break
645
646 if not primary_type:
647 continue
648
649 for item in ("primary", "patterns", "comps"):
650 if item not in filepaths:
651 filepaths[item] = None
652 continue
653 if not filepaths[item]:
654 continue
655 filepaths[item] = _get_metadata_from_repo(baseurl,
656 proxies,
657 cachedir,
658 reponame,
659 filepaths[item],
660 sumtypes[item],
661 checksums[item])
662
663 """ Get repo key """
664 try:
665 repokey = _get_metadata_from_repo(baseurl,
666 proxies,
667 cachedir,
668 reponame,
669 "repodata/repomd.xml.key")
670 except CreatorError:
671 repokey = None
672 msger.debug("\ncan't get %s/%s" % (baseurl, "repodata/repomd.xml.key"))
673
674 my_repo_metadata.append({"name":reponame,
675 "baseurl":baseurl,
676 "repomd":repomd,
677 "primary":filepaths['primary'],
678 "cachedir":cachedir,
679 "proxies":proxies,
680 "patterns":filepaths['patterns'],
681 "comps":filepaths['comps'],
682 "repokey":repokey})
683
684 return my_repo_metadata
685
686def get_rpmver_in_repo(repometadata):
687 for repo in repometadata:
688 if repo["primary"].endswith(".xml"):
689 root = xmlparse(repo["primary"])
690 ns = root.getroot().tag
691 ns = ns[0:ns.rindex("}")+1]
692
693 versionlist = []
694 for elm in root.getiterator("%spackage" % ns):
695 if elm.find("%sname" % ns).text == 'rpm':
696 for node in elm.getchildren():
697 if node.tag == "%sversion" % ns:
698 versionlist.append(node.attrib['ver'])
699
700 if versionlist:
701 return reversed(
702 sorted(
703 versionlist,
704 key = lambda ver: map(int, ver.split('.')))).next()
705
706 elif repo["primary"].endswith(".sqlite"):
707 con = sqlite.connect(repo["primary"])
708 for row in con.execute("select version from packages where "
709 "name=\"rpm\" ORDER by version DESC"):
710 con.close()
711 return row[0]
712
713 return None
714
715def get_arch(repometadata):
716 archlist = []
717 for repo in repometadata:
718 if repo["primary"].endswith(".xml"):
719 root = xmlparse(repo["primary"])
720 ns = root.getroot().tag
721 ns = ns[0:ns.rindex("}")+1]
722 for elm in root.getiterator("%spackage" % ns):
723 if elm.find("%sarch" % ns).text not in ("noarch", "src"):
724 arch = elm.find("%sarch" % ns).text
725 if arch not in archlist:
726 archlist.append(arch)
727 elif repo["primary"].endswith(".sqlite"):
728 con = sqlite.connect(repo["primary"])
729 for row in con.execute("select arch from packages where arch not in (\"src\", \"noarch\")"):
730 if row[0] not in archlist:
731 archlist.append(row[0])
732
733 con.close()
734
735 uniq_arch = []
736 for i in range(len(archlist)):
737 if archlist[i] not in rpmmisc.archPolicies.keys():
738 continue
739 need_append = True
740 j = 0
741 while j < len(uniq_arch):
742 if archlist[i] in rpmmisc.archPolicies[uniq_arch[j]].split(':'):
743 need_append = False
744 break
745 if uniq_arch[j] in rpmmisc.archPolicies[archlist[i]].split(':'):
746 if need_append:
747 uniq_arch[j] = archlist[i]
748 need_append = False
749 else:
750 uniq_arch.remove(uniq_arch[j])
751 continue
752 j += 1
753 if need_append:
754 uniq_arch.append(archlist[i])
755
756 return uniq_arch, archlist
757
758def get_package(pkg, repometadata, arch = None):
759 ver = ""
760 target_repo = None
761 if not arch:
762 arches = []
763 elif arch not in rpmmisc.archPolicies:
764 arches = [arch]
765 else:
766 arches = rpmmisc.archPolicies[arch].split(':')
767 arches.append('noarch')
768
769 for repo in repometadata:
770 if repo["primary"].endswith(".xml"):
771 root = xmlparse(repo["primary"])
772 ns = root.getroot().tag
773 ns = ns[0:ns.rindex("}")+1]
774 for elm in root.getiterator("%spackage" % ns):
775 if elm.find("%sname" % ns).text == pkg:
776 if elm.find("%sarch" % ns).text in arches:
777 version = elm.find("%sversion" % ns)
778 tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
779 if tmpver > ver:
780 ver = tmpver
781 location = elm.find("%slocation" % ns)
782 pkgpath = "%s" % location.attrib['href']
783 target_repo = repo
784 break
785 if repo["primary"].endswith(".sqlite"):
786 con = sqlite.connect(repo["primary"])
787 if arch:
788 sql = 'select version, release, location_href from packages ' \
789 'where name = "%s" and arch IN ("%s")' % \
790 (pkg, '","'.join(arches))
791 for row in con.execute(sql):
792 tmpver = "%s-%s" % (row[0], row[1])
793 if tmpver > ver:
794 ver = tmpver
795 pkgpath = "%s" % row[2]
796 target_repo = repo
797 break
798 else:
799 sql = 'select version, release, location_href from packages ' \
800 'where name = "%s"' % pkg
801 for row in con.execute(sql):
802 tmpver = "%s-%s" % (row[0], row[1])
803 if tmpver > ver:
804 ver = tmpver
805 pkgpath = "%s" % row[2]
806 target_repo = repo
807 break
808 con.close()
809 if target_repo:
810 makedirs("%s/packages/%s" % (target_repo["cachedir"], target_repo["name"]))
811 url = os.path.join(target_repo["baseurl"], pkgpath)
812 filename = str("%s/packages/%s/%s" % (target_repo["cachedir"], target_repo["name"], os.path.basename(pkgpath)))
813 if os.path.exists(filename):
814 ret = rpmmisc.checkRpmIntegrity('rpm', filename)
815 if ret == 0:
816 return filename
817
818 msger.warning("package %s is damaged: %s" %
819 (os.path.basename(filename), filename))
820 os.unlink(filename)
821
822 pkg = myurlgrab(str(url), filename, target_repo["proxies"])
823 return pkg
824 else:
825 return None
826
827def get_source_name(pkg, repometadata):
828
829 def get_bin_name(pkg):
830 m = RPM_RE.match(pkg)
831 if m:
832 return m.group(1)
833 return None
834
835 def get_src_name(srpm):
836 m = SRPM_RE.match(srpm)
837 if m:
838 return m.group(1)
839 return None
840
841 ver = ""
842 target_repo = None
843
844 pkg_name = get_bin_name(pkg)
845 if not pkg_name:
846 return None
847
848 for repo in repometadata:
849 if repo["primary"].endswith(".xml"):
850 root = xmlparse(repo["primary"])
851 ns = root.getroot().tag
852 ns = ns[0:ns.rindex("}")+1]
853 for elm in root.getiterator("%spackage" % ns):
854 if elm.find("%sname" % ns).text == pkg_name:
855 if elm.find("%sarch" % ns).text != "src":
856 version = elm.find("%sversion" % ns)
857 tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
858 if tmpver > ver:
859 ver = tmpver
860 fmt = elm.find("%sformat" % ns)
861 if fmt:
862 fns = fmt.getchildren()[0].tag
863 fns = fns[0:fns.rindex("}")+1]
864 pkgpath = fmt.find("%ssourcerpm" % fns).text
865 target_repo = repo
866 break
867
868 if repo["primary"].endswith(".sqlite"):
869 con = sqlite.connect(repo["primary"])
870 for row in con.execute("select version, release, rpm_sourcerpm from packages where name = \"%s\" and arch != \"src\"" % pkg_name):
871 tmpver = "%s-%s" % (row[0], row[1])
872 if tmpver > ver:
873 pkgpath = "%s" % row[2]
874 target_repo = repo
875 break
876 con.close()
877 if target_repo:
878 return get_src_name(pkgpath)
879 else:
880 return None
881
882def get_pkglist_in_patterns(group, patterns):
883 found = False
884 pkglist = []
885 try:
886 root = xmlparse(patterns)
887 except SyntaxError:
888 raise SyntaxError("%s syntax error." % patterns)
889
890 for elm in list(root.getroot()):
891 ns = elm.tag
892 ns = ns[0:ns.rindex("}")+1]
893 name = elm.find("%sname" % ns)
894 summary = elm.find("%ssummary" % ns)
895 if name.text == group or summary.text == group:
896 found = True
897 break
898
899 if not found:
900 return pkglist
901
902 found = False
903 for requires in list(elm):
904 if requires.tag.endswith("requires"):
905 found = True
906 break
907
908 if not found:
909 return pkglist
910
911 for pkg in list(requires):
912 pkgname = pkg.attrib["name"]
913 if pkgname not in pkglist:
914 pkglist.append(pkgname)
915
916 return pkglist
917
918def get_pkglist_in_comps(group, comps):
919 found = False
920 pkglist = []
921 try:
922 root = xmlparse(comps)
923 except SyntaxError:
924 raise SyntaxError("%s syntax error." % comps)
925
926 for elm in root.getiterator("group"):
927 id = elm.find("id")
928 name = elm.find("name")
929 if id.text == group or name.text == group:
930 packagelist = elm.find("packagelist")
931 found = True
932 break
933
934 if not found:
935 return pkglist
936
937 for require in elm.getiterator("packagereq"):
938 if require.tag.endswith("packagereq"):
939 pkgname = require.text
940 if pkgname not in pkglist:
941 pkglist.append(pkgname)
942
943 return pkglist
944
945def is_statically_linked(binary):
946 return ", statically linked, " in runner.outs(['file', binary])
947
948def setup_qemu_emulator(rootdir, arch):
949 # mount binfmt_misc if it doesn't exist
950 if not os.path.exists("/proc/sys/fs/binfmt_misc"):
951 modprobecmd = find_binary_path("modprobe")
952 runner.show([modprobecmd, "binfmt_misc"])
953 if not os.path.exists("/proc/sys/fs/binfmt_misc/register"):
954 mountcmd = find_binary_path("mount")
955 runner.show([mountcmd, "-t", "binfmt_misc", "none", "/proc/sys/fs/binfmt_misc"])
956
957 # qemu_emulator is a special case, we can't use find_binary_path
958 # qemu emulator should be a statically-linked executable file
959 qemu_emulator = "/usr/bin/qemu-arm"
960 if not os.path.exists(qemu_emulator) or not is_statically_linked(qemu_emulator):
961 qemu_emulator = "/usr/bin/qemu-arm-static"
962 if not os.path.exists(qemu_emulator):
963 raise CreatorError("Please install a statically-linked qemu-arm")
964
965 # qemu emulator version check
966 armv7_list = [arch for arch in rpmmisc.archPolicies.keys() if arch.startswith('armv7')]
967 if arch in armv7_list: # need qemu (>=0.13.0)
968 qemuout = runner.outs([qemu_emulator, "-h"])
969 m = re.search("version\s*([.\d]+)", qemuout)
970 if m:
971 qemu_version = m.group(1)
972 if qemu_version < "0.13":
973 raise CreatorError("Requires %s version >=0.13 for %s" % (qemu_emulator, arch))
974 else:
975 msger.warning("Can't get version info of %s, please make sure it's higher than 0.13.0" % qemu_emulator)
976
977 if not os.path.exists(rootdir + "/usr/bin"):
978 makedirs(rootdir + "/usr/bin")
979 shutil.copy(qemu_emulator, rootdir + "/usr/bin/qemu-arm-static")
980 qemu_emulator = "/usr/bin/qemu-arm-static"
981
982 # disable selinux, selinux will block qemu emulator to run
983 if os.path.exists("/usr/sbin/setenforce"):
984 msger.info('Try to disable selinux')
985 runner.show(["/usr/sbin/setenforce", "0"])
986
987 # unregister it if it has been registered and is a dynamically-linked executable
988 node = "/proc/sys/fs/binfmt_misc/arm"
989 if os.path.exists(node):
990 qemu_unregister_string = "-1\n"
991 fd = open("/proc/sys/fs/binfmt_misc/arm", "w")
992 fd.write(qemu_unregister_string)
993 fd.close()
994
995 # register qemu emulator for interpreting other arch executable file
996 if not os.path.exists(node):
997 qemu_arm_string = ":arm:M::\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfa\\xff\\xff\\xff:%s:\n" % qemu_emulator
998 fd = open("/proc/sys/fs/binfmt_misc/register", "w")
999 fd.write(qemu_arm_string)
1000 fd.close()
1001
1002 return qemu_emulator
1003
1004def SrcpkgsDownload(pkgs, repometadata, instroot, cachedir):
1005 def get_source_repometadata(repometadata):
1006 src_repometadata=[]
1007 for repo in repometadata:
1008 if repo["name"].endswith("-source"):
1009 src_repometadata.append(repo)
1010 if src_repometadata:
1011 return src_repometadata
1012 return None
1013
1014 def get_src_name(srpm):
1015 m = SRPM_RE.match(srpm)
1016 if m:
1017 return m.group(1)
1018 return None
1019
1020 src_repometadata = get_source_repometadata(repometadata)
1021
1022 if not src_repometadata:
1023 msger.warning("No source repo found")
1024 return None
1025
1026 src_pkgs = []
1027 lpkgs_dict = {}
1028 lpkgs_path = []
1029 for repo in src_repometadata:
1030 cachepath = "%s/%s/packages/*.src.rpm" %(cachedir, repo["name"])
1031 lpkgs_path += glob.glob(cachepath)
1032
1033 for lpkg in lpkgs_path:
1034 lpkg_name = get_src_name(os.path.basename(lpkg))
1035 lpkgs_dict[lpkg_name] = lpkg
1036 localpkgs = lpkgs_dict.keys()
1037
1038 cached_count = 0
1039 destdir = instroot+'/usr/src/SRPMS'
1040 if not os.path.exists(destdir):
1041 os.makedirs(destdir)
1042
1043 srcpkgset = set()
1044 for _pkg in pkgs:
1045 srcpkg_name = get_source_name(_pkg, repometadata)
1046 if not srcpkg_name:
1047 continue
1048 srcpkgset.add(srcpkg_name)
1049
1050 for pkg in list(srcpkgset):
1051 if pkg in localpkgs:
1052 cached_count += 1
1053 shutil.copy(lpkgs_dict[pkg], destdir)
1054 src_pkgs.append(os.path.basename(lpkgs_dict[pkg]))
1055 else:
1056 src_pkg = get_package(pkg, src_repometadata, 'src')
1057 if src_pkg:
1058 shutil.copy(src_pkg, destdir)
1059 src_pkgs.append(src_pkg)
1060 msger.info("%d source packages gotten from cache" % cached_count)
1061
1062 return src_pkgs
1063
1064def strip_end(text, suffix):
1065 if not text.endswith(suffix):
1066 return text
1067 return text[:-len(suffix)]