aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-03-07 23:04:29 +0100
committerDimitri Staessens <dimitri@ouroboros.rocks>2026-03-08 14:25:09 +0100
commit3ab851644501e4906e91084a81e33e1a3cebd5cc (patch)
tree3c9132d16ac137ed34fd0daefe1fb18b8c15f814
parent6cbcfb039e608419bd6ced673723918aca6fb278 (diff)
downloadrumba-3ab851644501e4906e91084a81e33e1a3cebd5cc.tar.gz
rumba-3ab851644501e4906e91084a81e33e1a3cebd5cc.zip
build: Move to pyproject + setuptools_scm0.23.0
Replace setup.py and MANIFEST.in with pyproject.toml. The version is now derived from git tags via setuptools_scm, and added .git_archival.txt and .gitattributes for git archive support. Remove _version.py from version control as it is now auto-generated at build time. Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
-rw-r--r--.git_archival.txt4
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore3
-rw-r--r--MANIFEST.in3
-rw-r--r--README.md15
-rw-r--r--doc/conf.py7
-rw-r--r--pyproject.toml39
-rw-r--r--rumba/__init__.py3
-rw-r--r--rumba/_version.py28
-rw-r--r--rumba/irm_backend.py83
-rw-r--r--rumba/prototypes/ouroboros.py9
-rwxr-xr-xsetup.py53
12 files changed, 123 insertions, 125 deletions
diff --git a/.git_archival.txt b/.git_archival.txt
new file mode 100644
index 0000000..3994ec0
--- /dev/null
+++ b/.git_archival.txt
@@ -0,0 +1,4 @@
+node: $Format:%H$
+node-date: $Format:%cI$
+describe-name: $Format:%(describe:tags=true)$
+ref-names: $Format:%D$
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..00a7b00
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+.git_archival.txt export-subst
diff --git a/.gitignore b/.gitignore
index 93e8e76..fb5f575 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ dist/
.eggs/
sdist/
+# setuptools_scm generated
+rumba/_version.py
+
# Virtual environments
.venv/
venv/
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index b44eea6..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-include AUTHORS
-include LICENSE
-include README.md
diff --git a/README.md b/README.md
index 87178af..3be9962 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ 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
@@ -35,8 +36,7 @@ On Debian/Ubuntu you may also need:
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev
```
-The required dependencies (`paramiko` and `docker`) are installed
-automatically.
+The required dependency (`paramiko`) is installed automatically.
## Defining a Topology
@@ -200,6 +200,17 @@ If you use Rumba in your research, please cite:
> "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
diff --git a/doc/conf.py b/doc/conf.py
index 55e3572..7ea5303 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -20,10 +20,7 @@ autoclass_content = 'both'
html_static_path = ['_static']
templates_path = ['_templates']
-_locals = {}
-with open('../rumba/_version.py') as fp:
- exec(fp.read(), None, _locals)
-version = _locals['__version__']
+from rumba import __version__
+version = __version__
-version = version
release = version
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b596afd
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,39 @@
+[build-system]
+requires = ["setuptools>=64", "setuptools_scm>=8"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "Rumba"
+dynamic = ["version"]
+description = "Rumba measurement framework for Ouroboros"
+readme = "README.md"
+license = "LGPL-2.1-or-later"
+requires-python = ">=3.8"
+keywords = ["ouroboros", "measurement", "testbed"]
+authors = [
+ { name = "Sander Vrijders", email = "sander@ouroboros.rocks" },
+ { name = "Dimitri Staessens", email = "dimitri@ouroboros.rocks" },
+]
+dependencies = [
+ "packaging",
+ "paramiko>=2.0,<4",
+]
+
+[project.optional-dependencies]
+docker = ["docker>=5.0,<8"]
+NumpyAcceleration = ["numpy"]
+graphs = ["pydot"]
+visualizer = ["igraph", "plotly"]
+pyouroboros = ["PyOuroboros"]
+
+[project.urls]
+Homepage = "https://codeberg.org/o7s/rumba"
+
+[tool.setuptools]
+script-files = ["tools/rumba-access"]
+
+[tool.setuptools.packages.find]
+exclude = ["examples", "tools", "doc"]
+
+[tool.setuptools_scm]
+version_file = "rumba/_version.py"
diff --git a/rumba/__init__.py b/rumba/__init__.py
index 62897b3..6ca82c1 100644
--- a/rumba/__init__.py
+++ b/rumba/__init__.py
@@ -23,3 +23,6 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., http://www.fsf.org/about/contact/.
#
+
+from rumba._version import version as __version__ # noqa: F401
+from rumba._version import version_tuple as __version_tuple__ # noqa: F401
diff --git a/rumba/_version.py b/rumba/_version.py
deleted file mode 100644
index f1e5b84..0000000
--- a/rumba/_version.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# A library to manage ARCFIRE experiments
-#
-# Copyright (C) 2017-2018 Nextworks S.r.l.
-# Copyright (C) 2017-2018 imec
-#
-# Sander Vrijders <sander.vrijders@ugent.be>
-# Dimitri Staessens <dimitri.staessens@ugent.be>
-# Vincenzo Maffione <v.maffione@nextworks.it>
-# Marco Capitani <m.capitani@nextworks.it>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., http://www.fsf.org/about/contact/.
-#
-
-__version_info__ = (0, 23, 0)
-__version__ = '.'.join(map(str, __version_info__))
diff --git a/rumba/irm_backend.py b/rumba/irm_backend.py
index e9cc5c6..e859e21 100644
--- a/rumba/irm_backend.py
+++ b/rumba/irm_backend.py
@@ -29,6 +29,8 @@ Provides two implementations:
"""
import abc
+from packaging.version import Version
+
import rumba.log as log
from rumba.elements.topology import LayerType
@@ -220,26 +222,48 @@ class IrmPython(IrmBackend):
def __init__(self):
try:
- from ouroboros import irm as _irm
from ouroboros import cli as _cli
- self._irm = _irm
self._cli = _cli
logger.info("pyouroboros IRM backend loaded successfully")
except ImportError:
raise ImportError(
"pyouroboros is required for Python backend. "
"Install it from the pyouroboros repository.")
+ self._check_version()
+
+ @staticmethod
+ def _check_version():
+ """Verify PyOuroboros is on the same patch level as Rumba.
+
+ Uses PEP 440 ordering so that dev/pre-release versions sort
+ correctly: tag 0.23.0 accepts 0.23.1.dev0 (setuptools_scm's
+ representation of the first commit after 0.23.0) because
+ 0.23.0 <= 0.23.1.dev0 < 0.23.1 in PEP 440.
+ """
+ import importlib.metadata
+ from rumba._version import version as rumba_ver
+
+ pyouro_ver = Version(importlib.metadata.version("PyOuroboros"))
+ floor = Version(rumba_ver)
+ parts = [int(x) for x in rumba_ver.split(".")[:3]]
+ parts[2] += 1
+ ceiling = Version(".".join(str(x) for x in parts))
+ if pyouro_ver < floor or pyouro_ver >= ceiling:
+ raise RuntimeError(
+ "PyOuroboros >= %s, < %s required (lockstep with Rumba), "
+ "got %s" % (floor, ceiling, pyouro_ver)
+ )
def _get_ipcp_type(self, layer_type):
"""Convert LayerType to pyouroboros IpcpType."""
type_map = {
- LayerType.LOCAL: self._irm.IpcpType.LOCAL,
- LayerType.UNICAST: self._irm.IpcpType.UNICAST,
- LayerType.BROADCAST: self._irm.IpcpType.BROADCAST,
- LayerType.ETH_LLC: self._irm.IpcpType.ETH_LLC,
- LayerType.ETH_DIX: self._irm.IpcpType.ETH_DIX,
- LayerType.UDP4: self._irm.IpcpType.UDP4,
- LayerType.UDP6: self._irm.IpcpType.UDP6,
+ LayerType.LOCAL: self._cli.IpcpType.LOCAL,
+ LayerType.UNICAST: self._cli.IpcpType.UNICAST,
+ LayerType.BROADCAST: self._cli.IpcpType.BROADCAST,
+ LayerType.ETH_LLC: self._cli.IpcpType.ETH_LLC,
+ LayerType.ETH_DIX: self._cli.IpcpType.ETH_DIX,
+ LayerType.UDP4: self._cli.IpcpType.UDP4,
+ LayerType.UDP6: self._cli.IpcpType.UDP6,
}
return type_map[layer_type]
@@ -253,9 +277,8 @@ class IrmPython(IrmBackend):
def bootstrap_ipcp(self, node, name, layer_type, layer_name,
policies=None, eth_dev=None, ip_addr=None,
autobind=True):
- pid = self._cli.pid_of(name)
ipcp_type = self._get_ipcp_type(layer_type)
- irm = self._irm
+ cli = self._cli
# Build config based on type
if layer_type == LayerType.UNICAST:
@@ -269,28 +292,28 @@ class IrmPython(IrmBackend):
for comp, pol in policies.items():
if comp == 'rmt.pff':
pol_map = {
- 'lfa': irm.LinkStatePolicy.LFA,
- 'ecmp': irm.LinkStatePolicy.ECMP,
- 'simple': irm.LinkStatePolicy.SIMPLE,
+ 'lfa': cli.LinkStatePolicy.LFA,
+ 'ecmp': cli.LinkStatePolicy.ECMP,
+ 'simple': cli.LinkStatePolicy.SIMPLE,
}
if pol in pol_map:
ls_kwargs['pol'] = pol_map[pol]
if ls_kwargs:
- routing_kwargs['ls'] = irm.LinkStateConfig(**ls_kwargs)
+ routing_kwargs['ls'] = cli.LinkStateConfig(**ls_kwargs)
if routing_kwargs:
- dt_kwargs['routing'] = irm.RoutingConfig(**routing_kwargs)
+ dt_kwargs['routing'] = cli.RoutingConfig(**routing_kwargs)
if dt_kwargs:
- uc_kwargs['dt'] = irm.DtConfig(**dt_kwargs)
+ uc_kwargs['dt'] = cli.DtConfig(**dt_kwargs)
- conf = irm.IpcpConfig(
+ conf = cli.IpcpConfig(
ipcp_type=ipcp_type,
layer_name=layer_name,
- unicast=irm.UnicastConfig(**uc_kwargs))
+ unicast=cli.UnicastConfig(**uc_kwargs))
elif layer_type in (LayerType.ETH_DIX, LayerType.ETH_LLC):
- eth_conf = irm.EthConfig(dev=eth_dev or "")
- conf = irm.IpcpConfig(
+ eth_conf = cli.EthConfig(dev=eth_dev or "")
+ conf = cli.IpcpConfig(
ipcp_type=ipcp_type,
layer_name=layer_name,
eth=eth_conf)
@@ -299,37 +322,35 @@ class IrmPython(IrmBackend):
udp4_kwargs = {}
if ip_addr:
udp4_kwargs['ip_addr'] = ip_addr
- conf = irm.IpcpConfig(
+ conf = cli.IpcpConfig(
ipcp_type=ipcp_type,
layer_name=layer_name,
- udp4=irm.Udp4Config(**udp4_kwargs))
+ udp4=cli.Udp4Config(**udp4_kwargs))
elif layer_type == LayerType.UDP6:
udp6_kwargs = {}
if ip_addr:
udp6_kwargs['ip_addr'] = ip_addr
- conf = irm.IpcpConfig(
+ conf = cli.IpcpConfig(
ipcp_type=ipcp_type,
layer_name=layer_name,
- udp6=irm.Udp6Config(**udp6_kwargs))
+ udp6=cli.Udp6Config(**udp6_kwargs))
else:
- conf = irm.IpcpConfig(
+ conf = cli.IpcpConfig(
ipcp_type=ipcp_type,
layer_name=layer_name)
- self._cli.bootstrap_ipcp(pid, conf, autobind=autobind)
+ self._cli.bootstrap_ipcp(name, conf, autobind=autobind)
logger.debug("Bootstrapped IPCP %s in layer %s", name, layer_name)
def enroll_ipcp(self, node, name, dst_name, autobind=True):
- pid = self._cli.pid_of(name)
- self._cli.enroll_ipcp(pid, dst_name, autobind=autobind)
+ self._cli.enroll_ipcp(name, dst_name, autobind=autobind)
logger.debug("Enrolled IPCP %s via %s", name, dst_name)
def connect_ipcp(self, node, name, dst_name):
- pid = self._cli.pid_of(name)
- self._cli.connect_ipcp(pid, dst_name)
+ self._cli.connect_ipcp(name, dst_name)
logger.debug("Connected IPCP %s to %s", name, dst_name)
def reg_name(self, node, name, ipcp_names):
diff --git a/rumba/prototypes/ouroboros.py b/rumba/prototypes/ouroboros.py
index ce05181..1c2e007 100644
--- a/rumba/prototypes/ouroboros.py
+++ b/rumba/prototypes/ouroboros.py
@@ -34,7 +34,6 @@ import rumba.multiprocess as m_processing
import rumba.log as log
import rumba.testbeds.local as local
import rumba.testbeds.localnet as localnet
-import rumba.testbeds.dockertb as docker
import rumba.storyboard as sb
from rumba.elements.topology import LayerType
from rumba.irm_backend import IrmPython, IrmCLI
@@ -186,8 +185,12 @@ class Experiment(mod.Experiment):
self.exec_local_cmd(cmd)
def setup_ouroboros(self):
- if isinstance(self.testbed, docker.Testbed):
- return
+ try:
+ import rumba.testbeds.dockertb as dockertb
+ if isinstance(self.testbed, dockertb.Testbed):
+ return
+ except ImportError:
+ pass
if self._is_local:
subprocess.check_call('sudo -v'.split())
diff --git a/setup.py b/setup.py
deleted file mode 100755
index 68a7253..0000000
--- a/setup.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env python
-
-import re
-import setuptools
-
-with open('rumba/_version.py') as fp:
- _version_src = fp.read()
-_match = re.search(r'__version_info__\s*=\s*\((\d+),\s*(\d+),\s*(\d+)\)',
- _version_src)
-version = '%s.%s.%s' % _match.groups()
-
-with open('README.md') as fp:
- long_description = fp.read()
-
-setuptools.setup(
- name='Rumba',
- version=version,
- url='https://codeberg.org/o7s/rumba',
- keywords='ouroboros measurement testbed',
- author='Sander Vrijders, Dimitri Staessens',
- author_email='sander@ouroboros.rocks, dimitri@ouroboros.rocks',
- license='LGPL',
- description='Rumba measurement framework for Ouroboros',
- long_description=long_description,
- long_description_content_type='text/markdown',
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'License :: OSI Approved '
- ':: GNU Lesser General Public License v2 or later (LGPLv2+)',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Programming Language :: Python :: 3.12',
- 'Programming Language :: Python :: 3.13',
- 'Programming Language :: Python :: 3.14',
- 'Topic :: System :: Networking',
- ],
- packages=setuptools.find_packages(exclude=['examples', 'tools', 'doc']),
- python_requires='>=3.8',
- install_requires=[
- 'paramiko>=2.0,<4',
- 'docker>=5.0,<8',
- ],
- extras_require={
- 'NumpyAcceleration': ['numpy'],
- 'graphs': ['pydot'],
- 'visualizer': ['igraph', 'plotly'],
- 'pyouroboros': ['PyOuroboros'],
- },
- scripts=['tools/rumba-access'],
-)