Freifunk CI - Ein Jahr später

Vor einem Jahr haben wir angekündigt uns mit Continuous Integration und Testing von Gluon auf echter Hardware beschäftigen zu wollen. Dieser Beitrag soll unsere bisherigen Ergebnisse zusammenfassen und einen aktuellen Ausblick geben.

Eines vorweg: Continuous Integration ist noch nicht gelöst. Aktuell bauen wir unsere Software händisch. Dieser Betrag beschreibt unseren Fortschritt beim Testing.

Hardware

Die von uns zusammengestellte Hardware ist stark von den folgenden Faktoren getrieben:

  • Wir brauchen bestimmte Funktionen, wie z.z. B.nbsp;B. Power Switches und Serial-Ports.
  • Die Hardware darf nicht teuer sein, um einfach weitere Instanzen aufbauen zu können.
  • Tests müssen vollständig automatisch ohne Eingriff eines Benutzers möglich sein.

Unsere Hardware besteht daher aus den folgenden Komponenten:

  • Test-Server: Ein RaspberryPi 3 mit Raspbian
  • Power Switch: Zum Schalten der Spannungsversorgung des Routers benutzen wir 8-fach Relaisboards aus dem Maker-Zubehör. Die Relais werden direkt von GPIOs des RaspberryPi gesteuert. Die Relais werden auf der Ausgangsseite der Netzteile der Router eingeschleift.
  • Knöpfe am Router drucken: zum Drücken von Knöpfen am Router benutzen wir ebenfalls Relais. Hierzu wird das Relais parallel zum Knopf des Routers angeschlossen.
  • Serial Port: Die Serielle Schnittstelle des Routers wird mit einem USB-Seriell-Wandler (auch aus dem Maker-Zubehör) per USB an den RaspberryPi angeschlossen. Diese Wandler sind auch direkt mit 3.3V Logik- Spannung verfügbar und daher ohne weitere Wandler einsetzbar.
  • Ethernet: Mindestens die Client-Seite des Routers muss vom RaspberryPi aus erreichbar sein. Wir schließen das Client-Netz daher direkt mit einem USB-Ethernet Adapter an.
  • Device under Test: Welche Hardware hier zum Einsatz kommt ist davon abhängig welche Hardware getestet werden soll. Wir haben unsere Tests mit einem TP-Link WR-841 gestartet. Im Wesentlichen haben wir diesen Router gewählt, weil der finanzielle Schaden so gering ist, falls wir den Router in der Entwicklung zerstören.

Die folgenden Bilder sollen einen Überblick über unseren Aufbau geben:

Test-Server mit Relaisboard zum Schalten der Spannungsversorgung
Test-Server mit Relaisboard zum Schalten der Spannungsversorgung
Gesamter Aufbau: Oben Test-Server und unten Device-under-Test
Gesamter Aufbau: Oben Test-Server und unten Device-under-Test

Software

Unsere Software besteht aus den folgenden Teilen:

  • Betriebssystem auf dem Testserver
  • Test-Automation
  • Wissen, wie der Router zu bedienen ist
  • Tests

Betriebssystem

Als Betriebssystem auf dem Test-Server kommt ein Stock Raspbian zum Einsatz. Somit steht uns auf dem Test-Server alle Werkzeuge eines normalen Linux zur Verfügung.

Wesentliche Änderungen am Betriebssystem sind:

  • Hardware-Initialisierung: Um GPIOs nutzen zu können müssen die GPIOs exportiert werden. Die Tests gehen davon aus, dass vom System durchgeführt wurde. Hierfür können z. B. Systemd-Units zum Einsatz kommen.
  • Benutzer: Für alle Braunschweiger Freifunker, die an dieser Hardware arbeiten werden eigene Benutzer angelegt.

Test-Automation

Als Testautomatisierung kommt labgrid zum Einsatz. Labgrid bietet die Infrastruktur, die notwendig ist, um ein Embedded Device in einen bestimmten Zustand zu bringen und anschließend auf diesem Gerät Befehle ausführen zu können.

Labgrid wurde zuvor nicht in einer low-cost Umgebung eingesetzt. Daher mussten einige zusätzliche Treiber entwickelt werden:

  • SmallUbootDriver: Der Bootloader auf den günstigen TP-Link Routern ist, im Gegensatz zu Bootloadern auf größeren Embedded-Devices, sehr minimalistisch. Darüber hinaus verhält sich der Bootloader anders, als andere UBoot-basierte Bootloader. Der SmallUbootDriver ist in der Lage diesen Bootloader zu steuern.
  • SysfsDigitalOutput: Um die GPIOs auf dem RaspberryPi nutzen zu können ist ein Treiber für diese notwendig. Dieser wird von SysfsDigitalOutput bereit gestellt.

Hardwarewissen

Hardwarewissen, sowie die Tests sind im Git-Repository ffbs-ci zusammengefasst.

Um die Hardware ansteuern und somit den Zustand des Device under Test steuern zu können muss labgrid Wissen über Hardware und Software haben.

Das Wissen über die vorhandene Hardware ist im local.yaml abgelegt. Dort ist beschrieben, wie die serielle Schnittstelle erreicht wird, welche GPIOs für die Steuerung der Spannungsversorgung und des Reset-Buttons verwendet werden sollen. Darüber hinaus können dort Konfigurationen für die verwendeten Treiber angegeben werden.

Auch wird dort festgelegt welche Strategy labgrid zur Steuerung der Hardware benutzt. In einer Strategy ist das Wissen abgelegt, wie das DUT in Zustände gebracht werden kann. Im Fall des TP-Link WR-841 sind die folgenden und einige weitere) Zustände in der SmallUbootStrategy definiert:

  • uboot: Das System befindet sich in einer UBoot-Konsole.
  • good_config: Das System wurde mit einer als funktionierend bekannten Firmware per TFTP geflashed und befindet sich nun im Config-Mode.
  • good_running: Das System hat den Config-Mode verlassen und befindet sich nun im normalen Betrieb.
  • new_running: Eine neue Firmware wurde über den Auto-Updater des als funktionierend bekannten Systems geflashed und befindet sich nun im normalen Betrieb.

Tests

Labgrid kommt mit einem Plugin für pytest. Die Tests sind daher für pytest geschrieben. Unsere Tests befinden sich aktuell in test_uboot_strategy.py.

Um das Schreiben von Tests zu vereinfachen setzen wir auf pytest-fixtures: Erwartet ein Test einen Parameter und es gibt eine Fixture mit diesem Namen, so gibt pytest die Fixture als Parameter mit in den Test.

Für unsere Tests sind Fixtures definiert, die das DUT vor einem Test in die Zustände der Strategy bringen. Die folgende Fixture bringt das DUT in den Zustand uboot, also den Bootloader.

1
2
3
@pytest.fixture(scope="function")
def in_uboot(strategy):
    strategy.transition("uboot")

Der folgende Test prüft dann, ob der Bootloader überhaupt funktioniert. Dieser Test ist eher als ein vorbereitender Test zu verstehen.

1
2
3
4
5
6
7
def test_uboot(target, in_uboot):
    command = target.get_driver('UBootDriver')
    stdout, stderr, returncode = command.run('version')
    assert returncode == 0
    assert len(stdout) > 0
    assert len(stderr) == 0
    assert 'U-Boot' in '\n'.join(stdout)

Der folgende Test hingegen prüft, ob im Config-Mode, die vorgeschlagenen Koordinaten korrekt sind. Wir ändern diese Koordinaten in unserem Gluon auf eine Koordinate in Braunschweig, (etwas unauffälliger Lokalpatriotismus) daher ist es sinnvoll zu prüfen, ob diese im Image auch korrekt abgelegt werden.

1
2
3
4
5
6
7
8
def assert_web(url, text_to_find):
    r = requests.get(url)
    assert r.status_code == 200, "Trying to get url {} failed with status code {}. Was looking for text '{}' there...".format(url, r.status_code, text_to_find)
    assert text_to_find in r.text, "Could not find '{}' in {}. Site returned:\n{}".format(text_to_find, url, r.text)

def test_good_config_default_coordinates(target, in_good_config):
    assert_web("http://192.168.1.1/cgi-bin/config/wizard", "10.52378")
    assert_web("http://192.168.1.1/cgi-bin/config/wizard", "52.26469")

Testabdeckung

Aktuell befinden sich in unserer Sammlung insgesamt 73 Tests. Mit diesen Tests sind wir in der Lage das Update eines Routers von einer als funktionierend bekannten Version auf eine neue Version zu testen und in dieser neuen Version Tests durchzuführen.

pytest mit Tests
pytest mit Tests

Die Tests beschränken sich dabei aktuell auf Tests der für Freifunk Braunschweig spezifischen Erweiterungen, sowie der grundsätzlichen Überprüfung der Konnektivität zum Freifunk Braunschweig Netzwerk.

Hierbei werden viele für den Benutzer wahrnehmbare Funktionen, wie z. B. das Aussehen und der Funktionsumfang der Weboberfläche der Router noch gar nicht getestet. Hier ist also durchaus noch Luft nach oben.

Ausblick

Mit den nächsten Schritten soll es nun in zwei Richtungen weiter gehen.

Zum Einen soll das Freifunk-Lab um weitere Router erweitert und die Testsammlung auf diese Router angepasst werden. Hierbei soll besonders auf andere Architekturen geachtet werden.

Zum Anderen soll eine wirkliche Continuous Integration erreicht werden: Werden Änderungen auf eines der Freifunk Braunschweig Repositories gepusht, so sollen diese automatisch gebaut und auf der Hardware getestet werden.

Anschließend ist denkbar dies ebenfalls für den Gluon Upstream zu tun. Dies könnte die Gluon-Entwickler mit zeitnahen Smoke Tests ihrer Änderungen versorgen.

Mitarbeit an diesen Themen ist gern gesehen. Freifunk Braunschweig ist unter Kontakt zu erreichen.