From 97863ff13fda00ec21af6354af812538046e0edf Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Mon, 29 Jan 2024 12:09:14 +0100 Subject: [PATCH 1/2] testutils/iotlab.py: Randomize the selected node It seems there have been some failures mainly due to infrastructure. Specifically the samr21-xpro failing to flash will cause many reruns with the same faulty hardware. Previously it would just take the first available node in the list, which is deterministic but doesn't help with flakey test reruns. This may cause an issue with distance to other nodes, but if random selection of nodes becomes a problem we would have to introduce node pairing lists... Which is a bit more work. This is at least a first step. --- testutils/iotlab.py | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/testutils/iotlab.py b/testutils/iotlab.py index 49d5fdd..ebde28d 100644 --- a/testutils/iotlab.py +++ b/testutils/iotlab.py @@ -1,4 +1,5 @@ import logging +import random import re from iotlabcli.auth import get_user_credentials @@ -9,6 +10,7 @@ stop_experiment, get_experiment, exp_resources, + info_experiment, AliasNodes, ) @@ -151,6 +153,25 @@ def _wait(self): ret = wait_experiment(Api(*self.user_credentials()), self.exp_id) return ret + def _select_random_node(self, site, board): + api = Api(*self.user_credentials()) + info = info_experiment(api, site=site) + choices = [] + for iot_lab_nodes in info.values(): + if not isinstance(iot_lab_nodes, list): + continue + for iot_lab_node in iot_lab_nodes: + if iot_lab_node.get("state", "") != "Alive": + continue + net_addr = iot_lab_node.get("network_address", "") + if board not in net_addr: + continue + choices.append(net_addr) + if not choices: + raise RuntimeError(f"No {board} found at {site}") + ret = random.choice(choices) + return ret + def _submit(self, site, duration): """Submit an experiment with required nodes""" api = Api(*self.user_credentials()) @@ -159,9 +180,19 @@ def _submit(self, site, duration): if ctrl.env.get('IOTLAB_NODE') is not None: resources.append(exp_resources([ctrl.env.get('IOTLAB_NODE')])) elif ctrl.board() is not None: - board = IoTLABExperiment._archi_from_board(ctrl.board()) - alias = AliasNodes(1, site, board) - resources.append(exp_resources(alias)) + # Since we cannot combine alias and phyical nodes in the same + # experiment and but we would prefer to use iotlab-m3 alias + # nodes because the m3 nodes can report that they are broken. + # Let's take the easiest solution and only use alias nodes + # if they are all iotlab-m3 nodes. + if all(ctrl.board() == 'iotlab-m3' for ctrl in self.ctrls): + board = IoTLABExperiment._archi_from_board(ctrl.board()) + alias = AliasNodes(1, site, board) + resources.append(exp_resources(alias)) + else: + board = IoTLABExperiment.BOARD_ARCHI_MAP[ctrl.board()]['name'] + net_addr = self._select_random_node(site, board) + resources.append(exp_resources([net_addr])) else: raise ValueError("neither BOARD or IOTLAB_NODE are set") return submit_experiment(api, self.name, duration, resources)['id'] From fc5280de6107e3edd9582fc1d8b14983100e3740 Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Wed, 31 Jan 2024 12:56:32 +0100 Subject: [PATCH 2/2] test_iotlab: Add test for random feature --- testutils/tests/test_iotlab.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/testutils/tests/test_iotlab.py b/testutils/tests/test_iotlab.py index 7223e95..3c761f0 100644 --- a/testutils/tests/test_iotlab.py +++ b/testutils/tests/test_iotlab.py @@ -177,6 +177,7 @@ def test_stop(monkeypatch, exp_id, expected): "ctrl_envs, exp_nodes", [ ([{"BOARD": "nrf52dk"}], ["nrf52dk-5.saclay.iot-lab.info"]), + ([{"BOARD": "iotlab-m3"}], ["m3-3.saclay.iot-lab.info"]), ( [{"IOTLAB_NODE": "samr21-21.saclay.iot-lab.info"}], ["samr21-21.saclay.iot-lab.info"], @@ -199,6 +200,18 @@ def test_start(monkeypatch, ctrl_envs, exp_nodes): monkeypatch.setattr( testutils.iotlab, "get_experiment", lambda api, exp_id: {"nodes": exp_nodes} ) + monkeypatch.setattr( + testutils.iotlab, + "info_experiment", + lambda api, site: { + "items": [ + { + "state": "Alive", + "network_address": exp_nodes[0], + }, + ], + }, + ) monkeypatch.setattr(testutils.iotlab, "wait_experiment", lambda api, exp_id: {}) ctrls = [MockRIOTCtrl(env) for env in ctrl_envs] exp = testutils.iotlab.IoTLABExperiment("test", ctrls)