Developer documentation  ‹ 11. Conventions and pitfalls  › 13. The autotest: the suites in two kvm

12. Writing a demo

The rules every file of demos/ follows. debug/check_demos.py enforces the ones a source shows; debug/demo_test.py runs the demos.

12.1. The tree and the packages

demos/<area>/<demo>.py, one level, python only; windows.py, freebsd.py and cisco.py at the top. A new demo goes into an existing area:

cloonix       the objects cloonix provides
switching     how a frame finds a port
routing       how a packet finds a way
address       who gets an address, what a name resolves to
security      tunnels, filters, and who is let in
services      the machines in the middle
availability  staying up when one of them stops
watching      reading what happened

Two files are not demos. The first is demos/packages: the packages of the chapter Learning with cloonix of the user book, number and title, each followed by its demos with the line the chapter gives them:

4 Routing
routing/frrspider.py         sixty routers, OSPF
routing/frrspider_bgp.py     the same sixty, BGP

Play in the Demo panel of the canvas fetches that file and shows it as it is: the reader of the book finds on the canvas the packages of the book. A new demo is added to a package in learn.rst and to the same place in demos/packages; check_demos.py refuses a demo listed in neither or not once, and any difference between the two files in packages, titles, demos, order or lines.

The second is demos/voice: for each demo, the phrase the Demo panel says when the demo starts, a title and the item it shows, written as it is to be spoken:

routing/frrspider.py         O S P F. Sixty routers learn their routes.

debug/demo_voice.py makes each phrase a clip, audio/<area>/<demo>.ogg of jscloonix; the sound off button of the panel silences it, sound on says the phrase again. check_demos.py refuses a demo without its line.

The docstring at the top of a demo holds the network as ascii art, the acts, the image, the packet to catch. Outside cloonix/, a HISTORY constant follows ROOTFS_CHOICES and is the first thing main() tells with story(); the same paragraph heads the section of the demo in the chapter of its area, xdoc_user/doc/demos_<area>.rst.

12.2. The console and the history

A demo writes two things, to two places:

the console   show(): the cloonix commands, one after the other. A
              terminal, or the log of the Demo panel.
the history   story(): HISTORY, the text of each step, what an act
              proved. The History popup of the canvas only.

The Demo panel sets CLOONIX_DEMO_STORY=1; the demo then writes each line of history with the mark #story# in front, the panel sends the marked lines to the popup and the others to the log. From a terminal the variable is not set and story() writes nothing. A refusal or a failure goes to stderr and shows in both. demo_test.py sets the variable, and check_demos.py refuses a print() outside show() and pause().

What the popup makes of the history:

step("...")          a new section, numbered and timed, a chip in the
                     table of contents; "act 2: ..." is shown as an act
the lines after it   the text of the step, kept as written, ascii art
                     included: why the step, what was measured, what it
                     proves
a cloonix command    a line that begins with "cloonix " is blue and a
                     click copies it
"       . text"      a trace, dim
show() lines         folded under the step that ran them
stderr, pause()      repeated in the step, red or amber
the docstring        the About view; a line in capitals is a heading

A result is shown: a ping is five pings, its last lines in the history, then hold() and the pause step() takes, four seconds, none with CLOONIX_DEMO_FAST=1 and none on a terminal. A demo that leaves something to try by hand calls pause(what): it waits for Enter from a terminal or from the Demo panel, and goes on at once from a script.

12.3. Start clean, draw itself

After finding its image a demo calls erase_net(): del all, then the topology read until it is empty. Then place(): a position for every item and an angle for every interface, sent before the items exist, so the topology appears as the drawing of the docstring. A demo never starts the net; it checks the net answers and prints cloonix start <name> otherwise.

12.4. The commands are visible

In the source a command is written whole, cloonix cli included:

run("cloonix cli %s plug %s eth0 %s" % (NET, node, LAN))
ssh(node, "ip addr add 10.0.10.1/24 dev eth0.10")

In the output every command prints itself as typed, alone on its line with nothing in front, so any line of the console pastes into a terminal as it stands:

cloonix cli nemo add cnt a10 eth=1 trixie
cloonix cli nemo ssh a10 "ip link add link eth0 name eth0.10 type vlan id 10"

Two commands do not print, the two that are polled; both are named in the docstring of the function that polls them:

cloonix cli <net> dump commands
cloonix --wait 3 cli <net> ssh <node> "echo yes"

run() takes the whole line and splits it as a shell does, so the line printed is the line executed. ssh() prints the line and builds the argument vector from the string, its command being one argument holding spaces and quotes. What the demos share is in demos/lib/demolib.py.

12.5. Images

Keys, certificates, zone files, rulesets and configurations are written over cloonix cli ssh while the demo runs; an image holds none, so several demos share one. Every image except trixie_systemd is built without systemd: a running systemd takes several inotify instances, counted by the kernel per user id. The images are built by vm_create/cnt_kvm_demo/doitall.sh, keyed by the demo name:

./doitall.sh wg
./doitall.sh --all

12.6. The two checkers

debug/check_demos.py                  static, under a second, no server
debug/demo_test.py nemo               runs every demo and reports
debug/demo_test.py nemo --list        what would run, what is skipped
debug/demo_test.py nemo --only vlan,stp
debug/demo_test.py nemo --repeat 3

demo_test.py reports a missing image as SKIP with its build command, runs each demo under a timeout, waits for an empty net between two demos, and prints the distinct KERR lines of syslog for each demo, passed or not.

Developer documentation  ‹ 11. Conventions and pitfalls  › 13. The autotest: the suites in two kvm