FIT/IoT-LAB tutorials#

This tutorial illustrates the use of EnOSlib to interact with FIT/IoT-LAB testbed.

Hint

For a complete schema reference see FIT/IoT-LAB Schema

Installation#

We strongly suggest to use a virtual environment to install and run your experiments.

$ python3 -m venv venv/
$ source venv/bin/activate
$ pip install -U pip
$ pip install enoslib[iotlab]

Configuration#

The Iotlab provider is built on top of 2 libraries provided by the FIT/IoT-LAB platform: cli-tools (iot-lab/cli-tools) and ssh-cli-tools (iot-lab/ssh-cli-tools). Underlying, these tools use the REST API to access and manipulate the platform.

To access the REST API, user must be authenticated. These tools relies on the configuration file “.iotlabrc” located at the home directory. It can be created by using the CLI tools, so inside the virtual environment:

iotlab-auth -u USERNAME -p PASSWORD

For more details, read the documentation of iotlab-auth (iotlab-auth -h).

External access#

Pre-requisites: Configure your SSH access to the FIT/IoT-LAB platform (https://www.iot-lab.info/legacy/tutorials/ssh-access/index.html).

To be able to access the A8 nodes from your PC, it’s necessary to configure the proxy ssh accordingly.

  • Configure you ~/.ssh/config properly, example:

Host *.grenoble.iot-lab.info
ProxyCommand ssh -A <user>@grenoble.iot-lab.info -W "$(basename %h):%p"
User <user>
ForwardAgent yes

Remember to set the site used (e.g. grenoble).

Basic examples#

Getting started tutorial#

This script implements a similar behavior as the getting started tutorial from FIT/IoT-LAB (https://www.iot-lab.info/legacy/tutorials/getting-started-tutorial/index.html).

Requirement: M3 image (tutorial_m3.elf)

 1import logging
 2import threading
 3import time
 4
 5import enoslib as en
 6
 7en.init_logging(level=logging.INFO)
 8en.check()
 9
10stop_reading = False
11
12
13def reading_thread(serial):
14    """
15    Reading thread. Continuously read the serial from node.
16
17    Necessary since the getting started tutorial supposes a user interaction
18    to send and receive packets at the same time.
19    """
20    while not stop_reading:
21        print(serial.read())
22
23
24# IoT-LAB provider configuration
25provider_conf = {
26    "walltime": "01:00",
27    "resources": {
28        "machines": [
29            {
30                "roles": ["sensor", "sender"],
31                "archi": "m3:at86rf231",
32                "site": "grenoble",
33                "number": 1,
34                "image": "tutorial_m3.elf",
35            },
36            {
37                "roles": ["sensor", "receiver"],
38                "archi": "m3:at86rf231",
39                "site": "grenoble",
40                "number": 1,
41                "image": "tutorial_m3.elf",
42            },
43        ]
44    },
45}
46
47conf = en.IotlabConf.from_dictionary(provider_conf)
48
49p = en.Iotlab(conf)
50try:
51    roles, networks = p.init()
52    print(roles)
53
54    sender = roles["sender"][0]
55    receiver = roles["receiver"][0]
56    with en.IotlabSerial(sender, interactive=True) as s_sender, en.IotlabSerial(
57        receiver, interactive=True
58    ) as s_receiver:
59
60        # Initializing read thread
61        print("Initializing reading thread")
62        read_thread = threading.Thread(target=reading_thread, args=(s_receiver,))
63        read_thread.start()
64        time.sleep(1)
65
66        # Sending packets
67        print("Sensor (%s) sending small packet" % sender.address)
68        s_sender.write("s")
69        time.sleep(1)
70        print("Sensor (%s) sending big packet" % sender.address)
71        s_sender.write("b")
72        time.sleep(1)
73
74        stop_reading = True
75        read_thread.join()
76
77except Exception as e:
78    print(e)
79finally:
80    # Delete testbed reservation
81    p.destroy()

Note

The test creates a thread to read serial while the test ask the sensor to send messages. We don’t recommend doing this in your tests. Anyway, this is necessary in this example since the getting started tutorial supposes the user interaction.

  • You can launch the script using :

    $ python tuto_iotlab_getting_started.py
    

Using A8 nodes#

This script shows how to use the A8 node available in the platform. They have a Linux OS installed, so we can access them through SSH and run simple linux commands.

 1import json
 2import logging
 3import sys
 4
 5import enoslib as en
 6
 7en.init_logging(level=logging.INFO)
 8en.check()
 9
10# IoT-LAB provider configuration
11provider_conf = {
12    "walltime": "01:00",
13    "resources": {
14        "machines": [
15            {
16                "roles": ["my_a8"],
17                "archi": "a8:at86rf231",
18                "site": "grenoble",
19                "number": 2,
20            }
21        ]
22    },
23}
24
25conf = en.IotlabConf.from_dictionary(provider_conf)
26
27p = en.Iotlab(conf)
28try:
29    roles, networks = p.init()
30    roles = en.sync_info(roles, networks)
31    print(roles)
32    print("A8 nodes have a simple linux OS. We can run 'date' command in them.")
33    result = en.run_command(command="date", pattern_hosts="my_a8*", roles=roles)
34    print("Results:")
35    json.dump(result["ok"], sys.stdout, indent=2)
36    json.dump(result["error"], sys.stderr, indent=2)
37
38except Exception as e:
39    print(e)
40finally:
41    # Delete testbed reservation
42    p.destroy()

Note

Note that the Linux version installed on nodes has limited capabilities.

  • You can launch the script using :

    $ python tuto_iotlab_a8_basic.py
    

Using RPi nodes#

This script shows how to use the RPI node available in the platform. They have a Linux OS installed, so we can access them through SSH and run simple linux commands.

 1import json
 2import logging
 3import sys
 4
 5import enoslib as en
 6
 7en.init_logging(level=logging.INFO)
 8en.check()
 9
10# IoT-LAB provider configuration
11provider_conf = {
12    "walltime": "01:00",
13    "resources": {
14        "machines": [
15            {
16                "roles": ["my_rpi"],
17                "archi": "rpi3:at86rf233",
18                "site": "grenoble",
19                "number": 2,
20            }
21        ]
22    },
23}
24
25conf = en.IotlabConf.from_dictionary(provider_conf)
26
27p = en.Iotlab(conf)
28roles, networks = p.init()
29roles = en.sync_info(roles, networks)
30print(roles)
31print("RPis nodes have a simple linux OS. We can run 'date' command in them.")
32result = en.run_command(command="date", roles=roles)
33print("Results:")
34json.dump(result.to_dict(), sys.stdout, indent=2)

Note

Note that the Linux version installed on nodes has limited capabilities.

  • You can launch the script using :

    $ python tuto_iotlab_rpi_basic.py
    

Advanced examples#

Monitor M3 consumption#

Simple example of using the monitoring tools in FIT/IoT-LAB testbed.

This script implements a similar behavior as the “Monitor the consumption of M3 node during an experiment” (https://www.iot-lab.info/legacy/tutorials/monitoring-consumption-m3/index.html).

Requirement: M3 image (tutorial_m3.elf)

 1import logging
 2import time
 3
 4import enoslib as en
 5
 6en.init_logging(level=logging.INFO)
 7en.check()
 8
 9provider_conf = {
10    "walltime": "01:00",
11    "resources": {
12        "machines": [
13            {
14                "roles": ["sensor"],
15                "archi": "m3:at86rf231",
16                "site": "grenoble",
17                "number": 1,
18                "image": "tutorial_m3.elf",
19                "profile": "test_profile",
20            },
21        ]
22    },
23    "monitoring": {
24        "profiles": [
25            {
26                "name": "test_profile",
27                "archi": "m3",
28                "consumption": {
29                    "current": True,
30                    "power": True,
31                    "voltage": True,
32                    "period": 8244,
33                    "average": 4,
34                },
35            }
36        ]
37    },
38}
39
40conf = en.IotlabConf.from_dictionary(provider_conf)
41
42p = en.Iotlab(conf)
43try:
44
45    roles, networks = p.init()
46    print(roles)
47
48    print("Running experiment for 60s")
49    time.sleep(60)
50
51    print("Collecting experiment data")
52    p.collect_data_experiment()  # collect experiment data
53
54except Exception as e:
55    print(e)
56finally:
57    # Delete testbed reservation
58    p.destroy()

Note

The monitoring files are compressed and saved in the current folder.

  • You can launch the script using :

    $ python tuto_iotlab_m3_consumption.py
    
  • Finally, you can evaluate and plot the results as done in the tutorial. It uses the plot_oml_consum tool (Available at: iot-lab/oml-plot-tools).

    $ tar xfz <expid>-grenoble.iot-lab.info.tar.gz
    $ plot_oml_consum -p -i <expid>/consumption/m3_<id>.oml
    

Radio Monitoring for M3 nodes#

Simple example of using the monitoring tools in FIT/IoT-LAB testbed.

This script implements a similar behavior as the “Radio monitoring for M3 nodes” (https://www.iot-lab.info/legacy/tutorials/monitoring-radio-m3/index.html).

Requirement: M3 image (tutorial_m3.elf)

  • Please follow the steps 1-4 in the FIT/IoT-LAB tutorial to create the image.

  • Save it in the same folder as the script.

  • Adjust the channels (11, 14) used during the test accordingly (see step 5 in the FIT/IoT-LAB tutorial).

 1import logging
 2import time
 3
 4import enoslib as en
 5
 6en.init_logging(level=logging.INFO)
 7en.check()
 8
 9provider_conf = {
10    "walltime": "01:00",
11    "resources": {
12        "machines": [
13            {
14                "roles": ["sensor"],
15                "archi": "m3:at86rf231",
16                "site": "grenoble",
17                "number": 2,
18                "image": "tutorial_m3.elf",
19                "profile": "test_profile",
20            },
21        ]
22    },
23    "monitoring": {
24        "profiles": [
25            {
26                "name": "test_profile",
27                "archi": "m3",
28                "radio": {
29                    "mode": "rssi",
30                    "num_per_channel": 1,
31                    "period": 1,
32                    "channels": [11, 14],
33                },
34            }
35        ]
36    },
37}
38
39conf = en.IotlabConf.from_dictionary(provider_conf)
40
41p = en.Iotlab(conf)
42try:
43
44    roles, networks = p.init()
45    print(roles)
46
47    print("Opening serial connection to sensor")
48    sender = roles["sensor"][0]
49
50    with en.IotlabSerial(sender, interactive=True) as serial_sender:
51        num_packets = 5
52        num_burst = 5
53        print(
54            "M3 sensor(%s): sending %d sets of %d packets"
55            % (sender.alias, num_burst, num_packets)
56        )
57        for i in range(0, num_burst):
58            for j in range(0, num_packets):
59                serial_sender.write("b")
60            time.sleep(5)
61
62    print("Collecting experiment data")
63    time.sleep(20)
64    p.collect_data_experiment()  # collect experiment data
65
66except Exception as e:
67    print(e)
68finally:
69    # Delete testbed reservation
70    p.destroy()

Note

The monitoring files are compressed and saved in the current folder.

  • You can launch the script using :

    $ python tuto_iotlab_m3_radio_monitoring.py
    
  • Finally, you can evaluate and plot the results as done in the tutorial. It uses the plot_oml_consum tool (Available at: iot-lab/oml-plot-tools).

    $ tar xfz <expid>-grenoble.iot-lab.info.tar.gz
    $ plot_oml_radio -a -i <expid>/radio/m3_<id>.oml
    

Radio Sniffer with M3 nodes#

Simple example of using the monitoring tools in FIT/IoT-LAB testbed.

This script implements a similar behavior as the “Radio sniffer with M3 nodes” (https://www.iot-lab.info/legacy/tutorials/monitoring-sniffer-m3/index.html).

Requirement: M3 image (tutorial_m3.elf)

 1import contextlib
 2import logging
 3import time
 4
 5import enoslib as en
 6
 7en.init_logging(level=logging.INFO)
 8en.check()
 9
10provider_conf = {
11    "walltime": "01:00",
12    "resources": {
13        "machines": [
14            {
15                "roles": ["sniffer"],
16                "hostname": ["m3-7.grenoble.iot-lab.info"],
17                "image": "tutorial_m3.elf",
18                "profile": "sniff_11",
19            },
20            {
21                "roles": ["sensor"],
22                "hostname": [
23                    "m3-8.grenoble.iot-lab.info",
24                    "m3-9.grenoble.iot-lab.info",
25                    "m3-10.grenoble.iot-lab.info",
26                    "m3-11.grenoble.iot-lab.info",
27                ],
28                "image": "tutorial_m3.elf",
29            },
30        ]
31    },
32    "monitoring": {
33        "profiles": [
34            {
35                "name": "sniff_11",
36                "archi": "m3",
37                "radio": {
38                    "mode": "sniffer",
39                    "channels": [11],
40                },
41            }
42        ]
43    },
44}
45
46conf = en.IotlabConf.from_dictionary(provider_conf)
47
48p = en.Iotlab(conf)
49try:
50
51    roles, networks = p.init()
52    print(roles)
53
54    sniffer = roles["sniffer"][0]
55    sensor = roles["sensor"][0]
56
57    with contextlib.ExitStack() as stack:
58        stack.enter_context(en.IotlabSniffer(sniffer))
59        s_sniffer = stack.enter_context(en.IotlabSerial(sniffer, interactive=True))
60        s_sensor = stack.enter_context(en.IotlabSerial(sensor, interactive=True))
61
62        print("Send a small packet with the sniffer (%s)" % sniffer.alias)
63        s_sniffer.write("s")
64        print("Send a big packet with another node (%s)" % sensor.alias)
65        s_sensor.write("b")
66
67    print("Collecting experiment data")
68    time.sleep(1)
69    p.collect_data_experiment()  # collect experiment data
70
71except Exception as e:
72    print(e)
73finally:
74    # Delete testbed reservation
75    p.destroy()

Note

The pcap file is compressed and saved in the current folder.

  • You can launch the script using :

    $ python tuto_iotlab_m3_radio_sniffer.py
    
  • Finally, you can analyze the packets with wireshark.

    $ tar xfz <expid>-grenoble.iot-lab.info.tar.gz
    $ wireshark <expid>/sniffer/m3-7.pcap
    

IPv6 - Interacting with frontend#

Some tutorials in FIT/IoT-LAB run commands directly in the frontend node. You can script this interaction using a combination of Static and IoT-LAB providers in EnOSlib.

This script implements a similar behavior as the “Public IPv6 (6LoWPAN/RPL) network with M3 nodes” (https://www.iot-lab.info/legacy/tutorials/contiki-public-ipv6-m3/index.html). This tutorial runs the tunslip tool in the frontend node to bridge packets between the external network and M3 sensors in the platform.

Requirement: M3 images (border-router.iotlab-m3 and http-server.iotlab-m3)

  • Please follow Step 4 in the original tutorial to download the images.

 1import logging
 2import re
 3
 4import iotlabcli.auth
 5
 6import enoslib as en
 7
 8en.init_logging(level=logging.INFO)
 9en.check()
10
11provider_conf = {
12    "walltime": "01:00",
13    "resources": {
14        "machines": [
15            {
16                "roles": ["border_router"],
17                "archi": "m3:at86rf231",
18                "site": "grenoble",
19                "number": 1,
20                "image": "border-router.iotlab-m3",
21            },
22            {
23                "roles": ["http_server"],
24                "archi": "m3:at86rf231",
25                "site": "grenoble",
26                "number": 9,
27                "image": "http-server.iotlab-m3",
28            },
29        ]
30    },
31}
32
33# gets the username for accessing the IoT-LAB platform from ~/.iotlabrc file
34# you probably can set it hardcoded
35
36iotlab_user, _ = iotlabcli.auth.get_user_credentials()
37
38conf = en.IotlabConf.from_dictionary(provider_conf)
39
40p = en.Iotlab(conf)
41
42try:
43    roles, networks = p.init()
44
45    print("Iotlab provider: M3 resources")
46    print(roles)
47
48    frontend = en.Host("grenoble.iot-lab.info", user=iotlab_user)
49
50    border = roles["border_router"][0]
51
52    tun_cmd = (
53        "sudo tunslip6.py -v2 -L "
54        "-a %s -p 20000 2001:660:5307:3100::1/64 > tunslip.output 2>&1" % border.alias
55    )
56
57    print(f"Running {tun_cmd} command on frontend: {frontend.alias}")
58    en.run_command(tun_cmd, roles=frontend, asynch=3600, poll=0)
59
60    # resetting border router
61    border.reset()
62
63    # getting server addr from tunslip output saved in file
64    result = en.run_command("cat tunslip.output", roles=frontend)
65    out = result["ok"]["grenoble"]["stdout"]
66    match = re.search(
67        r"Server IPv6 addresses:\n.+(2001:660:5307:3100::\w{4})",
68        out,
69        re.MULTILINE | re.DOTALL,
70    )
71    server_addr = match.groups()[0]
72    print("Get the Border Router IPv6 address from tunslip output: %s" % server_addr)
73
74    # ping
75    print("Ping6 BR node using global address:")
76    result = en.run_command("ping6 -c3 %s" % server_addr, roles=frontend)
77    print(result["ok"]["grenoble"]["stdout"])
78
79    # get list of nodes from web interface
80    print("View BR’s web-interface: ")
81    result = en.run_command("lynx --dump http://[%s]" % server_addr, roles=frontend)
82    print(result["ok"]["grenoble"]["stdout"])
83
84    print("Stopping tunslip process")
85    en.run_command('pgrep -u "$(whoami)" tunslip6 | xargs kill', roles=frontend)
86
87except Exception as e:
88    print(e)
89finally:
90    # Delete testbed reservation
91    p.destroy()

Note

IPv6 address used in M3 sensors are globally accessible.

  • You can launch the script using :

    $ python tuto_iotlab_m3_ipv6.py
    

Monitoring A8 nodes - IPv6#

This example shows how to install a monitoring stack (Grafana/Prometheus/Telegraf) on Grid’5000 and FIT/IoT-LAB nodes to collect infrastructure metrics. By using the IPv6 network and Prometheus, we’re able to collect data from both testbed. Note that IPv6 connection from Grid’5000 to IoT-LAB is allowed (the inverse isn’t true unless you open the firewall port accordingly).

Summarizing, the example does the following:

  • Install Grafana in a node: Grafana accepts connections in the port 3000, user: admin, password: admin.

  • Install Prometheus database in a node: accessible at port 9090. It is installed with the following parameters (scrape_interval: 10s, scrape_timeout: 10s, eval_interval: 15s). They are defined at: enoslib/service/monitoring/roles/prometheus/defaults/main.yml).

  • Install Telegraf on remaining nodes: Telegraf is configured to receive incoming connections from Prometheus at port 9273.

  1import logging
  2import time
  3from ipaddress import IPv6Network
  4
  5import enoslib as en
  6
  7en.init_logging(level=logging.INFO)
  8en.check()
  9
 10# IoT-LAB provider configuration
 11iotlab_dict = {
 12    "walltime": "01:00",
 13    "resources": {
 14        "machines": [
 15            {
 16                "roles": ["a8"],
 17                "archi": "a8:at86rf231",
 18                "site": "grenoble",
 19                "number": 2,
 20            }
 21        ]
 22    },
 23}
 24iotlab_conf = en.IotlabConf.from_dictionary(iotlab_dict)
 25iotlab_provider = en.Iotlab(iotlab_conf)
 26
 27g5k_dict = {
 28    "job_type": [],
 29    "walltime": "01:00:00",
 30    "resources": {
 31        "machines": [
 32            {
 33                "roles": ["control"],
 34                "cluster": "parasilo",
 35                "nodes": 1,
 36                "primary_network": "default",
 37            },
 38            {
 39                "roles": ["compute"],
 40                "cluster": "parasilo",
 41                "nodes": 1,
 42                "primary_network": "default",
 43            },
 44        ],
 45        "networks": [
 46            {"id": "default", "type": "prod", "roles": ["prod"], "site": "rennes"}
 47        ],
 48    },
 49}
 50g5k_conf = en.G5kConf.from_dictionary(g5k_dict)
 51g5k_provider = en.G5k(g5k_conf)
 52
 53
 54try:
 55    iotlab_roles, iotlab_networks = iotlab_provider.init()
 56    g5k_roles, g5k_networks = g5k_provider.init()
 57
 58    print("Enabling IPv6 on Grid'5000 nodes")
 59    en.run_command("dhclient -6 br0", roles=g5k_roles)
 60
 61    g5k_roles = en.sync_info(g5k_roles, g5k_networks)
 62    iotlab_roles = en.sync_info(iotlab_roles, iotlab_networks)
 63
 64    print("Deploy monitoring stack on Grid'5000")
 65    print("Install Grafana and Prometheus at: %s" % str(g5k_roles["control"]))
 66    print("Install Telegraf at: %s" % str(g5k_roles["compute"]))
 67
 68    def get_nets(networks, net_type):
 69        """Aux method to filter networs from roles"""
 70        return [
 71            n
 72            for net_list in networks.values()
 73            for n in net_list
 74            if isinstance(n.network, net_type)
 75        ]
 76
 77    m = en.TPGMonitoring(
 78        collector=g5k_roles["control"][0],
 79        agent=g5k_roles["compute"] + iotlab_roles["a8"],
 80        ui=g5k_roles["control"][0],
 81        networks=get_nets(g5k_networks, IPv6Network)
 82        + get_nets(iotlab_networks, IPv6Network),
 83    )
 84    m.deploy()
 85
 86    ui_address = g5k_roles["control"][0].address
 87    print("The UI is available at http://%s:3000" % ui_address)
 88    print("user=admin, password=admin")
 89
 90    sleep_time = 60
 91    print("Sleeping for %d seconds before finishing the test" % sleep_time)
 92    time.sleep(sleep_time)
 93    m.backup()
 94    m.destroy()
 95
 96except Exception as e:
 97    print(e)
 98finally:
 99    # Delete testbed reservation
100    iotlab_provider.destroy()
101    g5k_provider.destroy()

Note

The Prometheus database is compressed and saved in the current folder.

  • You can launch the script using :

    $ python tuto_iotlab_ipv6_monitoring.py
    

Jupyter Notebooks#

Grid5000 and FIT/IoT-LAB - IPv6#