2 # misc.py : miscellaneous utilities
4 # Copyright 2010, 2011 Intel Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Library General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 import sqlite3 as sqlite
34 from xml.etree import cElementTree
37 xmlparse = cElementTree.parse
40 from fs_related import *
41 from proxy import get_proxy_for
45 def get_image_type(path):
47 def _get_extension_name(path):
48 match = re.search("(?<=\.)\w+$", path)
54 def _ismeego(rootdir):
55 if (os.path.exists(rootdir + "/etc/moblin-release") \
56 or os.path.exists(rootdir + "/etc/meego-release")) \
57 and os.path.exists(rootdir + "/etc/inittab") \
58 and os.path.exists(rootdir + "/etc/rc.sysinit") \
59 and glob.glob(rootdir + "/boot/vmlinuz-*"):
64 if os.path.isdir(path):
77 extension = _get_extension_name(path)
78 if extension in maptab:
79 return maptab[extension]
82 file_header = fd.read(1024)
84 vdi_flag = "<<< Sun VirtualBox Disk Image >>>"
85 if file_header[0:len(vdi_flag)] == vdi_flag:
88 dev_null = os.open("/dev/null", os.O_WRONLY)
89 filecmd = find_binary_path("file")
90 args = [ filecmd, path ]
91 file = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=dev_null)
92 output = file.communicate()[0]
94 isoptn = re.compile(r".*ISO 9660 CD-ROM filesystem.*(bootable).*")
95 usbimgptn = re.compile(r".*x86 boot sector.*active.*")
96 rawptn = re.compile(r".*x86 boot sector.*")
97 vmdkptn = re.compile(r".*VMware. disk image.*")
98 ext3fsimgptn = re.compile(r".*Linux.*ext3 filesystem data.*")
99 if isoptn.match(output):
101 elif usbimgptn.match(output):
102 return maptab["usbimg"]
103 elif rawptn.match(output):
105 elif vmdkptn.match(output):
106 return maptab["vmdk"]
107 elif ext3fsimgptn.match(output):
112 def get_file_size(file):
113 """Return size in MB unit"""
114 du = find_binary_path("du")
115 dev_null = os.open("/dev/null", os.O_WRONLY)
116 duProc = subprocess.Popen([du, "-s", "-b", "-B", "1M", file],
117 stdout=subprocess.PIPE, stderr=dev_null)
118 duOutput = duProc.communicate()[0]
119 if duProc.returncode:
120 raise CreatorError("Failed to run %s" % du)
122 size1 = int(duOutput.split()[0])
123 duProc = subprocess.Popen([du, "-s", "-B", "1M", file],
124 stdout=subprocess.PIPE, stderr=dev_null)
125 duOutput = duProc.communicate()[0]
126 if duProc.returncode:
127 raise CreatorError("Failed to run %s" % du)
129 size2 = int(duOutput.split()[0])
136 def get_filesystem_avail(fs):
137 vfstat = os.statvfs(fs)
138 return vfstat.f_bavail * vfstat.f_bsize
140 def convert_image(srcimg, srcfmt, dstimg, dstfmt):
143 raise CreatorError("Invalid destination image format: %s" % dstfmt)
144 msger.debug("converting %s image to %s" % (srcimg, dstimg))
146 path = find_binary_path("qemu-img")
147 argv = [path, "convert", "-f", "vmdk", srcimg, "-O", dstfmt, dstimg]
148 elif srcfmt == "vdi":
149 path = find_binary_path("VBoxManage")
150 argv = [path, "internalcommands", "converttoraw", srcimg, dstimg]
152 raise CreatorError("Invalid soure image format: %s" % srcfmt)
154 rc = subprocess.call(argv)
156 msger.debug("convert successful")
158 raise CreatorError("Unable to convert disk to %s" % dstfmt)
160 def uncompress_squashfs(squashfsimg, outdir):
161 """Uncompress file system from squshfs image"""
162 unsquashfs = find_binary_path("unsquashfs")
163 args = [ unsquashfs, "-d", outdir, squashfsimg ]
164 rc = subprocess.call(args)
166 raise SquashfsError("Failed to uncompress %s." % squashfsimg)
168 def mkdtemp(dir = "/var/tmp", prefix = "mic-tmp-"):
170 return tempfile.mkdtemp(dir = dir, prefix = prefix)
172 def get_temp_reponame(baseurl):
173 md5obj = hashlib.md5(baseurl)
174 tmpreponame = "%s" % md5obj.hexdigest()
177 def get_repostrs_from_ks(ks):
179 for repodata in ks.handler.repo.repoList:
181 if hasattr(repodata, "name") and repodata.name:
182 repostr += ",name:" + repodata.name
183 if hasattr(repodata, "baseurl") and repodata.baseurl:
184 repostr += ",baseurl:" + repodata.baseurl
185 if hasattr(repodata, "mirrorlist") and repodata.mirrorlist:
186 repostr += ",mirrorlist:" + repodata.mirrorlist
187 if hasattr(repodata, "includepkgs") and repodata.includepkgs:
188 repostr += ",includepkgs:" + ";".join(repodata.includepkgs)
189 if hasattr(repodata, "excludepkgs") and repodata.excludepkgs:
190 repostr += ",excludepkgs:" + ";".join(repodata.excludepkgs)
191 if hasattr(repodata, "cost") and repodata.cost:
192 repostr += ",cost:%d" % repodata.cost
193 if hasattr(repodata, "save") and repodata.save:
195 if hasattr(repodata, "proxy") and repodata.proxy:
196 repostr += ",proxy:" + repodata.proxy
197 if hasattr(repodata, "proxyuser") and repodata.proxy_username:
198 repostr += ",proxyuser:" + repodata.proxy_username
199 if hasattr(repodata, "proxypasswd") and repodata.proxy_password:
200 repostr += ",proxypasswd:" + repodata.proxy_password
201 if repostr.find("name:") == -1:
202 repostr = ",name:%s" % get_temp_reponame(repodata.baseurl)
203 if hasattr(repodata, "debuginfo") and repodata.debuginfo:
204 repostr += ",debuginfo:"
205 if hasattr(repodata, "source") and repodata.source:
206 repostr += ",source:"
207 if hasattr(repodata, "gpgkey") and repodata.gpgkey:
208 repostr += ",gpgkey:" + repodata.gpgkey
209 kickstart_repos.append(repostr[1:])
210 return kickstart_repos
212 def get_uncompressed_data_from_url(url, filename, proxies):
213 filename = myurlgrab(url, filename, proxies)
215 if filename.endswith(".gz"):
217 gunzip = find_binary_path('gunzip')
218 subprocess.call([gunzip, "-f", filename])
219 elif filename.endswith(".bz2"):
221 bunzip2 = find_binary_path('bunzip2')
222 subprocess.call([bunzip2, "-f", filename])
224 filename = filename.replace(suffix, "")
227 def get_metadata_from_repo(baseurl, proxies, cachedir, reponame, filename):
228 url = str(baseurl + "/" + filename)
229 filename_tmp = str("%s/%s/%s" % (cachedir, reponame, os.path.basename(filename)))
230 return get_uncompressed_data_from_url(url,filename_tmp,proxies)
232 def get_metadata_from_repos(repostrs, cachedir):
233 my_repo_metadata = []
234 for repostr in repostrs:
238 items = repostr.split(",")
240 subitems = item.split(":")
241 if subitems[0] == "name":
242 reponame = subitems[1]
243 if subitems[0] == "baseurl":
245 if subitems[0] == "proxy":
247 if subitems[0] in ("http", "https", "ftp", "ftps", "file"):
250 proxy = get_proxy_for(baseurl)
253 proxies = {str(proxy.split(":")[0]):str(proxy)}
254 makedirs(cachedir + "/" + reponame)
255 url = str(baseurl + "/repodata/repomd.xml")
256 filename = str("%s/%s/repomd.xml" % (cachedir, reponame))
257 repomd = myurlgrab(url, filename, proxies)
259 root = xmlparse(repomd)
261 raise CreatorError("repomd.xml syntax error.")
263 ns = root.getroot().tag
264 ns = ns[0:ns.rindex("}")+1]
267 for elm in root.getiterator("%sdata" % ns):
268 if elm.attrib["type"] == "patterns":
269 patterns = elm.find("%slocation" % ns).attrib['href']
273 for elm in root.getiterator("%sdata" % ns):
274 if elm.attrib["type"] == "group_gz":
275 comps = elm.find("%slocation" % ns).attrib['href']
278 for elm in root.getiterator("%sdata" % ns):
279 if elm.attrib["type"] == "group":
280 comps = elm.find("%slocation" % ns).attrib['href']
284 for elm in root.getiterator("%sdata" % ns):
285 if elm.attrib["type"] == "primary_db":
286 primary_type=".sqlite"
290 for elm in root.getiterator("%sdata" % ns):
291 if elm.attrib["type"] == "primary":
298 primary = elm.find("%slocation" % ns).attrib['href']
299 primary = get_metadata_from_repo(baseurl, proxies, cachedir, reponame, primary)
302 patterns = get_metadata_from_repo(baseurl, proxies, cachedir, reponame, patterns)
305 comps = get_metadata_from_repo(baseurl, proxies, cachedir, reponame, comps)
309 repokey = get_metadata_from_repo(baseurl, proxies, cachedir, reponame, "repodata/repomd.xml.key")
312 msger.warning("can't get %s/%s" % (baseurl, "repodata/repomd.xml.key"))
314 my_repo_metadata.append({"name":reponame, "baseurl":baseurl, "repomd":repomd, "primary":primary, "cachedir":cachedir, "proxies":proxies, "patterns":patterns, "comps":comps, "repokey":repokey})
316 return my_repo_metadata
318 def get_package(pkg, repometadata, arch = None):
321 for repo in repometadata:
322 if repo["primary"].endswith(".xml"):
323 root = xmlparse(repo["primary"])
324 ns = root.getroot().tag
325 ns = ns[0:ns.rindex("}")+1]
326 for elm in root.getiterator("%spackage" % ns):
327 if elm.find("%sname" % ns).text == pkg:
328 if elm.find("%sarch" % ns).text != "src":
329 version = elm.find("%sversion" % ns)
330 tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
333 location = elm.find("%slocation" % ns)
334 pkgpath = "%s" % location.attrib['href']
337 if repo["primary"].endswith(".sqlite"):
338 con = sqlite.connect(repo["primary"])
340 for row in con.execute("select version, release,location_href from packages where name = \"%s\" and arch != \"src\"" % pkg):
341 tmpver = "%s-%s" % (row[0], row[1])
343 pkgpath = "%s" % row[2]
347 for row in con.execute("select version, release,location_href from packages where name = \"%s\"" % pkg):
348 tmpver = "%s-%s" % (row[0], row[1])
350 pkgpath = "%s" % row[2]
355 makedirs("%s/%s/packages" % (target_repo["cachedir"], target_repo["name"]))
356 url = str(target_repo["baseurl"] + "/" + pkgpath)
357 filename = str("%s/%s/packages/%s" % (target_repo["cachedir"], target_repo["name"], os.path.basename(pkgpath)))
358 pkg = myurlgrab(url, filename, target_repo["proxies"])
363 def get_source_name(pkg, repometadata):
365 def get_bin_name(pkg):
366 m = re.match("(.*)-(.*)-(.*)\.(.*)\.rpm", pkg)
371 def get_src_name(srpm):
372 m = re.match("(.*)-(\d+.*)-(\d+\.\d+).src.rpm", srpm)
380 pkg_name = get_bin_name(pkg)
384 for repo in repometadata:
385 if repo["primary"].endswith(".xml"):
386 root = xmlparse(repo["primary"])
387 ns = root.getroot().tag
388 ns = ns[0:ns.rindex("}")+1]
389 for elm in root.getiterator("%spackage" % ns):
390 if elm.find("%sname" % ns).text == pkg_name:
391 if elm.find("%sarch" % ns).text != "src":
392 version = elm.find("%sversion" % ns)
393 tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
396 fmt = elm.find("%sformat" % ns)
398 fns = fmt.getchildren()[0].tag
399 fns = fns[0:fns.rindex("}")+1]
400 pkgpath = fmt.find("%ssourcerpm" % fns).text
404 if repo["primary"].endswith(".sqlite"):
405 con = sqlite.connect(repo["primary"])
406 for row in con.execute("select version, release, rpm_sourcerpm from packages where name = \"%s\" and arch != \"src\"" % pkg_name):
407 tmpver = "%s-%s" % (row[0], row[1])
409 pkgpath = "%s" % row[2]
414 return get_src_name(pkgpath)
418 def get_release_no(repometadata, distro="meego"):
419 cpio = find_binary_path("cpio")
420 rpm2cpio = find_binary_path("rpm2cpio")
421 release_pkg = get_package("%s-release" % distro, repometadata)
426 p1 = subprocess.Popen([rpm2cpio, release_pkg], stdout = subprocess.PIPE)
427 p2 = subprocess.Popen([cpio, "-idv"], stdin = p1.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
429 f = open("%s/etc/%s-release" % (tmpdir, distro), "r")
433 shutil.rmtree(tmpdir, ignore_errors = True)
434 return content.split(" ")[2]
438 def get_kickstarts_from_repos(repometadata):
440 for repo in repometadata:
442 root = xmlparse(repo["repomd"])
444 raise CreatorError("repomd.xml syntax error.")
446 ns = root.getroot().tag
447 ns = ns[0:ns.rindex("}")+1]
449 for elm in root.getiterator("%sdata" % ns):
450 if elm.attrib["type"] == "image-config":
453 if elm.attrib["type"] != "image-config":
456 location = elm.find("%slocation" % ns)
457 image_config = str(repo["baseurl"] + "/" + location.attrib["href"])
458 filename = str("%s/%s/image-config.xml%s" % (repo["cachedir"], repo["name"], suffix))
460 image_config = get_uncompressed_data_from_url(image_config,filename,repo["proxies"])
463 root = xmlparse(image_config)
465 raise CreatorError("image-config.xml syntax error.")
467 for elm in root.getiterator("config"):
468 path = elm.find("path").text
469 path = path.replace("images-config", "image-config")
470 description = elm.find("description").text
471 makedirs(os.path.dirname("%s/%s/%s" % (repo["cachedir"], repo["name"], path)))
473 if "http" not in path:
474 url = str(repo["baseurl"] + "/" + path)
475 filename = str("%s/%s/%s" % (repo["cachedir"], repo["name"], path))
476 path = myurlgrab(url, filename, repo["proxies"])
477 kickstarts.append({"filename":path,"description":description})
480 def select_ks(ksfiles):
481 msger.info("Available kickstart files:")
485 msger.raw("\t%d. %s (%s)" % (i, ks["description"], os.path.basename(ks["filename"])))
488 choice = raw_input("Please input your choice and press ENTER. [1..%d] ? " % i)
489 if choice.lower() == "q":
493 if choice >= 1 and choice <= i:
496 return ksfiles[choice-1]["filename"]
498 def get_pkglist_in_patterns(group, patterns):
502 root = xmlparse(patterns)
504 raise SyntaxError("%s syntax error." % patterns)
506 for elm in list(root.getroot()):
508 ns = ns[0:ns.rindex("}")+1]
509 name = elm.find("%sname" % ns)
510 summary = elm.find("%ssummary" % ns)
511 if name.text == group or summary.text == group:
519 for requires in list(elm):
520 if requires.tag.endswith("requires"):
527 for pkg in list(requires):
528 pkgname = pkg.attrib["name"]
529 if pkgname not in pkglist:
530 pkglist.append(pkgname)
534 def get_pkglist_in_comps(group, comps):
538 root = xmlparse(comps)
540 raise SyntaxError("%s syntax error." % comps)
542 for elm in root.getiterator("group"):
544 name = elm.find("name")
545 if id.text == group or name.text == group:
546 packagelist = elm.find("packagelist")
553 for require in elm.getiterator("packagereq"):
554 if require.tag.endswith("packagereq"):
555 pkgname = require.text
556 if pkgname not in pkglist:
557 pkglist.append(pkgname)
561 def is_statically_linked(binary):
563 dev_null = os.open("/dev/null", os.O_WRONLY)
564 filecmd = find_binary_path("file")
565 args = [ filecmd, binary ]
566 file = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=dev_null)
567 output = file.communicate()[0]
569 if output.find(", statically linked, ") > 0:
573 def setup_qemu_emulator(rootdir, arch):
574 # mount binfmt_misc if it doesn't exist
575 if not os.path.exists("/proc/sys/fs/binfmt_misc"):
576 modprobecmd = find_binary_path("modprobe")
577 subprocess.call([modprobecmd, "binfmt_misc"])
578 if not os.path.exists("/proc/sys/fs/binfmt_misc/register"):
579 mountcmd = find_binary_path("mount")
580 subprocess.call([mountcmd, "-t", "binfmt_misc", "none", "/proc/sys/fs/binfmt_misc"])
582 # qemu_emulator is a special case, we can't use find_binary_path
583 # qemu emulator should be a statically-linked executable file
584 qemu_emulator = "/usr/bin/qemu-arm"
585 if not os.path.exists(qemu_emulator) or not is_statically_linked(qemu_emulator):
586 qemu_emulator = "/usr/bin/qemu-arm-static"
587 if not os.path.exists(qemu_emulator):
588 raise CreatorError("Please install a statically-linked qemu-arm")
589 if not os.path.exists(rootdir + "/usr/bin"):
590 makedirs(rootdir + "/usr/bin")
591 shutil.copy(qemu_emulator, rootdir + qemu_emulator)
593 # disable selinux, selinux will block qemu emulator to run
594 if os.path.exists("/usr/sbin/setenforce"):
595 subprocess.call(["/usr/sbin/setenforce", "0"])
597 node = "/proc/sys/fs/binfmt_misc/arm"
598 if is_statically_linked(qemu_emulator) and os.path.exists(node):
601 # unregister it if it has been registered and is a dynamically-linked executable
602 if not is_statically_linked(qemu_emulator) and os.path.exists(node):
603 qemu_unregister_string = "-1\n"
604 fd = open("/proc/sys/fs/binfmt_misc/arm", "w")
605 fd.write(qemu_unregister_string)
608 # register qemu emulator for interpreting other arch executable file
609 if not os.path.exists(node):
610 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
611 fd = open("/proc/sys/fs/binfmt_misc/register", "w")
612 fd.write(qemu_arm_string)
617 def create_release(config, destdir, name, outimages, release):
618 """ TODO: This functionality should really be in creator.py inside the
619 ImageCreator class. """
621 # For virtual machine images, we have a subdir for it, this is unnecessary
624 for i in range(len(outimages)):
626 if not os.path.isdir(file) and os.path.dirname(file) != destdir:
627 thatsubdir = os.path.dirname(file)
628 newfile = os.path.join(destdir, os.path.basename(file))
629 shutil.move(file, newfile)
630 outimages[i] = newfile
632 shutil.rmtree(thatsubdir, ignore_errors = True)
634 """ Create release directory and files """
635 os.system ("cp %s %s/%s.ks" % (config, destdir, name))
636 # When building a release we want to make sure the .ks
637 # file generates the same build even when --release= is not used.
638 fd = open(config, "r")
641 kscont = kscont.replace("@BUILD_ID@",release)
642 fd = open("%s/%s.ks" % (destdir,name), "w")
645 outimages.append("%s/%s.ks" % (destdir,name))
647 # Using system + mv, because of * in filename.
648 os.system ("mv %s/*-pkgs.txt %s/%s.packages" % (destdir, destdir, name))
649 outimages.append("%s/%s.packages" % (destdir,name))
651 d = os.listdir(destdir)
653 if f.endswith(".iso"):
654 ff = f.replace(".iso", ".img")
655 os.rename("%s/%s" %(destdir, f ), "%s/%s" %(destdir, ff))
656 outimages.append("%s/%s" %(destdir, ff))
657 elif f.endswith(".usbimg"):
658 ff = f.replace(".usbimg", ".img")
659 os.rename("%s/%s" %(destdir, f ), "%s/%s" %(destdir, ff))
660 outimages.append("%s/%s" %(destdir, ff))
662 fd = open(destdir + "/MANIFEST", "w")
663 d = os.listdir(destdir)
667 if os.path.exists("/usr/bin/md5sum"):
668 p = subprocess.Popen(["/usr/bin/md5sum", "-b", "%s/%s" %(destdir, f )],
669 stdout=subprocess.PIPE)
670 (md5sum, errorstr) = p.communicate()
671 if p.returncode != 0:
672 msger.warning("Can't generate md5sum for image %s/%s" %(destdir, f ))
674 md5sum = md5sum.split(" ")[0]
675 fd.write(md5sum+" "+f+"\n")
677 outimages.append("%s/MANIFEST" % destdir)
680 """ Update the file list. """
682 for file in outimages:
683 if os.path.exists("%s" % file):
684 updated_list.append(file)
688 def SrcpkgsDownload(pkgs, repometadata, instroot, cachedir):
690 def get_source_repometadata(repometadata):
692 for repo in repometadata:
693 if repo["name"].endswith("-source"):
694 src_repometadata.append(repo)
696 return src_repometadata
699 def get_src_name(srpm):
700 m = re.match("(.*)-(\d+.*)-(\d+\.\d+).src.rpm", srpm)
705 src_repometadata = get_source_repometadata(repometadata)
707 if not src_repometadata:
708 msger.warning("No source repo found")
714 for repo in src_repometadata:
715 cachepath = "%s/%s/packages/*.src.rpm" %(cachedir, repo["name"])
716 lpkgs_path += glob.glob(cachepath)
718 for lpkg in lpkgs_path:
719 lpkg_name = get_src_name(os.path.basename(lpkg))
720 lpkgs_dict[lpkg_name] = lpkg
721 localpkgs = lpkgs_dict.keys()
724 destdir = instroot+'/usr/src/SRPMS'
725 if not os.path.exists(destdir):
730 srcpkg_name = get_source_name(_pkg, repometadata)
733 srcpkgset.add(srcpkg_name)
735 for pkg in list(srcpkgset):
738 shutil.copy(lpkgs_dict[pkg], destdir)
739 src_pkgs.append(os.path.basename(lpkgs_dict[pkg]))
741 src_pkg = get_package(pkg, src_repometadata, 'src')
743 shutil.copy(src_pkg, destdir)
744 src_pkgs.append(src_pkg)
745 msger.info("%d source packages gotten from cache" %cached_count)