Rumba is a Python framework that allows users to write Python scripts
to define recursive internet networks and run scripted experiments.

The original documentation can be found
[here](https://arcfire.gitlab.io/rumba/).

This version of rumba is maintained by the Ouroboros (O7s) project.

The documentation can be found
[here](https://ouroboros.rocks/wiki/Rumba).

## Installation

To install Rumba from a local clone:

```bash
git clone https://codeberg.org/o7s/rumba.git
cd rumba
python3 -m venv .venv
source .venv/bin/activate
pip install .
```

Optional extras:

```bash
pip install rumba[docker]             # Docker testbed backend
pip install rumba[NumpyAcceleration]  # faster random traffic generation
pip install rumba[graphs]             # PDF graph export via pydot
pip install rumba[visualizer]         # interactive visualization via igraph + plotly
```

On Debian/Ubuntu you may also need:

```bash
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev
```

The required dependency (`paramiko`) is installed automatically.

## Defining a Topology

Rumba models recursive networks using **layers** and **nodes**. Layers
correspond to Ouroboros IPCP types. Nodes declare which layers they
belong to and how those layers are stacked via **registrations**.

The example below defines four nodes connected in a ring, with a single
unicast layer (`n1`) running over four point-to-point Ethernet links:

```
  a ---e01--- b
  |           |
 e04         e02
  |           |
  d ---e03--- c
```

```python
from rumba.model import UnicastLayer, EthDixLayer, Node

# A unicast layer that will span all four nodes
n1 = UnicastLayer("n1")

# Four point-to-point Ethernet links (max 2 members each)
e01 = EthDixLayer("e01")  # a <-> b
e02 = EthDixLayer("e02")  # b <-> c
e03 = EthDixLayer("e03")  # c <-> d
e04 = EthDixLayer("e04")  # d <-> a

# Each node lists its layer memberships and how layers are stacked.
# registrations = {upper: [lower, ...]} means "upper registers in lower".
a = Node("a",
         layers=[n1, e01, e04],
         registrations={n1: [e01, e04]})

b = Node("b",
         layers=[n1, e01, e02],
         registrations={n1: [e01, e02]})

c = Node("c",
         layers=[n1, e02, e03],
         registrations={n1: [e02, e03]})

d = Node("d",
         layers=[n1, e03, e04],
         registrations={n1: [e03, e04]})
```

Two nodes that share an `EthDixLayer` are connected by that Ethernet
link. The `registrations` dictionary tells Rumba how to stack the
layers: `{n1: [e01, e04]}` means the unicast layer `n1` registers in
both `e01` and `e04` on that node, giving it connectivity over those
links.

Policies can be set on unicast layers:

```python
n1.add_policy("routing", "lfa")
```

Link quality (delay, loss, rate) can be configured on Ethernet layers:

```python
from rumba.model import EthDixLayer, Delay

e01 = EthDixLayer("e01")
e01.set_delay(delay=10, jitter=2)   # 10 ms delay, 2 ms jitter
e01.set_loss(loss=0.1)              # 0.1% packet loss
e01.set_rate(rate=100)              # 100 Mbps
```

## Quick Start

A complete runnable script that deploys the ring topology above on a
local testbed:

```python
from rumba.model import *
from rumba.utils import ExperimentManager

import rumba.testbeds.local as local
import rumba.prototypes.ouroboros as our

# --- topology (from the example above) ---
n1 = UnicastLayer("n1")
e01 = EthDixLayer("e01")
e02 = EthDixLayer("e02")
e03 = EthDixLayer("e03")
e04 = EthDixLayer("e04")

a = Node("a", layers=[n1, e01, e04], registrations={n1: [e01, e04]})
b = Node("b", layers=[n1, e01, e02], registrations={n1: [e01, e02]})
c = Node("c", layers=[n1, e02, e03], registrations={n1: [e02, e03]})
d = Node("d", layers=[n1, e03, e04], registrations={n1: [e03, e04]})

# --- testbed and experiment ---
tb = local.Testbed()
exp = our.Experiment(tb, nodes=[a, b, c, d])

# --- run ---
with ExperimentManager(exp):
    exp.swap_in()              # provision the testbed
    exp.bootstrap_prototype()  # create IPCPs and enrollments
```

`swap_in()` provisions the nodes on the chosen testbed.
`bootstrap_prototype()` creates the Ouroboros IPCPs, bootstraps them,
and performs enrollments automatically. `ExperimentManager` handles
cleanup when the block exits.

## Key Concepts

- **Layer** — a network layer aligned with an Ouroboros IPCP type.
  Available types: `UnicastLayer`, `BroadcastLayer`, `EthDixLayer`,
  `EthLlcLayer`, `LocalLayer`, `Udp4Layer`, `Udp6Layer`.
- **Node** — a system declaring its layer memberships and
  registrations (layer stacking).
- **Testbed** — the infrastructure backend that provisions nodes
  (see table below).
- **Experiment** — computes IPCP placement, enrollment order, and
  manages the prototype lifecycle.
- **StoryBoard** — schedules client/server traffic with support for
  Poisson arrival rates.
- **CommandResult** — structured result from `node.execute_command()`
  carrying exit code, stdout, and stderr.
- **Process** — a managed background process with `is_alive()`,
  `wait()`, and `kill()`.
- **ExperimentManager** — context manager that handles experiment
  setup and teardown.

## Supported Testbeds

| Testbed  | Module                    | Description                                    |
|----------|---------------------------|------------------------------------------------|
| Local    | `rumba.testbeds.local`    | Single machine, processes run locally           |
| LocalNet | `rumba.testbeds.localnet` | Linux bridges + veth pairs, supports netem      |
| Docker   | `rumba.testbeds.dockertb` | Docker containers with bridge networking        |
| Emulab   | `rumba.testbeds.emulab`   | Emulab / iMinds testbed (SSH-based)             |
| jFed     | `rumba.testbeds.jfed`     | Virtual Wall, ExoGENI, CloudLab via jFed        |

## CLI Tools

The `rumba-access` command opens an SSH session to an experiment node:

```bash
rumba-access <node-name>
```

## More Examples

See the [examples/](examples/) directory for additional topologies and
usage patterns, including multi-layer stacking, Docker deployments,
storyboard scripting, and large-scale experiments.

## Citation

If you use Rumba in your research, please cite:

> S. Vrijders, D. Staessens, M. Capitani, and V. Maffione,
> "Rumba: A Python framework for automating large-scale recursive
> internet experiments on GENI and FIRE+," *IEEE*, 2018.

## Versioning

Rumba uses `setuptools_scm` to derive its version from git tags.

**Compatibility contract across Ouroboros repositories:**

| Scope | Rule |
|---|---|
| ouroboros (C) ↔ pyouroboros / rumba | Shared `major.minor` — rumba requires at least the same ouroboros `major.minor` |
| pyouroboros ↔ rumba ↔ ouroboros-integration | Strict lockstep `major.minor.patch` — always released together |

## License

Rumba is licensed under the
[GNU Lesser General Public License v2.1](LICENSE).

See [AUTHORS](AUTHORS) for the list of contributors.
