Skip to content

Commit

Permalink
feat: Adding DPDK support in UPF (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gmerold authored Jan 9, 2024
1 parent b4f41eb commit adf2dd5
Show file tree
Hide file tree
Showing 8 changed files with 1,648 additions and 86 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,28 @@ juju deploy sdcore-upf-k8s --trust --channel=edge

### Exposing the UPF externally

If a load balancer such as `metallb` is present, the charm will configure an externally accessible service port with the load balancer upon install of the charm.
If a load balancer such as `metallb` is present, the charm will configure an externally accessible
service port with the load balancer upon install of the charm.

### Running UPF in DPDK mode

By default, UPF runs in `af_packet` mode. To run UPF in `dpdk` mode, `upf-mode` config option
should be used, i.e.:

```shell
juju deploy sdcore-upf --trust --channel=edge --config upf-mode="dpdk" --config enable-hugepages=True --config access-interface-mac-address="00:b0:d0:63:c2:26" --config core-interface-mac-address="00:b0:d0:63:c2:36"
```

As shown in the example above, when running UPF in `dpdk` mode, it is necessary to enable
HugePages and pass the MAC addresses of the `Access` and `Core` interfaces.

```{note}
Example command shown above assumes using default network configuration. If needed, network configs
should be changed to match the actual configuration.
```

For detailed instructions on running UPF in `dpdk` mode please visit
[How-to: Running UPF in DPDK mode](https://canonical-charmed-5g.readthedocs-hosted.com/en/latest/how-to/running_upf_in_dpdk_mode/).

## Image

Expand Down
53 changes: 35 additions & 18 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
options:
upf-mode:
type: string
default: af_packet
description: |
Either `af_packet` (default) or `dpdk`.
dnn:
type: string
default: internet
Expand All @@ -7,25 +12,14 @@ options:
type: string
default: 192.168.251.0/24
description: gNodeB subnet.
core-interface:
type: string
description: Interface on the host to use for the Core Network.
core-ip:
type: string
default: 192.168.250.3/24
description: IP address used by the UPF's Core interface.
core-gateway-ip:
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.
access-interface-mac-address:
type: string
description: |
MAC address of the UPF's Access interface.
Required only if `upf-mode` is `dpdk`.
access-ip:
type: string
default: 192.168.252.3/24
Expand All @@ -39,14 +33,37 @@ options:
description: |
MTU for the access interface (1200 <= MTU <= 65535) in bytes.
If not specified, Multus will use its default value (typically 1500).
core-interface:
type: string
description: Interface on the host to use for the Core Network.
core-interface-mac-address:
type: string
description: |
MAC address of the UPF's Core interface.
Required only if `upf-mode` is `dpdk`.
core-ip:
type: string
default: 192.168.250.3/24
description: IP address used by the UPF's Core interface.
core-gateway-ip:
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).
external-upf-hostname:
type: string
description: |-
description: |
Externally accessible FQDN for the UPF.
If not provided, it will default to the LoadBalancer Service hostname.
If that is not available, it will default to the internal
Kubernetes FQDN of the service.
enable-hugepages:
type: boolean
default: false
description: When enabled, HugePages of 1Gi will be used for a total of 2Gi HugePages.
description: |
When enabled, HugePages of 1Gi will be used for a total of 2Gi HugePages.
HugePages must be enabled if `upf-mode` is `dpdk`.
27 changes: 16 additions & 11 deletions lib/charms/kubernetes_charm_libraries/v0/multus.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,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 = 12
LIBPATCH = 13


logger = logging.getLogger(__name__)
Expand All @@ -148,6 +148,8 @@ def __eq__(self, other):
class NetworkAnnotation:
"""NetworkAnnotation."""

NETWORK_ANNOTATION_RESOURCE_KEY = "k8s.v1.cni.cncf.io/networks"

name: str
interface: str
mac: Optional[str] = None
Expand Down Expand Up @@ -359,7 +361,7 @@ def patch_statefulset(
template=PodTemplateSpec(
metadata=ObjectMeta(
annotations={
"k8s.v1.cni.cncf.io/networks": json.dumps(
NetworkAnnotation.NETWORK_ANNOTATION_RESOURCE_KEY: json.dumps(
[
network_annotation.dict()
for network_annotation in network_annotations
Expand Down Expand Up @@ -414,7 +416,9 @@ def unpatch_statefulset(
selector=statefulset.spec.selector, # type: ignore[attr-defined]
serviceName=statefulset.spec.serviceName, # type: ignore[attr-defined]
template=PodTemplateSpec(
metadata=ObjectMeta(annotations={"k8s.v1.cni.cncf.io/networks": "[]"}),
metadata=ObjectMeta(
annotations={NetworkAnnotation.NETWORK_ANNOTATION_RESOURCE_KEY: "[]"}
),
spec=PodSpec(containers=[container]),
),
)
Expand Down Expand Up @@ -506,10 +510,10 @@ def _pod_is_patched(
def _annotations_contains_multus_networks(
annotations: dict, network_annotations: list[NetworkAnnotation]
) -> bool:
if "k8s.v1.cni.cncf.io/networks" not in annotations:
if NetworkAnnotation.NETWORK_ANNOTATION_RESOURCE_KEY not in annotations:
return False
try:
if json.loads(annotations["k8s.v1.cni.cncf.io/networks"]) != [
if json.loads(annotations[NetworkAnnotation.NETWORK_ANNOTATION_RESOURCE_KEY]) != [
network_annotation.dict() for network_annotation in network_annotations
]:
return False
Expand Down Expand Up @@ -568,7 +572,7 @@ def __init__(
self,
charm: CharmBase,
network_attachment_definitions_func: Callable[[], list[NetworkAttachmentDefinition]],
network_annotations: list[NetworkAnnotation],
network_annotations_func: Callable[[], list[NetworkAnnotation]],
container_name: str,
refresh_event: BoundEvent,
cap_net_admin: bool = False,
Expand All @@ -580,7 +584,8 @@ def __init__(
charm: Charm object
network_attachment_definitions_func: A callable to a function returning a list of
`NetworkAttachmentDefinition` to be created.
network_annotations: List of NetworkAnnotation.
network_annotations_func: A callable to a function returning a list
of `NetworkAnnotation` to be added to the container.
container_name: Container name
cap_net_admin: Container requires NET_ADMIN capability
privileged: Container requires privileged security context
Expand All @@ -590,7 +595,7 @@ def __init__(
super().__init__(charm, "kubernetes-multus")
self.kubernetes = KubernetesClient(namespace=self.model.name)
self.network_attachment_definitions_func = network_attachment_definitions_func
self.network_annotations = network_annotations
self.network_annotations_func = network_annotations_func
self.container_name = container_name
self.cap_net_admin = cap_net_admin
self.privileged = privileged
Expand All @@ -608,7 +613,7 @@ def _configure_multus(self, event: BoundEvent) -> None:
if not self._statefulset_is_patched():
self.kubernetes.patch_statefulset(
name=self.model.app.name,
network_annotations=self.network_annotations,
network_annotations=self.network_annotations_func(),
container_name=self.container_name,
cap_net_admin=self.cap_net_admin,
privileged=self.privileged,
Expand Down Expand Up @@ -682,7 +687,7 @@ def _statefulset_is_patched(self) -> bool:
"""Returns whether statefuset is patched with network annotations and capabilities."""
return self.kubernetes.statefulset_is_patched(
name=self.model.app.name,
network_annotations=self.network_annotations,
network_annotations=self.network_annotations_func(),
container_name=self.container_name,
cap_net_admin=self.cap_net_admin,
privileged=self.privileged,
Expand All @@ -692,7 +697,7 @@ def _pod_is_ready(self) -> bool:
"""Returns whether pod is ready with network annotations and capabilities."""
return self.kubernetes.pod_is_ready(
pod_name=self._pod,
network_annotations=self.network_annotations,
network_annotations=self.network_annotations_func(),
container_name=self.container_name,
cap_net_admin=self.cap_net_admin,
privileged=self.privileged,
Expand Down
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
httpx
ipaddress
ops
jinja2
lightkube
lightkube-models
macaddress
pydantic
pytest-interface-tester
PyYAML>=6.0.1
Expand Down
Loading

0 comments on commit adf2dd5

Please sign in to comment.