IP Router Interface

  • Canonical Telco
Channel Revision Published Runs on
latest/edge 2 20 Oct 2023
Ubuntu 22.04
juju deploy ip-router-interface --channel edge
Show information

Platform:

Ubuntu
22.04

charms.ip_router_interface.v0.ip_router_interface

Library for the ip-router integration

This library contains the Requires and Provides classes for interactions through the ip-router interface

Getting Started

From a charm directory, fetch the library using charmcraft:

charmcraft fetch-lib charms.ip_router_interface.v0.ip_router_interface
Provider charm

This example provider charm is all we need to listen to ip-router requirer requests. The ip-router provider fulfills the routing function for multiple charms that are requirers of the ip-router interface. For that reason, this charm will continuously track and update all of the connected requirers and the networks and routes they've requested, which it does in a routing table. As new charms are connected and disconnected to the relation, this routing table is automatically adds and removes the dependent networks.

The library handles the listening and synchronization for all of the ip-router network requests internally, which means as the charm author you don't need to worry about any of the business logic of validating or orchestrating the relation network.

You can also listen to the routing_table_updated event that is emitted after the tables are synced.

import logging, json
import ops
from charms.ip_router_interface.v0.ip_router_interface import *

class SimpleIPRouteProviderCharm(ops.CharmBase):

    def __init__(self, *args):
        super().__init__(*args)
        self.RouterProvider = RouterProvides(charm=self)
        self.framework.observe(self.on.install, self._on_install)
        self.framework.observe(self.RouterProvider.on.routing_table_updated, self._routing_table_updated)
        self.framework.observe(self.on.get_routing_table_action, self._action_get_routing_table)

    def _on_install(self, event: ops.InstallEvent):
        self.unit.status = ops.ActiveStatus("Ready to Provide")

    def _routing_table_updated(self, event: RoutingTableUpdatedEvent):
        routing_table = event.routing_table

        # Process the networks however you like
        implement_networks(all_networks)
        
        
    def _action_get_routing_table(self, event: ops.ActionEvent):
        all_networks = self.RouterProvider.get_flattened_routing_table()
        event.set_results({"msg": json.dumps(all_networks)})
    


if __name__ == "__main__":  # pragma: nocover
    ops.main(SimpleIPRouteProviderCharm)  # type: ignore
Requirer charm

This example requirer charm shows the two available actions as a host in the network:

  • get the latest list of all networks available from the provider
  • request a network to be assigned to the requirer charm

The ip-router requirer allows a foolproof, typechecked, secure and safe way to interact with the router that handles validation and format of the network request, so you can focus on more important things. The library also provides a way to list out all of the available networks. This list is not cached, and comes directly from the provider.

import logging, json
import ops

from charms.ip_router_interface.v0.ip_router_interface import *

class SimpleIPRouteRequirerCharm(ops.CharmBase):

    def __init__(self, *args):
        super().__init__(*args)
        self.RouterRequirer = RouterRequires(charm=self)
        self.framework.observe(self.on.install, self._on_install)
        self.framework.observe(self.on.ip_router_relation_joined, self._on_relation_joined)
        self.framework.observe(self.RouterProvider.on.routing_table_updated, self._routing_table_updated)

        self.framework.observe(self.on.get_all_networks_action, self._action_get_all_networks)
        self.framework.observe(self.on.request_network_action, self._action_request_network)

    def _on_install(self, event: ops.InstallEvent):
        self.unit.status = ops.ActiveStatus("Ready to Provide")

    def _on_relation_joined(self, event: ops.RelationJoinedEvent):
        self.unit.status = ops.ActiveStatus("Ready to Require")

    def _routing_table_updated(self, event: RoutingTableUpdatedEvent):
        # Get and process all of the available networks when they're updated
        all_networks = self.RouterRequirer.get_all_networks()

    def _action_get_all_networks(self, event: ops.ActionEvent):
        # Get and process all of the available networks any time you like
        all_networks = self.RouterRequirer.get_all_networks()
        event.set_results({"msg": json.dumps(all_networks)})

    def _action_request_network(self, event: ops.ActionEvent):
        # Request a new network as required in the required format
        self.RouterRequirer.request_network(event.params["network"])

        event.set_results({"msg": "ok"})


if __name__ == "__main__":  # pragma: nocover
    ops.main.main(SimpleIPRouteRequirerCharm)  # type: ignore

You can relate both charms by running:

juju integrate <ip-router provider charm> <ip-router requirer charm>

class RoutingTableUpdatedEvent

Description

Charm event for when a host registers a route to an existing interface in the router None

Methods

RoutingTableUpdatedEvent. __init__( self , handle , routing_table )

RoutingTableUpdatedEvent. snapshot( self )

RoutingTableUpdatedEvent. restore( self , snapshot )

class RouterProviderCharmEvents

class RouterRequirerCharmEvents

class RouterProvides

This class is used to manage the routing table of the router provider.

Attributes

charm
The Charm object that instantiates this class.
relationship_name
The name used for the relationship implementing the ip-router interface. All requirers that integrate to this name are grouped into one routing table. "ip-router" by default.

Description

It's capabilities are to:

  • Build a Routing Table from all of the databags of the requirers with their declared networks.
  • Synchronize the databags of all requiring units with the aforementioned routing table.
  • Send events indicating a change in this table.

Methods

RouterProvides. __init__( self , charm: CharmBase , relationship_name: str )

RouterProvides. get_routing_table( self )

Description

Build the routing table from all of the related databags. Relations that don't have missing or invalid network requests will be ignored.

RouterProvides. get_flattened_routing_table( self )

Returns

A list of objects of type Network

Description

Returns a read-only routing table that's flattened to fit the specification. The routing table is in the form of { app1_name: list_of_networks_1, app2_name: list_of_networks_2, ... }

A flattened table looks like [ *list_of_networks_1, *list_of_networks_2, ... ]

class RouterRequires

ip-router requirer class to be instantiated by charms that require routing

Attributes

charm
The Charm object that instantiates this class.

Description

This class provides methods to request a new network, and read the available network from the router providers. These should be used exclusively to interact with the relation.

Methods

RouterRequires. __init__( self , charm: CharmBase , relationship_name: str )

RouterRequires. request_network( self , networks , custom_network_name: str )

Requests a new network interface from the ip-router provider

Description

The interfaces must be valid according to _network_is_valid. Multiple calls to this function will replace the previously requested networks, so all of the networks required must be given with each call.

Arguments: networks: A list containing the desired networks of the type Network. custom_network_name: A string to use as the name of the network. Defaults to the relation name.

Raises: RuntimeError: No ip-router relation exists yet or validation of one or more of the networks failed.

RouterRequires. get_all_networks( self )

Fetches combined routing tables made available by ip-router providers

Returns

A list of objects of type Network. This list contains networks from all ip-router providers that are integrated with the charm.