diff options
| -rw-r--r-- | README.adoc | 10 | ||||
| -rw-r--r-- | lib/oeqa/selftest/cases/updater.py | 260 |
2 files changed, 195 insertions, 75 deletions
diff --git a/README.adoc b/README.adoc index c87bd01..3562f52 100644 --- a/README.adoc +++ b/README.adoc | |||
| @@ -20,7 +20,7 @@ If you already have a Yocto-based project and you want to add atomic filesystem | |||
| 20 | 2. Clone BSP integration layer (`meta-updater-$\{PLATFORM}`, e.g. https://github.com/advancedtelematic/meta-updater-raspberrypi[meta-updater-raspberrypi]) and add it to your `conf/bblayers.conf`. If your board isn't supported yet, you could write a BSP integration for it yourself. See the <<Adding support for your board>> section for the details. | 20 | 2. Clone BSP integration layer (`meta-updater-$\{PLATFORM}`, e.g. https://github.com/advancedtelematic/meta-updater-raspberrypi[meta-updater-raspberrypi]) and add it to your `conf/bblayers.conf`. If your board isn't supported yet, you could write a BSP integration for it yourself. See the <<Adding support for your board>> section for the details. |
| 21 | 3. Set up your https://www.yoctoproject.org/docs/2.1/ref-manual/ref-manual.html#var-DISTRO[distro]. If you are using "poky", the default distro in Yocto, you can change it in your `conf/local.conf` to "poky-sota". Alternatively, if you are using your own or third party distro configuration, you can add `INHERIT += " sota"` to it, thus combining capabilities of your distro with meta-updater features. | 21 | 3. Set up your https://www.yoctoproject.org/docs/2.1/ref-manual/ref-manual.html#var-DISTRO[distro]. If you are using "poky", the default distro in Yocto, you can change it in your `conf/local.conf` to "poky-sota". Alternatively, if you are using your own or third party distro configuration, you can add `INHERIT += " sota"` to it, thus combining capabilities of your distro with meta-updater features. |
| 22 | 22 | ||
| 23 | You can then build your image as usual, with bitbake. After building the root file system, bitbake will then create an https://ostree.readthedocs.io/en/latest/manual/adapting-existing/[OSTree-enabled version] of it, commit it to your local OSTree repo and (optionally) push it to a remote server. Additionally, a live disk image will be created (normally named `$\{IMAGE_NAME}.-sdimg-ota` e.g. `core-image-raspberrypi3.rpi-sdimg-ota`). You can control this behaviour through <<variables in your local.conf,OSTree-related variables in your local.conf>>. | 23 | You can then build your image as usual, with bitbake. After building the root file system, bitbake will then create an https://ostree.readthedocs.io/en/latest/manual/adapting-existing/[OSTree-enabled version] of it, commit it to your local OSTree repo and (optionally) push it to a remote server. Additionally, a live disk image will be created (normally named `$\{IMAGE_NAME}.-sdimg-ota` e.g. `core-image-raspberrypi3.rpi-sdimg-ota`). You can control this behaviour through <<sota-related-variables-in-localconf,variables in your local.conf>>. |
| 24 | 24 | ||
| 25 | === Build in AGL | 25 | === Build in AGL |
| 26 | 26 | ||
| @@ -73,7 +73,7 @@ You may take a look into https://github.com/advancedtelematic/meta-updater-minno | |||
| 73 | 73 | ||
| 74 | Although we have used U-Boot so far, other boot loaders can be configured work with OSTree as well. | 74 | Although we have used U-Boot so far, other boot loaders can be configured work with OSTree as well. |
| 75 | 75 | ||
| 76 | == SOTA-related variables in `local.conf` | 76 | == SOTA-related variables in local.conf |
| 77 | 77 | ||
| 78 | * `OSTREE_REPO` - path to your OSTree repository. Defaults to `$\{DEPLOY_DIR_IMAGE}/ostree_repo` | 78 | * `OSTREE_REPO` - path to your OSTree repository. Defaults to `$\{DEPLOY_DIR_IMAGE}/ostree_repo` |
| 79 | * `OSTREE_OSNAME` - OS deployment name on your target device. For more information about deployments and osnames see the https://ostree.readthedocs.io/en/latest/manual/deployment/[OSTree documentation]. Defaults to "poky". | 79 | * `OSTREE_OSNAME` - OS deployment name on your target device. For more information about deployments and osnames see the https://ostree.readthedocs.io/en/latest/manual/deployment/[OSTree documentation]. Defaults to "poky". |
| @@ -151,13 +151,15 @@ SANITY_TESTED_DISTROS = "" | |||
| 151 | IMAGE_INSTALL_append = " dropbear " | 151 | IMAGE_INSTALL_append = " dropbear " |
| 152 | ``` | 152 | ``` |
| 153 | 153 | ||
| 154 | 3. To be able to build an image for the grub tests, you will need to install https://github.com/tianocore/tianocore.github.io/wiki/OVMF[TianoCore's ovmf] package on your host system. On Debian-like systems, you can do so with this command: | 154 | 3. Some tests require that `SOTA_PACKED_CREDENTIALS` is set in your `conf/local.conf`. See the <<sota-related-variables-in-localconf,SOTA-related variables in local.conf>> section. |
| 155 | |||
| 156 | 4. To be able to build an image for the grub tests, you will need to install https://github.com/tianocore/tianocore.github.io/wiki/OVMF[TianoCore's ovmf] package on your host system. On Debian-like systems, you can do so with this command: | ||
| 155 | + | 157 | + |
| 156 | ``` | 158 | ``` |
| 157 | sudo apt install ovmf | 159 | sudo apt install ovmf |
| 158 | ``` | 160 | ``` |
| 159 | 161 | ||
| 160 | 4. Run oe-selftest: | 162 | 5. Run oe-selftest: |
| 161 | + | 163 | + |
| 162 | ``` | 164 | ``` |
| 163 | oe-selftest --run-tests updater | 165 | oe-selftest --run-tests updater |
diff --git a/lib/oeqa/selftest/cases/updater.py b/lib/oeqa/selftest/cases/updater.py index b544762..adb4061 100644 --- a/lib/oeqa/selftest/cases/updater.py +++ b/lib/oeqa/selftest/cases/updater.py | |||
| @@ -64,7 +64,6 @@ class GeneralTests(OESelftestTestCase): | |||
| 64 | "Java not found. Do you have a JDK installed on your host machine?") | 64 | "Java not found. Do you have a JDK installed on your host machine?") |
| 65 | 65 | ||
| 66 | def test_add_package(self): | 66 | def test_add_package(self): |
| 67 | print('') | ||
| 68 | deploydir = get_bb_var('DEPLOY_DIR_IMAGE') | 67 | deploydir = get_bb_var('DEPLOY_DIR_IMAGE') |
| 69 | imagename = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal') | 68 | imagename = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal') |
| 70 | image_path = deploydir + '/' + imagename + '.otaimg' | 69 | image_path = deploydir + '/' + imagename + '.otaimg' |
| @@ -134,22 +133,34 @@ class AktualizrToolsTests(OESelftestTestCase): | |||
| 134 | self.assertTrue(os.path.getsize(ca_path) > 0, "Client certificate at %s is empty." % ca_path) | 133 | self.assertTrue(os.path.getsize(ca_path) > 0, "Client certificate at %s is empty." % ca_path) |
| 135 | 134 | ||
| 136 | 135 | ||
| 137 | class QemuTests(OESelftestTestCase): | 136 | class AutoProvTests(OESelftestTestCase): |
| 138 | 137 | ||
| 139 | @classmethod | 138 | def setUpLocal(self): |
| 140 | def setUpClass(cls): | 139 | layer = "meta-updater-qemux86-64" |
| 141 | super(QemuTests, cls).setUpClass() | 140 | result = runCmd('bitbake-layers show-layers') |
| 142 | cls.qemu, cls.s = qemu_launch(machine='qemux86-64') | 141 | if re.search(layer, result.output) is None: |
| 142 | # Assume the directory layout for finding other layers. We could also | ||
| 143 | # make assumptions by using 'show-layers', but either way, if the | ||
| 144 | # layers we need aren't where we expect them, we are out of like. | ||
| 145 | path = os.path.abspath(os.path.dirname(__file__)) | ||
| 146 | metadir = path + "/../../../../../" | ||
| 147 | self.meta_qemu = metadir + layer | ||
| 148 | runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu) | ||
| 149 | else: | ||
| 150 | self.meta_qemu = None | ||
| 151 | self.append_config('MACHINE = "qemux86-64"') | ||
| 152 | self.append_config('SOTA_CLIENT_PROV = " aktualizr-auto-prov "') | ||
| 153 | self.qemu, self.s = qemu_launch(machine='qemux86-64') | ||
| 143 | 154 | ||
| 144 | @classmethod | 155 | def tearDownLocal(self): |
| 145 | def tearDownClass(cls): | 156 | qemu_terminate(self.s) |
| 146 | qemu_terminate(cls.s) | 157 | if self.meta_qemu: |
| 147 | super(QemuTests, cls).tearDownClass() | 158 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True) |
| 148 | 159 | ||
| 149 | def qemu_command(self, command): | 160 | def qemu_command(self, command): |
| 150 | return qemu_send_command(self.qemu.ssh_port, command) | 161 | return qemu_send_command(self.qemu.ssh_port, command) |
| 151 | 162 | ||
| 152 | def test_qemu(self): | 163 | def test_provisioning(self): |
| 153 | print('Checking machine name (hostname) of device:') | 164 | print('Checking machine name (hostname) of device:') |
| 154 | stdout, stderr, retcode = self.qemu_command('hostname') | 165 | stdout, stderr, retcode = self.qemu_command('hostname') |
| 155 | self.assertEqual(retcode, 0, "Unable to check hostname. " + | 166 | self.assertEqual(retcode, 0, "Unable to check hostname. " + |
| @@ -157,10 +168,10 @@ class QemuTests(OESelftestTestCase): | |||
| 157 | machine = get_bb_var('MACHINE', 'core-image-minimal') | 168 | machine = get_bb_var('MACHINE', 'core-image-minimal') |
| 158 | self.assertEqual(stderr, b'', 'Error: ' + stderr.decode()) | 169 | self.assertEqual(stderr, b'', 'Error: ' + stderr.decode()) |
| 159 | # Strip off line ending. | 170 | # Strip off line ending. |
| 160 | value_str = stdout.decode()[:-1] | 171 | value = stdout.decode()[:-1] |
| 161 | self.assertEqual(value_str, machine, | 172 | self.assertEqual(value, machine, |
| 162 | 'MACHINE does not match hostname: ' + machine + ', ' + value_str) | 173 | 'MACHINE does not match hostname: ' + machine + ', ' + value) |
| 163 | print(value_str) | 174 | print(value) |
| 164 | print('Checking output of aktualizr-info:') | 175 | print('Checking output of aktualizr-info:') |
| 165 | ran_ok = False | 176 | ran_ok = False |
| 166 | for delay in [0, 1, 2, 5, 10, 15]: | 177 | for delay in [0, 1, 2, 5, 10, 15]: |
| @@ -171,31 +182,122 @@ class QemuTests(OESelftestTestCase): | |||
| 171 | break | 182 | break |
| 172 | self.assertTrue(ran_ok, 'aktualizr-info failed: ' + stderr.decode() + stdout.decode()) | 183 | self.assertTrue(ran_ok, 'aktualizr-info failed: ' + stderr.decode() + stdout.decode()) |
| 173 | 184 | ||
| 185 | verifyProvisioned(self, machine) | ||
| 186 | |||
| 187 | |||
| 188 | class RpiTests(OESelftestTestCase): | ||
| 189 | |||
| 190 | def setUpLocal(self): | ||
| 191 | # Add layers before changing the machine type, otherwise the sanity | ||
| 192 | # checker complains loudly. | ||
| 193 | layer_python = "meta-openembedded/meta-python" | ||
| 194 | layer_rpi = "meta-raspberrypi" | ||
| 195 | layer_upd_rpi = "meta-updater-raspberrypi" | ||
| 196 | result = runCmd('bitbake-layers show-layers') | ||
| 197 | # Assume the directory layout for finding other layers. We could also | ||
| 198 | # make assumptions by using 'show-layers', but either way, if the | ||
| 199 | # layers we need aren't where we expect them, we are out of like. | ||
| 200 | path = os.path.abspath(os.path.dirname(__file__)) | ||
| 201 | metadir = path + "/../../../../../" | ||
| 202 | if re.search(layer_python, result.output) is None: | ||
| 203 | self.meta_python = metadir + layer_python | ||
| 204 | runCmd('bitbake-layers add-layer "%s"' % self.meta_python) | ||
| 205 | else: | ||
| 206 | self.meta_python = None | ||
| 207 | if re.search(layer_rpi, result.output) is None: | ||
| 208 | self.meta_rpi = metadir + layer_rpi | ||
| 209 | runCmd('bitbake-layers add-layer "%s"' % self.meta_rpi) | ||
| 210 | else: | ||
| 211 | self.meta_rpi = None | ||
| 212 | if re.search(layer_upd_rpi, result.output) is None: | ||
| 213 | self.meta_upd_rpi = metadir + layer_upd_rpi | ||
| 214 | runCmd('bitbake-layers add-layer "%s"' % self.meta_upd_rpi) | ||
| 215 | else: | ||
| 216 | self.meta_upd_rpi = None | ||
| 217 | |||
| 218 | # This is trickier that I would've thought. The fundamental problem is | ||
| 219 | # that the qemu layer changes the u-boot file extension to .rom, but | ||
| 220 | # raspberrypi still expects .bin. To prevent this, the qemu layer must | ||
| 221 | # be temporarily removed if it is present. It has to be removed by name | ||
| 222 | # without the complete path, but to add it back when we are done, we | ||
| 223 | # need the full path. | ||
| 224 | p = re.compile(r'meta-updater-qemux86-64\s*(\S*meta-updater-qemux86-64)\s') | ||
| 225 | m = p.search(result.output) | ||
| 226 | if m and m.lastindex > 0: | ||
| 227 | self.meta_qemu = m.group(1) | ||
| 228 | runCmd('bitbake-layers remove-layer meta-updater-qemux86-64') | ||
| 229 | else: | ||
| 230 | self.meta_qemu = None | ||
| 231 | |||
| 232 | self.append_config('MACHINE = "raspberrypi3"') | ||
| 233 | self.append_config('SOTA_CLIENT_PROV = " aktualizr-auto-prov "') | ||
| 234 | |||
| 235 | def tearDownLocal(self): | ||
| 236 | if self.meta_qemu: | ||
| 237 | runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu, ignore_status=True) | ||
| 238 | if self.meta_upd_rpi: | ||
| 239 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_upd_rpi, ignore_status=True) | ||
| 240 | if self.meta_rpi: | ||
| 241 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_rpi, ignore_status=True) | ||
| 242 | if self.meta_python: | ||
| 243 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_python, ignore_status=True) | ||
| 244 | |||
| 245 | def test_rpi(self): | ||
| 246 | logger = logging.getLogger("selftest") | ||
| 247 | logger.info('Running bitbake to build rpi-basic-image') | ||
| 248 | self.append_config('SOTA_CLIENT_PROV = "aktualizr-auto-prov"') | ||
| 249 | bitbake('rpi-basic-image') | ||
| 250 | credentials = get_bb_var('SOTA_PACKED_CREDENTIALS') | ||
| 251 | # Skip the test if the variable SOTA_PACKED_CREDENTIALS is not set. | ||
| 252 | if credentials is None: | ||
| 253 | raise unittest.SkipTest("Variable 'SOTA_PACKED_CREDENTIALS' not set.") | ||
| 254 | # Check if the file exists. | ||
| 255 | self.assertTrue(os.path.isfile(credentials), "File %s does not exist" % credentials) | ||
| 256 | deploydir = get_bb_var('DEPLOY_DIR_IMAGE') | ||
| 257 | imagename = get_bb_var('IMAGE_LINK_NAME', 'rpi-basic-image') | ||
| 258 | # Check if the credentials are included in the output image. | ||
| 259 | result = runCmd('tar -jtvf %s/%s.tar.bz2 | grep sota_provisioning_credentials.zip' % | ||
| 260 | (deploydir, imagename), ignore_status=True) | ||
| 261 | self.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output) | ||
| 262 | |||
| 174 | 263 | ||
| 175 | class GrubTests(OESelftestTestCase): | 264 | class GrubTests(OESelftestTestCase): |
| 176 | 265 | ||
| 177 | def setUpLocal(self): | 266 | def setUpLocal(self): |
| 178 | # This is a bit of a hack but I can't see a better option. | 267 | layer_intel = "meta-intel" |
| 268 | layer_minnow = "meta-updater-minnowboard" | ||
| 269 | result = runCmd('bitbake-layers show-layers') | ||
| 270 | # Assume the directory layout for finding other layers. We could also | ||
| 271 | # make assumptions by using 'show-layers', but either way, if the | ||
| 272 | # layers we need aren't where we expect them, we are out of like. | ||
| 179 | path = os.path.abspath(os.path.dirname(__file__)) | 273 | path = os.path.abspath(os.path.dirname(__file__)) |
| 180 | metadir = path + "/../../../../../" | 274 | metadir = path + "/../../../../../" |
| 181 | grub_config = 'OSTREE_BOOTLOADER = "grub"\nMACHINE = "intel-corei7-64"' | 275 | if re.search(layer_intel, result.output) is None: |
| 182 | self.append_config(grub_config) | 276 | self.meta_intel = metadir + layer_intel |
| 183 | self.meta_intel = metadir + "meta-intel" | 277 | runCmd('bitbake-layers add-layer "%s"' % self.meta_intel) |
| 184 | self.meta_minnow = metadir + "meta-updater-minnowboard" | 278 | else: |
| 185 | runCmd('bitbake-layers add-layer "%s"' % self.meta_intel) | 279 | self.meta_intel = None |
| 186 | runCmd('bitbake-layers add-layer "%s"' % self.meta_minnow) | 280 | if re.search(layer_minnow, result.output) is None: |
| 281 | self.meta_minnow = metadir + layer_minnow | ||
| 282 | runCmd('bitbake-layers add-layer "%s"' % self.meta_minnow) | ||
| 283 | else: | ||
| 284 | self.meta_minnow = None | ||
| 285 | self.append_config('MACHINE = "intel-corei7-64"') | ||
| 286 | self.append_config('OSTREE_BOOTLOADER = "grub"') | ||
| 287 | self.append_config('SOTA_CLIENT_PROV = " aktualizr-auto-prov "') | ||
| 187 | self.qemu, self.s = qemu_launch(efi=True, machine='intel-corei7-64') | 288 | self.qemu, self.s = qemu_launch(efi=True, machine='intel-corei7-64') |
| 188 | 289 | ||
| 189 | def tearDownLocal(self): | 290 | def tearDownLocal(self): |
| 190 | qemu_terminate(self.s) | 291 | qemu_terminate(self.s) |
| 191 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_intel, ignore_status=True) | 292 | if self.meta_intel: |
| 192 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_minnow, ignore_status=True) | 293 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_intel, ignore_status=True) |
| 294 | if self.meta_minnow: | ||
| 295 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_minnow, ignore_status=True) | ||
| 193 | 296 | ||
| 194 | def qemu_command(self, command): | 297 | def qemu_command(self, command): |
| 195 | return qemu_send_command(self.qemu.ssh_port, command) | 298 | return qemu_send_command(self.qemu.ssh_port, command) |
| 196 | 299 | ||
| 197 | def test_grub(self): | 300 | def test_grub(self): |
| 198 | print('') | ||
| 199 | print('Checking machine name (hostname) of device:') | 301 | print('Checking machine name (hostname) of device:') |
| 200 | stdout, stderr, retcode = self.qemu_command('hostname') | 302 | stdout, stderr, retcode = self.qemu_command('hostname') |
| 201 | self.assertEqual(retcode, 0, "Unable to check hostname. " + | 303 | self.assertEqual(retcode, 0, "Unable to check hostname. " + |
| @@ -218,16 +320,32 @@ class GrubTests(OESelftestTestCase): | |||
| 218 | break | 320 | break |
| 219 | self.assertTrue(ran_ok, 'aktualizr-info failed: ' + stderr.decode() + stdout.decode()) | 321 | self.assertTrue(ran_ok, 'aktualizr-info failed: ' + stderr.decode() + stdout.decode()) |
| 220 | 322 | ||
| 323 | verifyProvisioned(self, machine) | ||
| 324 | |||
| 221 | 325 | ||
| 222 | class ImplProvTests(OESelftestTestCase): | 326 | class ImplProvTests(OESelftestTestCase): |
| 223 | 327 | ||
| 224 | def setUpLocal(self): | 328 | def setUpLocal(self): |
| 329 | layer = "meta-updater-qemux86-64" | ||
| 330 | result = runCmd('bitbake-layers show-layers') | ||
| 331 | if re.search(layer, result.output) is None: | ||
| 332 | # Assume the directory layout for finding other layers. We could also | ||
| 333 | # make assumptions by using 'show-layers', but either way, if the | ||
| 334 | # layers we need aren't where we expect them, we are out of like. | ||
| 335 | path = os.path.abspath(os.path.dirname(__file__)) | ||
| 336 | metadir = path + "/../../../../../" | ||
| 337 | self.meta_qemu = metadir + layer | ||
| 338 | runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu) | ||
| 339 | else: | ||
| 340 | self.meta_qemu = None | ||
| 341 | self.append_config('MACHINE = "qemux86-64"') | ||
| 225 | self.append_config('SOTA_CLIENT_PROV = " aktualizr-implicit-prov "') | 342 | self.append_config('SOTA_CLIENT_PROV = " aktualizr-implicit-prov "') |
| 226 | # note: this will build aktualizr-native as a side-effect | ||
| 227 | self.qemu, self.s = qemu_launch(machine='qemux86-64') | 343 | self.qemu, self.s = qemu_launch(machine='qemux86-64') |
| 228 | 344 | ||
| 229 | def tearDownLocal(self): | 345 | def tearDownLocal(self): |
| 230 | qemu_terminate(self.s) | 346 | qemu_terminate(self.s) |
| 347 | if self.meta_qemu: | ||
| 348 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True) | ||
| 231 | 349 | ||
| 232 | def qemu_command(self, command): | 350 | def qemu_command(self, command): |
| 233 | return qemu_send_command(self.qemu.ssh_port, command) | 351 | return qemu_send_command(self.qemu.ssh_port, command) |
| @@ -240,10 +358,10 @@ class ImplProvTests(OESelftestTestCase): | |||
| 240 | machine = get_bb_var('MACHINE', 'core-image-minimal') | 358 | machine = get_bb_var('MACHINE', 'core-image-minimal') |
| 241 | self.assertEqual(stderr, b'', 'Error: ' + stderr.decode()) | 359 | self.assertEqual(stderr, b'', 'Error: ' + stderr.decode()) |
| 242 | # Strip off line ending. | 360 | # Strip off line ending. |
| 243 | value_str = stdout.decode()[:-1] | 361 | value = stdout.decode()[:-1] |
| 244 | self.assertEqual(value_str, machine, | 362 | self.assertEqual(value, machine, |
| 245 | 'MACHINE does not match hostname: ' + machine + ', ' + value_str) | 363 | 'MACHINE does not match hostname: ' + machine + ', ' + value) |
| 246 | print(value_str) | 364 | print(value) |
| 247 | print('Checking output of aktualizr-info:') | 365 | print('Checking output of aktualizr-info:') |
| 248 | ran_ok = False | 366 | ran_ok = False |
| 249 | for delay in [0, 1, 2, 5, 10, 15]: | 367 | for delay in [0, 1, 2, 5, 10, 15]: |
| @@ -271,36 +389,33 @@ class ImplProvTests(OESelftestTestCase): | |||
| 271 | akt_native_run(self, 'aktualizr_cert_provider -c {creds} -t root@localhost -p {port} -s -g {config}' | 389 | akt_native_run(self, 'aktualizr_cert_provider -c {creds} -t root@localhost -p {port} -s -g {config}' |
| 272 | .format(creds=creds, port=self.qemu.ssh_port, config=config)) | 390 | .format(creds=creds, port=self.qemu.ssh_port, config=config)) |
| 273 | 391 | ||
| 274 | # Verify that device HAS provisioned. | 392 | verifyProvisioned(self, machine) |
| 275 | ran_ok = False | ||
| 276 | for delay in [5, 5, 5, 5, 10]: | ||
| 277 | sleep(delay) | ||
| 278 | stdout, stderr, retcode = self.qemu_command('aktualizr-info') | ||
| 279 | if retcode == 0 and stderr == b'' and stdout.decode().find('Fetched metadata: yes') >= 0: | ||
| 280 | ran_ok = True | ||
| 281 | break | ||
| 282 | self.assertIn(b'Device ID: ', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 283 | self.assertIn(b'Primary ecu hardware ID: qemux86-64', stdout, | ||
| 284 | 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 285 | self.assertIn(b'Fetched metadata: yes', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 286 | p = re.compile(r'Device ID: ([a-z0-9-]*)\n') | ||
| 287 | m = p.search(stdout.decode()) | ||
| 288 | self.assertTrue(m, 'Device ID could not be read: ' + stderr.decode() + stdout.decode()) | ||
| 289 | self.assertGreater(m.lastindex, 0, 'Device ID could not be read: ' + stderr.decode() + stdout.decode()) | ||
| 290 | logger = logging.getLogger("selftest") | ||
| 291 | logger.info('Device successfully provisioned with ID: ' + m.group(1)) | ||
| 292 | 393 | ||
| 293 | 394 | ||
| 294 | class HsmTests(OESelftestTestCase): | 395 | class HsmTests(OESelftestTestCase): |
| 295 | 396 | ||
| 296 | def setUpLocal(self): | 397 | def setUpLocal(self): |
| 398 | layer = "meta-updater-qemux86-64" | ||
| 399 | result = runCmd('bitbake-layers show-layers') | ||
| 400 | if re.search(layer, result.output) is None: | ||
| 401 | # Assume the directory layout for finding other layers. We could also | ||
| 402 | # make assumptions by using 'show-layers', but either way, if the | ||
| 403 | # layers we need aren't where we expect them, we are out of like. | ||
| 404 | path = os.path.abspath(os.path.dirname(__file__)) | ||
| 405 | metadir = path + "/../../../../../" | ||
| 406 | self.meta_qemu = metadir + layer | ||
| 407 | runCmd('bitbake-layers add-layer "%s"' % self.meta_qemu) | ||
| 408 | else: | ||
| 409 | self.meta_qemu = None | ||
| 410 | self.append_config('MACHINE = "qemux86-64"') | ||
| 297 | self.append_config('SOTA_CLIENT_PROV = "aktualizr-hsm-prov"') | 411 | self.append_config('SOTA_CLIENT_PROV = "aktualizr-hsm-prov"') |
| 298 | self.append_config('SOTA_CLIENT_FEATURES = "hsm"') | 412 | self.append_config('SOTA_CLIENT_FEATURES = "hsm"') |
| 299 | # note: this will build aktualizr-native as a side-effect | ||
| 300 | self.qemu, self.s = qemu_launch(machine='qemux86-64') | 413 | self.qemu, self.s = qemu_launch(machine='qemux86-64') |
| 301 | 414 | ||
| 302 | def tearDownLocal(self): | 415 | def tearDownLocal(self): |
| 303 | qemu_terminate(self.s) | 416 | qemu_terminate(self.s) |
| 417 | if self.meta_qemu: | ||
| 418 | runCmd('bitbake-layers remove-layer "%s"' % self.meta_qemu, ignore_status=True) | ||
| 304 | 419 | ||
| 305 | def qemu_command(self, command): | 420 | def qemu_command(self, command): |
| 306 | return qemu_send_command(self.qemu.ssh_port, command) | 421 | return qemu_send_command(self.qemu.ssh_port, command) |
| @@ -313,11 +428,11 @@ class HsmTests(OESelftestTestCase): | |||
| 313 | machine = get_bb_var('MACHINE', 'core-image-minimal') | 428 | machine = get_bb_var('MACHINE', 'core-image-minimal') |
| 314 | self.assertEqual(stderr, b'', 'Error: ' + stderr.decode()) | 429 | self.assertEqual(stderr, b'', 'Error: ' + stderr.decode()) |
| 315 | # Strip off line ending. | 430 | # Strip off line ending. |
| 316 | value_str = stdout.decode()[:-1] | 431 | value = stdout.decode()[:-1] |
| 317 | self.assertEqual(value_str, machine, | 432 | self.assertEqual(value, machine, |
| 318 | 'MACHINE does not match hostname: ' + machine + ', ' + value_str + | 433 | 'MACHINE does not match hostname: ' + machine + ', ' + value + |
| 319 | '\nIs tianocore ovmf installed?') | 434 | '\nIs tianocore ovmf installed?') |
| 320 | print(value_str) | 435 | print(value) |
| 321 | print('Checking output of aktualizr-info:') | 436 | print('Checking output of aktualizr-info:') |
| 322 | ran_ok = False | 437 | ran_ok = False |
| 323 | for delay in [0, 1, 2, 5, 10, 15]: | 438 | for delay in [0, 1, 2, 5, 10, 15]: |
| @@ -387,24 +502,7 @@ class HsmTests(OESelftestTestCase): | |||
| 387 | self.assertEqual(p11_m.group(1), hsm_m.group(1), 'Slot number does not match: ' + | 502 | self.assertEqual(p11_m.group(1), hsm_m.group(1), 'Slot number does not match: ' + |
| 388 | p11_err.decode() + p11_out.decode() + hsm_err.decode() + hsm_out.decode()) | 503 | p11_err.decode() + p11_out.decode() + hsm_err.decode() + hsm_out.decode()) |
| 389 | 504 | ||
| 390 | # Verify that device HAS provisioned. | 505 | verifyProvisioned(self, machine) |
| 391 | ran_ok = False | ||
| 392 | for delay in [5, 5, 5, 5, 10]: | ||
| 393 | sleep(delay) | ||
| 394 | stdout, stderr, retcode = self.qemu_command('aktualizr-info') | ||
| 395 | if retcode == 0 and stderr == b'' and stdout.decode().find('Fetched metadata: yes') >= 0: | ||
| 396 | ran_ok = True | ||
| 397 | break | ||
| 398 | self.assertIn(b'Device ID: ', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 399 | self.assertIn(b'Primary ecu hardware ID: qemux86-64', stdout, | ||
| 400 | 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 401 | self.assertIn(b'Fetched metadata: yes', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 402 | p = re.compile(r'Device ID: ([a-z0-9-]*)\n') | ||
| 403 | m = p.search(stdout.decode()) | ||
| 404 | self.assertTrue(m, 'Device ID could not be read: ' + stderr.decode() + stdout.decode()) | ||
| 405 | self.assertGreater(m.lastindex, 0, 'Device ID could not be read: ' + stderr.decode() + stdout.decode()) | ||
| 406 | logger = logging.getLogger("selftest") | ||
| 407 | logger.info('Device successfully provisioned with ID: ' + m.group(1)) | ||
| 408 | 506 | ||
| 409 | 507 | ||
| 410 | def qemu_launch(efi=False, machine=None): | 508 | def qemu_launch(efi=False, machine=None): |
| @@ -471,5 +569,25 @@ def akt_native_run(testInst, cmd, **kwargs): | |||
| 471 | testInst.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output) | 569 | testInst.assertEqual(result.status, 0, "Status not equal to 0. output: %s" % result.output) |
| 472 | 570 | ||
| 473 | 571 | ||
| 572 | def verifyProvisioned(testInst, machine): | ||
| 573 | # Verify that device HAS provisioned. | ||
| 574 | ran_ok = False | ||
| 575 | for delay in [5, 5, 5, 5, 10]: | ||
| 576 | sleep(delay) | ||
| 577 | stdout, stderr, retcode = testInst.qemu_command('aktualizr-info') | ||
| 578 | if retcode == 0 and stderr == b'' and stdout.decode().find('Fetched metadata: yes') >= 0: | ||
| 579 | ran_ok = True | ||
| 580 | break | ||
| 581 | testInst.assertIn(b'Device ID: ', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 582 | testInst.assertIn(b'Primary ecu hardware ID: ' + machine.encode(), stdout, | ||
| 583 | 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 584 | testInst.assertIn(b'Fetched metadata: yes', stdout, 'Provisioning failed: ' + stderr.decode() + stdout.decode()) | ||
| 585 | p = re.compile(r'Device ID: ([a-z0-9-]*)\n') | ||
| 586 | m = p.search(stdout.decode()) | ||
| 587 | testInst.assertTrue(m, 'Device ID could not be read: ' + stderr.decode() + stdout.decode()) | ||
| 588 | testInst.assertGreater(m.lastindex, 0, 'Device ID could not be read: ' + stderr.decode() + stdout.decode()) | ||
| 589 | logger = logging.getLogger("selftest") | ||
| 590 | logger.info('Device successfully provisioned with ID: ' + m.group(1)) | ||
| 591 | |||
| 474 | 592 | ||
| 475 | # vim:set ts=4 sw=4 sts=4 expandtab: | 593 | # vim:set ts=4 sw=4 sts=4 expandtab: |
