testimage.bbclass 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. # Copyright (C) 2013 Intel Corporation
  2. #
  3. # Released under the MIT license (see COPYING.MIT)
  4. # testimage.bbclass enables testing of qemu images using python unittests.
  5. # Most of the tests are commands run on target image over ssh.
  6. # To use it add testimage to global inherit and call your target image with -c testimage
  7. # You can try it out like this:
  8. # - first build a qemu core-image-sato
  9. # - add IMAGE_CLASSES += "testimage" in local.conf
  10. # - then bitbake core-image-sato -c testimage. That will run a standard suite of tests.
  11. # You can set (or append to) TEST_SUITES in local.conf to select the tests
  12. # which you want to run for your target.
  13. # The test names are the module names in meta/lib/oeqa/runtime.
  14. # Each name in TEST_SUITES represents a required test for the image. (no skipping allowed)
  15. # Appending "auto" means that it will try to run all tests that are suitable for the image (each test decides that on it's own).
  16. # Note that order in TEST_SUITES is relevant: tests are run in an order such that
  17. # tests mentioned in @skipUnlessPassed run before the tests that depend on them,
  18. # but without such dependencies, tests run in the order in which they are listed
  19. # in TEST_SUITES.
  20. #
  21. # A layer can add its own tests in lib/oeqa/runtime, provided it extends BBPATH as normal in its layer.conf.
  22. # TEST_LOG_DIR contains a command ssh log and may contain infromation about what command is running, output and return codes and for qemu a boot log till login.
  23. # Booting is handled by this class, and it's not a test in itself.
  24. # TEST_QEMUBOOT_TIMEOUT can be used to set the maximum time in seconds the launch code will wait for the login prompt.
  25. TEST_LOG_DIR ?= "${WORKDIR}/testimage"
  26. TEST_EXPORT_DIR ?= "${TMPDIR}/testimage/${PN}"
  27. TEST_INSTALL_TMP_DIR ?= "${WORKDIR}/testimage/install_tmp"
  28. TEST_NEEDED_PACKAGES_DIR ?= "${WORKDIR}/testimage/packages"
  29. TEST_EXTRACTED_DIR ?= "${TEST_NEEDED_PACKAGES_DIR}/extracted"
  30. TEST_PACKAGED_DIR ?= "${TEST_NEEDED_PACKAGES_DIR}/packaged"
  31. RPMTESTSUITE = "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'smart rpm', '', d)}"
  32. SYSTEMDSUITE = "${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)}"
  33. MINTESTSUITE = "ping"
  34. NETTESTSUITE = "${MINTESTSUITE} ssh df date scp oe_syslog ${SYSTEMDSUITE}"
  35. DEVTESTSUITE = "gcc kernelmodule ldd"
  36. DEFAULT_TEST_SUITES = "${MINTESTSUITE} auto"
  37. DEFAULT_TEST_SUITES_pn-core-image-minimal = "${MINTESTSUITE}"
  38. DEFAULT_TEST_SUITES_pn-core-image-minimal-dev = "${MINTESTSUITE}"
  39. DEFAULT_TEST_SUITES_pn-core-image-full-cmdline = "${NETTESTSUITE} perl python logrotate"
  40. DEFAULT_TEST_SUITES_pn-core-image-x11 = "${MINTESTSUITE}"
  41. DEFAULT_TEST_SUITES_pn-core-image-lsb = "${NETTESTSUITE} pam parselogs ${RPMTESTSUITE}"
  42. DEFAULT_TEST_SUITES_pn-core-image-sato = "${NETTESTSUITE} connman xorg parselogs ${RPMTESTSUITE} \
  43. ${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'python', '', d)}"
  44. DEFAULT_TEST_SUITES_pn-core-image-sato-sdk = "${NETTESTSUITE} buildcvs buildiptables buildgalculator \
  45. connman ${DEVTESTSUITE} logrotate perl parselogs python ${RPMTESTSUITE} xorg"
  46. DEFAULT_TEST_SUITES_pn-core-image-lsb-dev = "${NETTESTSUITE} pam perl python parselogs ${RPMTESTSUITE}"
  47. DEFAULT_TEST_SUITES_pn-core-image-lsb-sdk = "${NETTESTSUITE} buildcvs buildiptables buildgalculator \
  48. connman ${DEVTESTSUITE} logrotate pam parselogs perl python ${RPMTESTSUITE}"
  49. DEFAULT_TEST_SUITES_pn-meta-toolchain = "auto"
  50. # aarch64 has no graphics
  51. DEFAULT_TEST_SUITES_remove_aarch64 = "xorg"
  52. # qemumips is quite slow and has reached the timeout limit several times on the YP build cluster,
  53. # mitigate this by removing build tests for qemumips machines.
  54. MIPSREMOVE ??= "buildcvs buildiptables buildgalculator"
  55. DEFAULT_TEST_SUITES_remove_qemumips = "${MIPSREMOVE}"
  56. DEFAULT_TEST_SUITES_remove_qemumips64 = "${MIPSREMOVE}"
  57. TEST_SUITES ?= "${DEFAULT_TEST_SUITES}"
  58. TEST_QEMUBOOT_TIMEOUT ?= "1000"
  59. TEST_TARGET ?= "qemu"
  60. TESTIMAGEDEPENDS = ""
  61. TESTIMAGEDEPENDS_qemuall = "qemu-native:do_populate_sysroot qemu-helper-native:do_populate_sysroot"
  62. TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'cpio-native:do_populate_sysroot', '', d)}"
  63. TESTIMAGEDEPENDS_qemuall += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'cpio-native:do_populate_sysroot', '', d)}"
  64. TESTIMAGEDEPENDS_qemuall += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'createrepo-native:do_populate_sysroot', '', d)}"
  65. TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'python-smartpm-native:do_populate_sysroot', '', d)}"
  66. TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'ipk', 'opkg-utils-native:do_populate_sysroot', '', d)}"
  67. TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'deb', 'apt-native:do_populate_sysroot', '', d)}"
  68. TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'python-smartpm-native:do_populate_sysroot', '', d)}"
  69. TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'createrepo-native:do_populate_sysroot', '', d)}"
  70. TESTIMAGELOCK = "${TMPDIR}/testimage.lock"
  71. TESTIMAGELOCK_qemuall = ""
  72. TESTIMAGE_DUMP_DIR ?= "/tmp/oe-saved-tests/"
  73. TESTIMAGE_UPDATE_VARS ?= "DL_DIR WORKDIR DEPLOY_DIR"
  74. testimage_dump_target () {
  75. top -bn1
  76. ps
  77. free
  78. df
  79. # The next command will export the default gateway IP
  80. export DEFAULT_GATEWAY=$(ip route | awk '/default/ { print $3}')
  81. ping -c3 $DEFAULT_GATEWAY
  82. dmesg
  83. netstat -an
  84. ip address
  85. # Next command will dump logs from /var/log/
  86. find /var/log/ -type f 2>/dev/null -exec echo "====================" \; -exec echo {} \; -exec echo "====================" \; -exec cat {} \; -exec echo "" \;
  87. }
  88. testimage_dump_host () {
  89. top -bn1
  90. iostat -x -z -N -d -p ALL 20 2
  91. ps -ef
  92. free
  93. df
  94. memstat
  95. dmesg
  96. ip -s link
  97. netstat -an
  98. }
  99. python do_testimage() {
  100. testimage_sanity(d)
  101. if (d.getVar('IMAGE_PKGTYPE') == 'rpm'
  102. and 'smart' in d.getVar('TEST_SUITES')):
  103. create_rpm_index(d)
  104. testimage_main(d)
  105. }
  106. addtask testimage
  107. do_testimage[nostamp] = "1"
  108. do_testimage[depends] += "${TESTIMAGEDEPENDS}"
  109. do_testimage[lockfiles] += "${TESTIMAGELOCK}"
  110. def testimage_sanity(d):
  111. if (d.getVar('TEST_TARGET') == 'simpleremote'
  112. and (not d.getVar('TEST_TARGET_IP')
  113. or not d.getVar('TEST_SERVER_IP'))):
  114. bb.fatal('When TEST_TARGET is set to "simpleremote" '
  115. 'TEST_TARGET_IP and TEST_SERVER_IP are needed too.')
  116. def testimage_main(d):
  117. import os
  118. import json
  119. import signal
  120. import logging
  121. from bb.utils import export_proxies
  122. from oeqa.core.utils.misc import updateTestData
  123. from oeqa.runtime.context import OERuntimeTestContext
  124. from oeqa.runtime.context import OERuntimeTestContextExecutor
  125. from oeqa.core.target.qemu import supported_fstypes
  126. from oeqa.core.utils.test import getSuiteCases
  127. from oeqa.utils import make_logger_bitbake_compatible
  128. def sigterm_exception(signum, stackframe):
  129. """
  130. Catch SIGTERM from worker in order to stop qemu.
  131. """
  132. raise RuntimeError
  133. logger = make_logger_bitbake_compatible(logging.getLogger("BitBake"))
  134. pn = d.getVar("PN")
  135. bb.utils.mkdirhier(d.getVar("TEST_LOG_DIR"))
  136. image_name = ("%s/%s" % (d.getVar('DEPLOY_DIR_IMAGE'),
  137. d.getVar('IMAGE_LINK_NAME')))
  138. tdname = "%s.testdata.json" % image_name
  139. td = json.load(open(tdname, "r"))
  140. # Some variables need to be updates (mostly paths) with the
  141. # ones of the current environment because some tests require them.
  142. updateTestData(d, td, d.getVar('TESTIMAGE_UPDATE_VARS').split())
  143. image_manifest = "%s.manifest" % image_name
  144. image_packages = OERuntimeTestContextExecutor.readPackagesManifest(image_manifest)
  145. extract_dir = d.getVar("TEST_EXTRACTED_DIR")
  146. # Get machine
  147. machine = d.getVar("MACHINE")
  148. # Get rootfs
  149. fstypes = [fs for fs in d.getVar('IMAGE_FSTYPES').split(' ')
  150. if fs in supported_fstypes]
  151. if not fstypes:
  152. bb.fatal('Unsupported image type built. Add a comptible image to '
  153. 'IMAGE_FSTYPES. Supported types: %s' %
  154. ', '.join(supported_fstypes))
  155. rootfs = '%s.%s' % (image_name, fstypes[0])
  156. # Get tmpdir (not really used, just for compatibility)
  157. tmpdir = d.getVar("TMPDIR")
  158. # Get deploy_dir_image (not really used, just for compatibility)
  159. dir_image = d.getVar("DEPLOY_DIR_IMAGE")
  160. # Get bootlog
  161. bootlog = os.path.join(d.getVar("TEST_LOG_DIR"),
  162. 'qemu_boot_log.%s' % d.getVar('DATETIME'))
  163. # Get display
  164. display = d.getVar("BB_ORIGENV").getVar("DISPLAY")
  165. # Get kernel
  166. kernel_name = ('%s-%s.bin' % (d.getVar("KERNEL_IMAGETYPE"), machine))
  167. kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), kernel_name)
  168. # Get boottime
  169. boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT"))
  170. # Get use_kvm
  171. qemu_use_kvm = d.getVar("QEMU_USE_KVM")
  172. if qemu_use_kvm and qemu_use_kvm == 'True' and 'x86' in machine:
  173. kvm = True
  174. else:
  175. kvm = False
  176. # TODO: We use the current implementatin of qemu runner because of
  177. # time constrains, qemu runner really needs a refactor too.
  178. target_kwargs = { 'machine' : machine,
  179. 'rootfs' : rootfs,
  180. 'tmpdir' : tmpdir,
  181. 'dir_image' : dir_image,
  182. 'display' : display,
  183. 'kernel' : kernel,
  184. 'boottime' : boottime,
  185. 'bootlog' : bootlog,
  186. 'kvm' : kvm,
  187. }
  188. # TODO: Currently BBPATH is needed for custom loading of targets.
  189. # It would be better to find these modules using instrospection.
  190. target_kwargs['target_modules_path'] = d.getVar('BBPATH')
  191. # runtime use network for download projects for build
  192. export_proxies(d)
  193. # we need the host dumper in test context
  194. host_dumper = OERuntimeTestContextExecutor.getHostDumper(
  195. d.getVar("testimage_dump_host"),
  196. d.getVar("TESTIMAGE_DUMP_DIR"))
  197. # the robot dance
  198. target = OERuntimeTestContextExecutor.getTarget(
  199. d.getVar("TEST_TARGET"), None, d.getVar("TEST_TARGET_IP"),
  200. d.getVar("TEST_SERVER_IP"), **target_kwargs)
  201. # test context
  202. tc = OERuntimeTestContext(td, logger, target, host_dumper,
  203. image_packages, extract_dir)
  204. # Load tests before starting the target
  205. test_paths = get_runtime_paths(d)
  206. test_modules = d.getVar('TEST_SUITES')
  207. tc.loadTests(test_paths, modules=test_modules)
  208. if not getSuiteCases(tc.suites):
  209. bb.fatal('Empty test suite, please verify TEST_SUITES variable')
  210. package_extraction(d, tc.suites)
  211. bootparams = None
  212. if d.getVar('VIRTUAL-RUNTIME_init_manager', '') == 'systemd':
  213. bootparams = 'systemd.log_level=debug systemd.log_target=console'
  214. results = None
  215. orig_sigterm_handler = signal.signal(signal.SIGTERM, sigterm_exception)
  216. try:
  217. # We need to check if runqemu ends unexpectedly
  218. # or if the worker send us a SIGTERM
  219. tc.target.start(extra_bootparams=bootparams)
  220. results = tc.runTests()
  221. except (RuntimeError, BlockingIOError) as err:
  222. if isinstance(err, RuntimeError):
  223. bb.error('testimage received SIGTERM, shutting down...')
  224. else:
  225. bb.error('runqemu failed, shutting down...')
  226. if results:
  227. results.stop()
  228. results = None
  229. finally:
  230. signal.signal(signal.SIGTERM, orig_sigterm_handler)
  231. tc.target.stop()
  232. # Show results (if we have them)
  233. if not results:
  234. bb.fatal('%s - FAILED - tests were interrupted during execution' % pn)
  235. tc.logSummary(results, pn)
  236. tc.logDetails()
  237. if not results.wasSuccessful():
  238. bb.fatal('%s - FAILED - check the task log and the ssh log' % pn)
  239. def get_runtime_paths(d):
  240. """
  241. Returns a list of paths where runtime test must reside.
  242. Runtime tests are expected in <LAYER_DIR>/lib/oeqa/runtime/cases/
  243. """
  244. paths = []
  245. for layer in d.getVar('BBLAYERS').split():
  246. path = os.path.join(layer, 'lib/oeqa/runtime/cases')
  247. if os.path.isdir(path):
  248. paths.append(path)
  249. return paths
  250. def create_index(arg):
  251. import subprocess
  252. index_cmd = arg
  253. try:
  254. bb.note("Executing '%s' ..." % index_cmd)
  255. result = subprocess.check_output(index_cmd,
  256. stderr=subprocess.STDOUT,
  257. shell=True)
  258. result = result.decode('utf-8')
  259. except subprocess.CalledProcessError as e:
  260. return("Index creation command '%s' failed with return code "
  261. '%d:\n%s' % (e.cmd, e.returncode, e.output.decode("utf-8")))
  262. if result:
  263. bb.note(result)
  264. return None
  265. def create_rpm_index(d):
  266. # Index RPMs
  267. rpm_createrepo = bb.utils.which(os.getenv('PATH'), "createrepo")
  268. index_cmds = []
  269. archs = (d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or '').replace('-', '_')
  270. for arch in archs.split():
  271. rpm_dir = os.path.join(d.getVar('DEPLOY_DIR_RPM'), arch)
  272. idx_path = os.path.join(d.getVar('WORKDIR'), 'rpm', arch)
  273. db_path = os.path.join(d.getVar('WORKDIR'), 'rpmdb', arch)
  274. if not os.path.isdir(rpm_dir):
  275. continue
  276. if os.path.exists(db_path):
  277. bb.utils.remove(dbpath, True)
  278. lockfilename = os.path.join(d.getVar('DEPLOY_DIR_RPM'), 'rpm.lock')
  279. lf = bb.utils.lockfile(lockfilename, False)
  280. oe.path.copyhardlinktree(rpm_dir, idx_path)
  281. # Full indexes overload a 256MB image so reduce the number of rpms
  282. # in the feed. Filter to p* since we use the psplash packages and
  283. # this leaves some allarch and machine arch packages too.
  284. bb.utils.remove(idx_path + "*/[a-oq-z]*.rpm")
  285. bb.utils.unlockfile(lf)
  286. cmd = '%s --dbpath %s --update -q %s' % (rpm_createrepo,
  287. db_path, idx_path)
  288. # Create repodata
  289. result = create_index(cmd)
  290. if result:
  291. bb.fatal('%s' % ('\n'.join(result)))
  292. def package_extraction(d, test_suites):
  293. from oeqa.utils.package_manager import find_packages_to_extract
  294. from oeqa.utils.package_manager import extract_packages
  295. bb.utils.remove(d.getVar("TEST_NEEDED_PACKAGES_DIR"), recurse=True)
  296. packages = find_packages_to_extract(test_suites)
  297. if packages:
  298. bb.utils.mkdirhier(d.getVar("TEST_INSTALL_TMP_DIR"))
  299. bb.utils.mkdirhier(d.getVar("TEST_PACKAGED_DIR"))
  300. bb.utils.mkdirhier(d.getVar("TEST_EXTRACTED_DIR"))
  301. extract_packages(d, packages)
  302. testimage_main[vardepsexclude] += "BB_ORIGENV DATETIME"
  303. inherit testsdk