summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README.adoc2
-rw-r--r--classes/image_types_ostree.bbclass6
-rw-r--r--classes/image_types_ota.bbclass2
-rw-r--r--classes/sota_qemux86-64.bbclass4
-rw-r--r--conf/include/bblayers/sota.inc1
-rw-r--r--conf/include/bblayers/sota_qemux86-64.inc1
-rw-r--r--lib/oeqa/selftest/garage_push.py42
l---------lib/oeqa/selftest/qemucommand.py1
-rw-r--r--lib/oeqa/selftest/updater.py179
-rw-r--r--recipes-bsp/u-boot/u-boot_2016.11.bb22
-rw-r--r--recipes-sota/aktualizr/aktualizr-hsm-test-prov.bb2
-rw-r--r--recipes-sota/aktualizr/aktualizr_git.bb2
-rw-r--r--recipes-support/util-linux/util-linux_%.bbappend3
-rwxr-xr-xscripts/envsetup.sh10
-rw-r--r--scripts/lib/wic/plugins/source/otaimage.py2
-rw-r--r--scripts/qemucommand.py127
-rwxr-xr-xscripts/run-qemu-ota129
18 files changed, 331 insertions, 205 deletions
diff --git a/.gitignore b/.gitignore
index bee8a64..8d35cb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
1__pycache__ 1__pycache__
2*.pyc
diff --git a/README.adoc b/README.adoc
index 0097670..b4608d5 100644
--- a/README.adoc
+++ b/README.adoc
@@ -136,5 +136,5 @@ SANITY_TESTED_DISTROS=""
136* Run oe-selftest: 136* Run oe-selftest:
137 137
138``` 138```
139oe-selftest --run-tests garage_push 139oe-selftest --run-tests updater
140``` 140```
diff --git a/classes/image_types_ostree.bbclass b/classes/image_types_ostree.bbclass
index 172f2c8..e6bea76 100644
--- a/classes/image_types_ostree.bbclass
+++ b/classes/image_types_ostree.bbclass
@@ -2,7 +2,7 @@
2 2
3inherit image 3inherit image
4 4
5IMAGE_DEPENDS_ostree = "ostree-native:do_populate_sysroot \ 5do_image_ostree[depends] += "ostree-native:do_populate_sysroot \
6 openssl-native:do_populate_sysroot \ 6 openssl-native:do_populate_sysroot \
7 coreutils-native:do_populate_sysroot \ 7 coreutils-native:do_populate_sysroot \
8 unzip-native:do_populate_sysroot \ 8 unzip-native:do_populate_sysroot \
@@ -161,7 +161,7 @@ IMAGE_CMD_ostree () {
161} 161}
162 162
163IMAGE_TYPEDEP_ostreepush = "ostree" 163IMAGE_TYPEDEP_ostreepush = "ostree"
164IMAGE_DEPENDS_ostreepush = "aktualizr-native:do_populate_sysroot ca-certificates-native:do_populate_sysroot " 164do_image_ostreepush[depends] += "aktualizr-native:do_populate_sysroot ca-certificates-native:do_populate_sysroot"
165IMAGE_CMD_ostreepush () { 165IMAGE_CMD_ostreepush () {
166 # Print warnings if credetials are not set or if the file has not been found. 166 # Print warnings if credetials are not set or if the file has not been found.
167 if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then 167 if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
@@ -179,7 +179,7 @@ IMAGE_CMD_ostreepush () {
179} 179}
180 180
181IMAGE_TYPEDEP_garagesign = "ostreepush" 181IMAGE_TYPEDEP_garagesign = "ostreepush"
182IMAGE_DEPENDS_garagesign = "garage-sign-native:do_populate_sysroot" 182do_image_ostreepush[depends] += "garage-sign-native:do_populate_sysroot"
183IMAGE_CMD_garagesign () { 183IMAGE_CMD_garagesign () {
184 if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then 184 if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
185 # if credentials are issued by a server that doesn't support offline signing, exit silently 185 # if credentials are issued by a server that doesn't support offline signing, exit silently
diff --git a/classes/image_types_ota.bbclass b/classes/image_types_ota.bbclass
index 09c30ff..b15178a 100644
--- a/classes/image_types_ota.bbclass
+++ b/classes/image_types_ota.bbclass
@@ -11,7 +11,7 @@ inherit image
11 11
12OSTREE_BOOTLOADER ??= 'u-boot' 12OSTREE_BOOTLOADER ??= 'u-boot'
13 13
14IMAGE_DEPENDS_otaimg = "e2fsprogs-native:do_populate_sysroot \ 14do_image_otaimg[depends] += "e2fsprogs-native:do_populate_sysroot \
15 ${@'grub:do_populate_sysroot' if d.getVar('OSTREE_BOOTLOADER', True) == 'grub' else ''} \ 15 ${@'grub:do_populate_sysroot' if d.getVar('OSTREE_BOOTLOADER', True) == 'grub' else ''} \
16 ${@'virtual/bootloader:do_deploy' if d.getVar('OSTREE_BOOTLOADER', True) == 'u-boot' else ''}" 16 ${@'virtual/bootloader:do_deploy' if d.getVar('OSTREE_BOOTLOADER', True) == 'u-boot' else ''}"
17 17
diff --git a/classes/sota_qemux86-64.bbclass b/classes/sota_qemux86-64.bbclass
index 5ec4f69..666ad6b 100644
--- a/classes/sota_qemux86-64.bbclass
+++ b/classes/sota_qemux86-64.bbclass
@@ -4,8 +4,10 @@ PREFERRED_VERSION_linux-yocto_qemux86-64_sota = "4.4%"
4IMAGE_FSTYPES_remove = "wic" 4IMAGE_FSTYPES_remove = "wic"
5 5
6# U-Boot support for SOTA 6# U-Boot support for SOTA
7PREFERRED_PROVIDER_virtual/bootloader_sota = "u-boot-ota" 7PREFERRED_PROVIDER_virtual/bootloader_sota = "u-boot"
8UBOOT_MACHINE_sota = "qemu-x86_defconfig" 8UBOOT_MACHINE_sota = "qemu-x86_defconfig"
9OSTREE_BOOTLOADER ?= "u-boot" 9OSTREE_BOOTLOADER ?= "u-boot"
10 10
11OSTREE_KERNEL_ARGS ?= "ramdisk_size=16384 rw rootfstype=ext4 rootwait rootdelay=2 ostree_root=/dev/hda" 11OSTREE_KERNEL_ARGS ?= "ramdisk_size=16384 rw rootfstype=ext4 rootwait rootdelay=2 ostree_root=/dev/hda"
12
13IMAGE_ROOTFS_EXTRA_SPACE = "${@bb.utils.contains('DISTRO_FEATURES', 'sota', '65536', '', d)}"
diff --git a/conf/include/bblayers/sota.inc b/conf/include/bblayers/sota.inc
index 97edecb..b1fd28a 100644
--- a/conf/include/bblayers/sota.inc
+++ b/conf/include/bblayers/sota.inc
@@ -1,4 +1,3 @@
1
2BBLAYERS += "${METADIR}/meta-updater" 1BBLAYERS += "${METADIR}/meta-updater"
3BBLAYERS += "${METADIR}/meta-openembedded/meta-filesystems" 2BBLAYERS += "${METADIR}/meta-openembedded/meta-filesystems"
4BBLAYERS += "${METADIR}/meta-openembedded/meta-oe" 3BBLAYERS += "${METADIR}/meta-openembedded/meta-oe"
diff --git a/conf/include/bblayers/sota_qemux86-64.inc b/conf/include/bblayers/sota_qemux86-64.inc
index 22ace81..12d32ff 100644
--- a/conf/include/bblayers/sota_qemux86-64.inc
+++ b/conf/include/bblayers/sota_qemux86-64.inc
@@ -1,2 +1 @@
1
2BBLAYERS += " ${METADIR}/meta-updater-qemux86-64 " BBLAYERS += " ${METADIR}/meta-updater-qemux86-64 "
diff --git a/lib/oeqa/selftest/garage_push.py b/lib/oeqa/selftest/garage_push.py
deleted file mode 100644
index 21bd1c1..0000000
--- a/lib/oeqa/selftest/garage_push.py
+++ /dev/null
@@ -1,42 +0,0 @@
1import unittest
2import os
3import logging
4
5from oeqa.selftest.base import oeSelfTest
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var
7
8class GaragePushTests(oeSelfTest):
9
10 @classmethod
11 def setUpClass(cls):
12 # Ensure we have the right data in pkgdata
13 logger = logging.getLogger("selftest")
14 logger.info('Running bitbake to build aktualizr-native tools')
15 bitbake('aktualizr-native garage-sign-native')
16
17 def test_help(self):
18 image_dir = get_bb_var("D", "aktualizr-native")
19 bin_dir = get_bb_var("bindir", "aktualizr-native")
20 gp_path = os.path.join(image_dir, bin_dir[1:], 'garage-push')
21 result = runCmd('%s --help' % gp_path, ignore_status=True)
22 self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
23
24 def test_java(self):
25 result = runCmd('which java', ignore_status=True)
26 self.assertEqual(result.status, 0, "Java not found.")
27
28 def test_sign(self):
29 image_dir = get_bb_var("D", "garage-sign-native")
30 bin_dir = get_bb_var("bindir", "garage-sign-native")
31 gs_path = os.path.join(image_dir, bin_dir[1:], 'garage-sign')
32 result = runCmd('%s --help' % gs_path, ignore_status=True)
33 self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
34
35 def test_push(self):
36 bitbake('core-image-minimal')
37 self.write_config('IMAGE_INSTALL_append = " man "')
38 bitbake('core-image-minimal')
39
40 def test_hsm(self):
41 self.write_config('SOTA_CLIENT_FEATURES="hsm hsm-test"')
42 bitbake('core-image-minimal')
diff --git a/lib/oeqa/selftest/qemucommand.py b/lib/oeqa/selftest/qemucommand.py
new file mode 120000
index 0000000..bc06dde
--- /dev/null
+++ b/lib/oeqa/selftest/qemucommand.py
@@ -0,0 +1 @@
../../../scripts/qemucommand.py \ No newline at end of file
diff --git a/lib/oeqa/selftest/updater.py b/lib/oeqa/selftest/updater.py
new file mode 100644
index 0000000..ad99964
--- /dev/null
+++ b/lib/oeqa/selftest/updater.py
@@ -0,0 +1,179 @@
1import unittest
2import os
3import logging
4import subprocess
5import time
6
7from oeqa.selftest.base import oeSelfTest
8from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
9from oeqa.selftest.qemucommand import QemuCommand
10
11
12class SotaToolsTests(oeSelfTest):
13
14 @classmethod
15 def setUpClass(cls):
16 logger = logging.getLogger("selftest")
17 logger.info('Running bitbake to build aktualizr-native tools')
18 bitbake('aktualizr-native')
19
20 def test_push_help(self):
21 bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'aktualizr-native')
22 p = bb_vars['SYSROOT_DESTDIR'] + bb_vars['bindir'] + "/" + "garage-push"
23 self.assertTrue(os.path.isfile(p), msg = "No garage-push found (%s)" % p)
24 result = runCmd('%s --help' % p, ignore_status=True)
25 self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
26
27 def test_deploy_help(self):
28 bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'aktualizr-native')
29 p = bb_vars['SYSROOT_DESTDIR'] + bb_vars['bindir'] + "/" + "garage-deploy"
30 self.assertTrue(os.path.isfile(p), msg = "No garage-deploy found (%s)" % p)
31 result = runCmd('%s --help' % p, ignore_status=True)
32 self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
33
34
35class GarageSignTests(oeSelfTest):
36
37 @classmethod
38 def setUpClass(cls):
39 logger = logging.getLogger("selftest")
40 logger.info('Running bitbake to build garage-sign-native')
41 bitbake('garage-sign-native')
42
43 def test_help(self):
44 bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'garage-sign-native')
45 p = bb_vars['SYSROOT_DESTDIR'] + bb_vars['bindir'] + "/" + "garage-sign"
46 self.assertTrue(os.path.isfile(p), msg = "No garage-sign found (%s)" % p)
47 result = runCmd('%s --help' % p, ignore_status=True)
48 self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
49
50
51class HsmTests(oeSelfTest):
52
53 def test_hsm(self):
54 self.write_config('SOTA_CLIENT_FEATURES="hsm hsm-test"')
55 bitbake('core-image-minimal')
56
57
58class GeneralTests(oeSelfTest):
59
60 def test_feature_sota(self):
61 result = get_bb_var('DISTRO_FEATURES').find('sota')
62 self.assertNotEqual(result, -1, 'Feature "sota" not set at DISTRO_FEATURES');
63
64 def test_feature_systemd(self):
65 result = get_bb_var('DISTRO_FEATURES').find('systemd')
66 self.assertNotEqual(result, -1, 'Feature "systemd" not set at DISTRO_FEATURES');
67
68 def test_credentials(self):
69 bitbake('core-image-minimal')
70 credentials = get_bb_var('SOTA_PACKED_CREDENTIALS')
71 # skip the test if the variable SOTA_PACKED_CREDENTIALS is not set
72 if credentials is None:
73 raise unittest.SkipTest("Variable 'SOTA_PACKED_CREDENTIALS' not set.")
74 # Check if the file exists
75 self.assertTrue(os.path.isfile(credentials), "File %s does not exist" % credentials)
76 deploydir = get_bb_var('DEPLOY_DIR_IMAGE')
77 imagename = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
78 # Check if the credentials are included in the output image
79 result = runCmd('tar -jtvf %s/%s.tar.bz2 | grep sota_provisioning_credentials.zip' % (deploydir, imagename), ignore_status=True)
80 self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output)
81
82 def test_java(self):
83 result = runCmd('which java', ignore_status=True)
84 self.assertEqual(result.status, 0, "Java not found.")
85
86 def test_add_package(self):
87 print('')
88 deploydir = get_bb_var('DEPLOY_DIR_IMAGE')
89 imagename = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
90 image_path = deploydir + '/' + imagename + '.otaimg'
91 logger = logging.getLogger("selftest")
92
93 logger.info('Running bitbake with man in the image package list')
94 self.write_config('IMAGE_INSTALL_append = " man "')
95 bitbake('-c cleanall man')
96 bitbake('core-image-minimal')
97 result = runCmd('oe-pkgdata-util find-path /usr/bin/man')
98 self.assertEqual(result.output, 'man: /usr/bin/man')
99 path1 = os.path.realpath(image_path)
100 size1 = os.path.getsize(path1)
101 logger.info('First image %s has size %i' % (path1, size1))
102
103 logger.info('Running bitbake without man in the image package list')
104 self.write_config('IMAGE_INSTALL_remove = " man "')
105 bitbake('-c cleanall man')
106 bitbake('core-image-minimal')
107 result = runCmd('oe-pkgdata-util find-path /usr/bin/man', ignore_status=True)
108 self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
109 self.assertEqual(result.output, 'ERROR: Unable to find any package producing path /usr/bin/man')
110 path2 = os.path.realpath(image_path)
111 size2 = os.path.getsize(path2)
112 logger.info('Second image %s has size %i' % (path2, size2))
113 self.assertNotEqual(path1, path2, "Image paths are identical; image was not rebuilt.")
114 self.assertNotEqual(size1, size2, "Image sizes are identical; image was not rebuilt.")
115
116
117class QemuTests(oeSelfTest):
118
119 @classmethod
120 def setUpClass(cls):
121 logger = logging.getLogger("selftest")
122 logger.info('Running bitbake to build core-image-minimal')
123 bitbake('core-image-minimal')
124 # Create empty object.
125 args = type('', (), {})()
126 args.imagename = 'core-image-minimal'
127 args.mac = None
128 # Could use DEPLOY_DIR_IMAGE here but it's already in the machine
129 # subdirectory.
130 args.dir = 'tmp/deploy/images'
131 args.efi = False
132 args.machine = None
133 args.kvm = None # Autodetect
134 args.no_gui = True
135 args.gdb = False
136 args.pcap = None
137 args.overlay = None
138 args.dry_run = False
139
140 cls.qemu = QemuCommand(args)
141 cmdline = cls.qemu.command_line()
142 print('Booting image with run-qemu-ota...')
143 cls.s = subprocess.Popen(cmdline)
144 time.sleep(10)
145
146 @classmethod
147 def tearDownClass(cls):
148 try:
149 cls.s.terminate()
150 except KeyboardInterrupt:
151 pass
152
153 def run_test_qemu(self, command):
154 command = ['ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@localhost -p ' +
155 str(self.qemu.ssh_port) + ' "' + command + '"']
156 s2 = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
157 value, err = s2.communicate()
158 return value, err
159
160 def test_hostname(self):
161 print('')
162 print('Checking machine name (hostname) of device:')
163 value, err = self.run_test_qemu('hostname')
164 machine = get_bb_var('MACHINE', 'core-image-minimal')
165 self.assertEqual(err, b'', 'Error: ' + err.decode())
166 # Strip off line ending.
167 value_str = value.decode()[:-1]
168 self.assertEqual(value_str, machine,
169 'MACHINE does not match hostname: ' + machine + ', ' + value_str)
170 print(value_str)
171
172 def test_var_sota(self):
173 print('')
174 print('Checking contents of /var/sota:')
175 value, err = self.run_test_qemu('ls /var/sota')
176 self.assertEqual(err, b'', 'Error: ' + err.decode())
177 print(value.decode())
178
179
diff --git a/recipes-bsp/u-boot/u-boot_2016.11.bb b/recipes-bsp/u-boot/u-boot_2016.11.bb
deleted file mode 100644
index acd4bb8..0000000
--- a/recipes-bsp/u-boot/u-boot_2016.11.bb
+++ /dev/null
@@ -1,22 +0,0 @@
1require recipes-bsp/u-boot/u-boot.inc
2
3HOMEPAGE = "http://www.denx.de/wiki/U-Boot/WebHome"
4SECTION = "bootloaders"
5
6LICENSE = "GPLv2+"
7LIC_FILES_CHKSUM = "file://Licenses/README;md5=a2c678cfd4a4d97135585cad908541c6"
8PE = "1"
9
10DEPENDS += "dtc-native"
11
12SRCREV = "5ea3e51fc481613a8dee8c02848d1b42c81ad892"
13SRC_URI = "git://git.denx.de/u-boot.git"
14S = "${WORKDIR}/git"
15
16PV = "v2016.11+git${SRCPV}"
17
18#This patch is not compliant with u-boot 2016.11
19#Version of u-boot from yocto 2.2 Morty is 2016.03 from:
20# meta/recipes-bsp/u-boot/u-boot_2016.03.bb
21SRC_URI_remove_raspberrypi3 = "file://0003-Include-lowlevel_init.o-for-rpi2.patch"
22SRC_URI_remove_raspberrypi2 = "file://0003-Include-lowlevel_init.o-for-rpi2.patch"
diff --git a/recipes-sota/aktualizr/aktualizr-hsm-test-prov.bb b/recipes-sota/aktualizr/aktualizr-hsm-test-prov.bb
index b14dc29..c443c56 100644
--- a/recipes-sota/aktualizr/aktualizr-hsm-test-prov.bb
+++ b/recipes-sota/aktualizr/aktualizr-hsm-test-prov.bb
@@ -6,7 +6,7 @@ LICENSE = "MPL-2.0"
6LIC_FILES_CHKSUM = "file://${WORKDIR}/LICENSE;md5=9741c346eef56131163e13b9db1241b3" 6LIC_FILES_CHKSUM = "file://${WORKDIR}/LICENSE;md5=9741c346eef56131163e13b9db1241b3"
7 7
8DEPENDS = "aktualizr-native" 8DEPENDS = "aktualizr-native"
9RDEPENDS_${PN} = "aktualizr" 9RDEPENDS_${PN} = "aktualizr softhsm softhsm-testtoken"
10 10
11SRC_URI = " \ 11SRC_URI = " \
12 file://LICENSE \ 12 file://LICENSE \
diff --git a/recipes-sota/aktualizr/aktualizr_git.bb b/recipes-sota/aktualizr/aktualizr_git.bb
index c98027d..162065e 100644
--- a/recipes-sota/aktualizr/aktualizr_git.bb
+++ b/recipes-sota/aktualizr/aktualizr_git.bb
@@ -11,7 +11,6 @@ DEPENDS_append_class-native = "glib-2.0-native "
11 11
12RDEPENDS_${PN}_class-target = "lshw " 12RDEPENDS_${PN}_class-target = "lshw "
13RDEPENDS_${PN}_append_class-target = "${@bb.utils.contains('SOTA_CLIENT_FEATURES', 'hsm', ' engine-pkcs11', '', d)} " 13RDEPENDS_${PN}_append_class-target = "${@bb.utils.contains('SOTA_CLIENT_FEATURES', 'hsm', ' engine-pkcs11', '', d)} "
14RDEPENDS_${PN}_append_class-target = "${@bb.utils.contains('SOTA_CLIENT_FEATURES', 'hsm-test', ' softhsm softhsm-testtoken', '', d)} "
15 14
16PV = "1.0+git${SRCPV}" 15PV = "1.0+git${SRCPV}"
17PR = "7" 16PR = "7"
@@ -47,5 +46,6 @@ FILES_${PN}_class-target = " \
47 " 46 "
48FILES_${PN}_class-native = " \ 47FILES_${PN}_class-native = " \
49 ${bindir}/aktualizr_implicit_writer \ 48 ${bindir}/aktualizr_implicit_writer \
49 ${bindir}/garage-deploy \
50 ${bindir}/garage-push \ 50 ${bindir}/garage-push \
51 " 51 "
diff --git a/recipes-support/util-linux/util-linux_%.bbappend b/recipes-support/util-linux/util-linux_%.bbappend
deleted file mode 100644
index d653bb2..0000000
--- a/recipes-support/util-linux/util-linux_%.bbappend
+++ /dev/null
@@ -1,3 +0,0 @@
1PACKAGES_append_class-native = "${@bb.utils.contains('DISTRO_FEATURES', 'sota', ' util-linux-agetty-native util-linux-fdisk-native util-linux-cfdisk-native util-linux-sfdisk-native util-linux-swaponoff-native util-linux-losetup-native util-linux-umount-native util-linux-mount-native util-linux-readprofile-native util-linux-uuidd-native util-linux-uuidgen-native util-linux-lscpu-native util-linux-fsck-native util-linux-blkid util-linux-mkfs-native util-linux-mcookie-native util-linux-reset-native util-linux-mkfs.cramfs-native util-linux-fsck.cramfs-native util-linux-fstrim-native util-linux-partx-native ${PN}-bash-completion-native util-linux-hwclock util-linux-findfs-native util-linux-getopt-native util-linux-sulogin-native', ' ', d)}"
2
3PACKAGES_append_class-native = "${@' util-linux-pylibmount-native' if bb.utils.contains('DISTRO_FEATURES', 'sota', True, False, d) and bb.utils.contains('PACKAGECONFIG', 'pylibmount', True, False, d) else ' '}"
diff --git a/scripts/envsetup.sh b/scripts/envsetup.sh
index 260b048..ff78681 100755
--- a/scripts/envsetup.sh
+++ b/scripts/envsetup.sh
@@ -24,15 +24,7 @@ fi
24METADIR="${SOURCEDIR}/../.." 24METADIR="${SOURCEDIR}/../.."
25 25
26if [[ ! -f "${BUILDDIR}/conf/local.conf" ]]; then 26if [[ ! -f "${BUILDDIR}/conf/local.conf" ]]; then
27 if [ -z "$TEMPLATECONF" ] && [ -d ${METADIR}/meta-updater-${MACHINE}/conf ]; then 27 source "$METADIR/poky/oe-init-build-env" "$BUILDDIR"
28 # Use the template configurations for the specified machine
29 TEMPLATECONF=${METADIR}/meta-updater-${MACHINE}/conf
30 source "$METADIR/poky/oe-init-build-env" "$BUILDDIR"
31 unset TEMPLATECONF
32 else
33 # Use the default configurations or TEMPLATECONF set by the user
34 source "$METADIR/poky/oe-init-build-env" "$BUILDDIR"
35 fi
36 echo "METADIR := \"\${@os.path.abspath('${METADIR}')}\"" >> conf/bblayers.conf 28 echo "METADIR := \"\${@os.path.abspath('${METADIR}')}\"" >> conf/bblayers.conf
37 cat "${METADIR}/meta-updater/conf/include/bblayers/sota.inc" >> conf/bblayers.conf 29 cat "${METADIR}/meta-updater/conf/include/bblayers/sota.inc" >> conf/bblayers.conf
38 cat "${METADIR}/meta-updater/conf/include/bblayers/sota_${MACHINE}.inc" >> conf/bblayers.conf 30 cat "${METADIR}/meta-updater/conf/include/bblayers/sota_${MACHINE}.inc" >> conf/bblayers.conf
diff --git a/scripts/lib/wic/plugins/source/otaimage.py b/scripts/lib/wic/plugins/source/otaimage.py
index 26cfb10..ee8088b 100644
--- a/scripts/lib/wic/plugins/source/otaimage.py
+++ b/scripts/lib/wic/plugins/source/otaimage.py
@@ -20,7 +20,7 @@ import os
20import sys 20import sys
21 21
22from wic.plugins.source.rawcopy import RawCopyPlugin 22from wic.plugins.source.rawcopy import RawCopyPlugin
23from wic.utils.misc import get_bitbake_var 23from wic.misc import get_bitbake_var
24 24
25logger = logging.getLogger('wic') 25logger = logging.getLogger('wic')
26 26
diff --git a/scripts/qemucommand.py b/scripts/qemucommand.py
new file mode 100644
index 0000000..82a9540
--- /dev/null
+++ b/scripts/qemucommand.py
@@ -0,0 +1,127 @@
1from os.path import exists, join, realpath, abspath
2from os import listdir
3import random
4import socket
5from subprocess import check_output, CalledProcessError
6
7EXTENSIONS = {
8 'intel-corei7-64': 'wic',
9 'qemux86-64': 'otaimg'
10}
11
12
13def find_local_port(start_port):
14 """"
15 Find the next free TCP port after 'start_port'.
16 """
17
18 for port in range(start_port, start_port + 10):
19 try:
20 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21 s.bind(('', port))
22 return port
23 except socket.error:
24 print("Skipping port %d" % port)
25 finally:
26 s.close()
27 raise Exception("Could not find a free TCP port")
28
29
30def random_mac():
31 """Return a random Ethernet MAC address
32 @link https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml#ethernet-numbers-2
33 """
34 head = "ca:fe:"
35 hex_digits = '0123456789abcdef'
36 tail = ':'.join([random.choice(hex_digits) + random.choice(hex_digits) for _ in range(4)])
37 return head + tail
38
39
40class QemuCommand(object):
41 def __init__(self, args):
42 if args.machine:
43 self.machine = args.machine
44 else:
45 machines = listdir(args.dir)
46 if len(machines) == 1:
47 self.machine = machines[0]
48 else:
49 raise ValueError("Could not autodetect machine type from %s" % args.dir)
50 if args.efi:
51 self.bios = 'OVMF.fd'
52 else:
53 uboot = abspath(join(args.dir, self.machine, 'u-boot-qemux86-64.rom'))
54 if not exists(uboot):
55 raise ValueError("U-Boot image %s does not exist" % uboot)
56 self.bios = uboot
57 if exists(args.imagename):
58 image = args.imagename
59 else:
60 ext = EXTENSIONS.get(self.machine, 'wic')
61 image = join(args.dir, self.machine, '%s-%s.%s' % (args.imagename, self.machine, ext))
62 self.image = realpath(image)
63 if not exists(self.image):
64 raise ValueError("OS image %s does not exist" % self.image)
65 if args.mac:
66 self.mac_address = args.mac
67 else:
68 self.mac_address = random_mac()
69 self.serial_port = find_local_port(8990)
70 self.ssh_port = find_local_port(2222)
71 if args.kvm is None:
72 # Autodetect KVM using 'kvm-ok'
73 try:
74 check_output(['kvm-ok'])
75 self.kvm = True
76 except CalledProcessError:
77 self.kvm = False
78 else:
79 self.kvm = args.kvm
80 self.gui = not args.no_gui
81 self.gdb = args.gdb
82 self.pcap = args.pcap
83 self.overlay = args.overlay
84
85 def command_line(self):
86 netuser = 'user,hostfwd=tcp:0.0.0.0:%d-:22,restrict=off' % self.ssh_port
87 if self.gdb:
88 netuser += ',hostfwd=tcp:0.0.0.0:2159-:2159'
89 cmdline = [
90 "qemu-system-x86_64",
91 "-bios", self.bios
92 ]
93 if not self.overlay:
94 cmdline += ["-drive", "file=%s,if=ide,format=raw,snapshot=on" % self.image]
95 cmdline += [
96 "-serial", "tcp:127.0.0.1:%d,server,nowait" % self.serial_port,
97 "-m", "1G",
98 "-usb",
99 "-usbdevice", "tablet",
100 "-show-cursor",
101 "-vga", "std",
102 "-net", netuser,
103 "-net", "nic,macaddr=%s" % self.mac_address
104 ]
105 if self.pcap:
106 cmdline += ['-net', 'dump,file=' + self.pcap]
107 if self.gui:
108 cmdline += ["-serial", "stdio"]
109 else:
110 cmdline.append('-nographic')
111 if self.kvm:
112 cmdline.append('-enable-kvm')
113 else:
114 cmdline += ['-cpu', 'Haswell']
115 if self.overlay:
116 cmdline.append(self.overlay)
117 return cmdline
118
119 def img_command_line(self):
120 cmdline = [
121 "qemu-img", "create",
122 "-o", "backing_file=%s" % self.image,
123 "-f", "qcow2",
124 self.overlay]
125 return cmdline
126
127
diff --git a/scripts/run-qemu-ota b/scripts/run-qemu-ota
index 641296c..56e4fbc 100755
--- a/scripts/run-qemu-ota
+++ b/scripts/run-qemu-ota
@@ -2,126 +2,12 @@
2 2
3from argparse import ArgumentParser 3from argparse import ArgumentParser
4from subprocess import Popen 4from subprocess import Popen
5from os.path import exists, join, realpath 5from os.path import exists
6from os import listdir
7import random
8import sys 6import sys
9import socket 7from qemucommand import QemuCommand
10 8
11DEFAULT_DIR = 'tmp/deploy/images' 9DEFAULT_DIR = 'tmp/deploy/images'
12 10
13EXTENSIONS = {
14 'intel-corei7-64': 'wic',
15 'qemux86-64': 'otaimg'
16}
17
18
19def find_local_port(start_port):
20 """"
21 Find the next free TCP port after 'start_port'.
22 """
23
24 for port in range(start_port, start_port + 10):
25 try:
26 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
27 s.bind(('', port))
28 return port
29 except socket.error:
30 print("Skipping port %d" % port)
31 finally:
32 s.close()
33 raise Exception("Could not find a free TCP port")
34
35
36def random_mac():
37 """Return a random Ethernet MAC address
38 @link https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml#ethernet-numbers-2
39 """
40 head = "ca:fe:"
41 hex_digits = '0123456789abcdef'
42 tail = ':'.join([random.choice(hex_digits) + random.choice(hex_digits) for _ in range(4)])
43 return head + tail
44
45
46class QemuCommand(object):
47 def __init__(self, args):
48 if args.machine:
49 self.machine = args.machine
50 else:
51 machines = listdir(args.dir)
52 if len(machines) == 1:
53 self.machine = machines[0]
54 else:
55 raise ValueError("Could not autodetect machine type from %s" % args.dir)
56 if args.efi:
57 self.bios = 'OVMF.fd'
58 else:
59 uboot = join(args.dir, self.machine, 'u-boot-qemux86-64.rom')
60 if not exists(uboot):
61 raise ValueError("U-Boot image %s does not exist" % uboot)
62 self.bios = uboot
63 if exists(args.imagename):
64 image = args.imagename
65 else:
66 ext = EXTENSIONS.get(self.machine, 'wic')
67 image = join(args.dir, self.machine, '%s-%s.%s' % (args.imagename, self.machine, ext))
68 self.image = realpath(image)
69 if not exists(self.image):
70 raise ValueError("OS image %s does not exist" % self.image)
71 if args.mac:
72 self.mac_address = args.mac
73 else:
74 self.mac_address = random_mac()
75 self.serial_port = find_local_port(8990)
76 self.ssh_port = find_local_port(2222)
77 self.kvm = not args.no_kvm
78 self.gui = not args.no_gui
79 self.gdb = args.gdb
80 self.pcap = args.pcap
81 self.overlay = args.overlay
82
83 def command_line(self):
84 netuser = 'user,hostfwd=tcp:0.0.0.0:%d-:22,restrict=off' % self.ssh_port
85 if self.gdb:
86 netuser += ',hostfwd=tcp:0.0.0.0:2159-:2159'
87 cmdline = [
88 "qemu-system-x86_64",
89 "-bios", self.bios
90 ]
91 if not self.overlay:
92 cmdline += ["-drive", "file=%s,if=ide,format=raw,snapshot=on" % self.image]
93 cmdline += [
94 "-serial", "tcp:127.0.0.1:%d,server,nowait" % self.serial_port,
95 "-m", "1G",
96 "-usb",
97 "-usbdevice", "tablet",
98 "-show-cursor",
99 "-vga", "std",
100 "-net", netuser,
101 "-net", "nic,macaddr=%s" % self.mac_address
102 ]
103 if self.pcap:
104 cmdline += ['-net', 'dump,file=' + self.pcap]
105 if self.gui:
106 cmdline += ["-serial", "stdio"]
107 else:
108 cmdline.append('-nographic')
109 if self.kvm:
110 cmdline.append('-enable-kvm')
111 else:
112 cmdline += ['-cpu', 'Haswell']
113 if self.overlay:
114 cmdline.append(self.overlay)
115 return cmdline
116
117 def img_command_line(self):
118 cmdline = [
119 "qemu-img", "create",
120 "-o", "backing_file=%s" % self.image,
121 "-f", "qcow2",
122 self.overlay]
123 return cmdline
124
125 11
126def main(): 12def main():
127 parser = ArgumentParser(description='Run meta-updater image in qemu') 13 parser = ArgumentParser(description='Run meta-updater image in qemu')
@@ -135,11 +21,18 @@ def main():
135 'OSTREE_BOOTLOADER = "grub" and OVMF.fd firmware to be installed (try "apt install ovmf")', 21 'OSTREE_BOOTLOADER = "grub" and OVMF.fd firmware to be installed (try "apt install ovmf")',
136 action='store_true') 22 action='store_true')
137 parser.add_argument('--machine', default=None, help="Target MACHINE") 23 parser.add_argument('--machine', default=None, help="Target MACHINE")
138 parser.add_argument('--no-kvm', help='Disable KVM in QEMU', action='store_true') 24 kvm_group = parser.add_argument_group()
25 kvm_group.add_argument('--force-kvm', help='Force use of KVM (default is to autodetect)',
26 dest='kvm', action='store_true', default=None)
27 kvm_group.add_argument('--no-kvm', help='Disable KVM in QEMU',
28 dest='kvm', action='store_false')
139 parser.add_argument('--no-gui', help='Disable GUI', action='store_true') 29 parser.add_argument('--no-gui', help='Disable GUI', action='store_true')
140 parser.add_argument('--gdb', help='Export gdbserver port 2159 from the image', action='store_true') 30 parser.add_argument('--gdb', help='Export gdbserver port 2159 from the image', action='store_true')
141 parser.add_argument('--pcap', default=None, help='Dump all network traffic') 31 parser.add_argument('--pcap', default=None, help='Dump all network traffic')
142 parser.add_argument('-o', '--overlay', type=str, metavar='file.cow', help='Use an overlay storage image file. Will be created if it does not exist. This option lets you have a persistent image without modifying the underlying image file, permitting multiple different persistent machines.') 32 parser.add_argument('-o', '--overlay', type=str, metavar='file.cow',
33 help='Use an overlay storage image file. Will be created if it does not exist. ' +
34 'This option lets you have a persistent image without modifying the underlying image ' +
35 'file, permitting multiple different persistent machines.')
143 parser.add_argument('-n', '--dry-run', help='Print qemu command line rather then run it', action='store_true') 36 parser.add_argument('-n', '--dry-run', help='Print qemu command line rather then run it', action='store_true')
144 args = parser.parse_args() 37 args = parser.parse_args()
145 try: 38 try: