Skip to content

Commit

Permalink
Support custom MTU sizes (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
gatici authored Oct 6, 2023
1 parent d0a8c4d commit 75ea307
Show file tree
Hide file tree
Showing 4 changed files with 390 additions and 105 deletions.
10 changes: 10 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ options:
type: string
default: 192.168.250.1
description: Gateway IP address to the Core Network.
core-interface-mtu-size:
type: int
description: |
MTU for the core interface (1200 <= MTU <= 65535) in bytes.
If not specified, Multus will use its default value (typically 1500).
access-interface:
type: string
description: Interface on the host to use for the Access Network.
Expand All @@ -29,6 +34,11 @@ options:
type: string
default: 192.168.252.1
description: Gateway IP address to the Access Network.
access-interface-mtu-size:
type: int
description: |
MTU for the access interface (1200 <= MTU <= 65535) in bytes.
If not specified, Multus will use its default value (typically 1500).
external-upf-hostname:
type: string
description: |-
Expand Down
23 changes: 11 additions & 12 deletions lib/charms/kubernetes_charm_libraries/v0/multus.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Configure the requested network attachment definitions
- Patch the statefulset with the necessary annotations for the container to have interfaces
that use those new network attachments.
- If an existing NAD config changed, it triggers pod restart to make the new config effective
- On charm removal, it will:
- Delete the created network attachment definitions
Expand All @@ -23,9 +24,6 @@
class NadConfigChangedEvent(EventBase):
def __init__(self, handle: Handle):
super().__init__(handle)
class KubernetesMultusCharmEvents(CharmEvents):
Expand Down Expand Up @@ -125,7 +123,7 @@ def _on_config_changed(self, event: EventBase):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 7
LIBPATCH = 8


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -564,8 +562,11 @@ def _configure_network_attachment_definitions(self):
list of NetworkAttachmentDefinitions to create
- Else, delete it
2. Goes through the list of NetworkAttachmentDefinitions to create and create them all
3. Detects the NAD config changes and triggers pod restart
if any there is any modification in existing NADs
"""
network_attachment_definitions_to_create = self.network_attachment_definitions_func()
nad_config_changed = False
for (
existing_network_attachment_definition
) in self.kubernetes.list_network_attachment_definitions():
Expand All @@ -579,6 +580,7 @@ def _configure_network_attachment_definitions(self):
self.kubernetes.delete_network_attachment_definition(
name=existing_network_attachment_definition.metadata.name
)
nad_config_changed = True
else:
network_attachment_definitions_to_create.remove(
existing_network_attachment_definition
Expand All @@ -587,6 +589,11 @@ def _configure_network_attachment_definitions(self):
self.kubernetes.create_network_attachment_definition(
network_attachment_definition=network_attachment_definition_to_create
)
if nad_config_changed:
# We want to trigger the pod restart once if there is a change in NADs
# after all the NADs are configured.
logger.warning("Restarting pod to make the new NAD configs effective.")
self.delete_pod()

def _network_attachment_definitions_are_created(self) -> bool:
"""Returns whether all network attachment definitions are created."""
Expand Down Expand Up @@ -658,11 +665,3 @@ def _on_remove(self, event: RemoveEvent) -> None:
def delete_pod(self) -> None:
"""Delete the pod."""
self.kubernetes.delete_pod(self._pod)

def get_network_attachment_definitions(self) -> list[NetworkAttachmentDefinition]:
"""Get all existing network attachment definitions in the namespace.
Returns:
NetworkAttachmentDefinitions (list) : List of network attachment definitions
"""
return self.kubernetes.list_network_attachment_definitions()
160 changes: 123 additions & 37 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import logging
import time
from subprocess import check_output
from typing import Optional
from typing import Any, Dict, Optional

from charms.kubernetes_charm_libraries.v0.multus import ( # type: ignore[import]
KubernetesMultusCharmLib,
Expand Down Expand Up @@ -45,6 +45,8 @@
CORE_NETWORK_ATTACHMENT_DEFINITION_NAME = "core-net"
ACCESS_INTERFACE_NAME = "access"
CORE_INTERFACE_NAME = "core"
ACCESS_INTERFACE_BRIDGE_NAME = "access-br"
CORE_INTERFACE_BRIDGE_NAME = "core-br"
CONFIG_FILE_NAME = "upf.json"
BESSCTL_CONFIGURE_EXECUTED_FILE_NAME = "bessctl_configure_executed"
UPF_MODE = "af_packet"
Expand Down Expand Up @@ -250,46 +252,28 @@ def _get_n4_upf_hostname(self) -> str:
return lb_hostname
return self._upf_hostname

def _network_attachment_definitions_from_config(self) -> list[NetworkAttachmentDefinition]:
"""Returns list of Multus NetworkAttachmentDefinitions to be created based on config."""
access_nad_config = {
"cniVersion": "0.3.1",
"ipam": {
"type": "static",
"routes": [
{
"dst": self._get_gnb_subnet_config(),
"gw": self._get_access_network_gateway_ip_config(),
},
],
"addresses": [
{
"address": self._get_access_network_ip_config(),
}
],
},
"capabilities": {"mac": True},
}
if (access_interface := self._get_access_interface_config()) is not None:
def _network_attachment_definitions_from_config(
self,
) -> list[NetworkAttachmentDefinition]:
"""Returns list of Multus NetworkAttachmentDefinitions to be created based on config.
Returns:
network_attachment_definitions: list[NetworkAttachmentDefinition]
"""
access_nad_config = self._get_access_nad_config()

if access_interface := self._get_access_interface_config():
access_nad_config.update({"type": "macvlan", "master": access_interface})
else:
access_nad_config.update({"type": "bridge", "bridge": "access-br"})
core_nad_config = {
"cniVersion": "0.3.1",
"ipam": {
"type": "static",
"addresses": [
{
"address": self._get_core_network_ip_config(),
}
],
},
"capabilities": {"mac": True},
}
if (core_interface := self._get_core_interface_config()) is not None:
access_nad_config.update({"type": "bridge", "bridge": ACCESS_INTERFACE_BRIDGE_NAME})

core_nad_config = self._get_core_nad_config()

if core_interface := self._get_core_interface_config():
core_nad_config.update({"type": "macvlan", "master": core_interface})
else:
core_nad_config.update({"type": "bridge", "bridge": "core-br"})
core_nad_config.update({"type": "bridge", "bridge": CORE_INTERFACE_BRIDGE_NAME})

return [
NetworkAttachmentDefinition(
metadata=ObjectMeta(name=ACCESS_NETWORK_ATTACHMENT_DEFINITION_NAME),
Expand Down Expand Up @@ -480,6 +464,10 @@ def _get_invalid_configs(self) -> list[str]:
invalid_configs.append("core-gateway-ip")
if not self._gnb_subnet_config_is_valid():
invalid_configs.append("gnb-subnet")
if not self._access_interface_mtu_size_is_valid():
invalid_configs.append("access-interface-mtu-size")
if not self._core_interface_mtu_size_is_valid():
invalid_configs.append("core-interface-mtu-size")
return invalid_configs

def _create_default_route(self) -> None:
Expand Down Expand Up @@ -759,6 +747,104 @@ def _get_cpu_extensions() -> list[str]:
del cpu_flags[0]
return cpu_flags

def _get_access_nad_config(self) -> Dict[Any, Any]:
"""Get access interface NAD config.
Returns:
config (dict): Access interface NAD config
"""
config = {
"cniVersion": "0.3.1",
"ipam": {
"type": "static",
"routes": [
{
"dst": self._get_gnb_subnet_config(),
"gw": self._get_access_network_gateway_ip_config(),
},
],
"addresses": [
{
"address": self._get_access_network_ip_config(),
}
],
},
"capabilities": {"mac": True},
}
if access_mtu := self._get_access_interface_mtu_config():
config.update({"mtu": access_mtu})
return config

def _get_core_nad_config(self) -> Dict[Any, Any]:
"""Get core interface NAD config.
Returns:
config (dict): Core interface NAD config
"""
config = {
"cniVersion": "0.3.1",
"ipam": {
"type": "static",
"addresses": [
{
"address": self._get_core_network_ip_config(),
}
],
},
"capabilities": {"mac": True},
}
if core_mtu := self._get_core_interface_mtu_config():
config.update({"mtu": core_mtu})
return config

def _get_core_interface_mtu_config(self) -> Optional[str]:
"""Get Core interface MTU size.
Returns:
mtu_size (str/None): If MTU size is not configured return None
If it is set, returns the configured value
"""
return self.model.config.get("core-interface-mtu-size")

def _get_access_interface_mtu_config(self) -> Optional[str]:
"""Get Access interface MTU size.
Returns:
mtu_size (str/None): If MTU size is not configured return None
If it is set, returns the configured value
"""
return self.model.config.get("access-interface-mtu-size")

def _access_interface_mtu_size_is_valid(self) -> bool:
"""Checks whether the access interface MTU size is valid.
Returns:
bool: Whether access interface MTU size is valid
"""
if (access_mtu := self._get_access_interface_mtu_config()) is None:
return True
try:
return 1200 <= int(access_mtu) <= 65535
except ValueError:
return False

def _core_interface_mtu_size_is_valid(self) -> bool:
"""Checks whether the core interface MTU size is valid.
Returns:
bool: Whether core interface MTU size is valid
"""
if (core_mtu := self._get_core_interface_mtu_config()) is None:
return True
try:
return 1200 <= int(core_mtu) <= 65535
except ValueError:
return False


def render_bessd_config_file(
upf_hostname: str,
Expand Down
Loading

0 comments on commit 75ea307

Please sign in to comment.