Backends#

The backend abstraction layer allows OpenOptics to run on different network simulators and hardware targets. The create_backend factory selects the implementation at runtime based on the backend parameter passed to BaseNetwork.

openoptics.backends.create_backend(backend_name: str) → BackendBase[source]#

Instantiate and return the backend for the given name.

Parameters:

backend_name – “Mininet”, “ns3”, or “Tofino”

Returns:

A BackendBase instance.

class openoptics.backends.SwitchHandle(name: str, thrift_port: int)[source]#

Bases: object

A backend-agnostic reference to a switch.

class openoptics.backends.BackendBase[source]#

Bases: ABC

Abstract base class for OpenOptics backends.

Implement this class to add support for a new backend (e.g. ns-3, Tofino). BaseNetwork and DeviceManager interact with the network exclusively through this interface, keeping them backend-agnostic.

Subclass attributes#

supports_device_managerbool

If False, start_monitor() skips DeviceManager creation (which requires BMv2 Thrift). Tofino uses BFRt gRPC instead.

supports_dashboard_without_device_managerbool

If True, start_monitor() may still start the dashboard even when there is no DeviceManager. Simulator backends use this to publish their own event-driven telemetry.

supports_clibool

If False, BaseNetwork.start() skips the interactive OpticalCLI and calls self.run() on the backend instead. Used by simulation backends (ns-3) where there is no live network to interact with; the whole scenario is scripted up front and run() just advances the simulator.

supports_device_manager: bool = True#
supports_dashboard_without_device_manager: bool = False#
supports_cli: bool = True#
max_source_route_hops: int | None = None#
run() → None[source]#

Run the backend (simulator) to completion.

Only called by BaseNetwork.start() when supports_cli is False. Default implementation is a no-op so non-simulator backends don’t need to override it.

setup_dashboard(service) → None[source]#

Wire backend-specific collectors / event sources into the dashboard.

Called by BaseNetwork.start_monitor() after DashboardService has been created + epoched, and after any default DeviceMetricCollector has been registered. Simulator backends (ns-3) override this to register a Python sink and connect their ns-3 TraceSource hooks. Default: no-op.

Parameters:

service – The live DashboardService; use its register_event_source / register_collector methods.

classmethod accepted_kwargs() → set[source]#

Return the names of backend-specific keyword arguments accepted by setup().

Override in subclasses to declare backend-specific parameters. BaseNetwork.__init__ validates user-supplied **backend_kwargs against this set and raises ValueError for unknown names.

abstractmethod setup(*, nb_node: int, nb_host_per_tor: int, nb_link: int, nb_time_slices: int, time_slice_duration_us: int, guardband_us: int, calendar_queue_mode: int, **backend_kwargs) → None[source]#

Create backend nodes and start the network.

After this call, get_switch(), get_tor_switches(), and get_ip_to_tor() must return valid data.

Parameters:

**backend_kwargs – Backend-specific parameters declared via accepted_kwargs(). Unknown keys are rejected earlier by BaseNetwork.__init__, so subclasses may safely consume whatever they declared.

abstractmethod get_switch(name: str) → SwitchHandle[source]#

Return a SwitchHandle for the named switch.

abstractmethod switch_exists(name: str) → bool[source]#

Return True if a switch with the given name exists.

abstractmethod get_tor_switches() → list[source]#

Return a list of SwitchHandle for all ToR switches.

get_optical_switches() → list[source]#

Return a list of SwitchHandle for all optical (OCS) switches.

Default returns [] so backends without OCS device-plane telemetry need no override.

abstractmethod get_ip_to_tor() → dict[source]#

Return the {ip_str: tor_id} mapping populated during setup.

abstractmethod load_table(switch_name: str, entries: list, print_flag: bool = False, save_flag: bool = False, save_name: str = 'saved_commands') → bool[source]#

Load P4 table entries to the named switch.

Parameters:
  • switch_name – Name of the switch (e.g. “ocs”, “tor0”).

  • entries – List of TableEntry objects to install.

  • print_flag – Print backend output if True.

  • save_flag – Save a human-readable representation to a file if True.

  • save_name – Filename stem used when save_flag is True.

Returns:

True on success.

abstractmethod clear_table(switch_name: str, table: str, print_flag: bool = False) → None[source]#

Clear all entries from a P4 table on the named switch.

Parameters:
  • switch_name – Name of the switch (e.g. “ocs”, “tor0”).

  • table – Logical table name (e.g. "ocs_schedule"). Each backend maps this to its internal table identifier.

  • print_flag – Print backend output if True.

abstractmethod stop() → None[source]#

Stop the network.

abstractmethod cleanup() → None[source]#

Clean up residual state (e.g. stale processes, network namespaces).

get_cli_commands() → dict[source]#

Return backend-specific CLI commands to register on OpticalCLI.

Returns:

A dict of {command_name: (fn, docstring)} where fn(cli, line) is called when the user types the command.

default_handler(line: str) → bool[source]#

Handle an unrecognised CLI line.

Return True if the backend handled the command, False to fall through to OpticalCLI’s default error message.