Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ContainerPoolManager: add netavark support #378

Merged
merged 2 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def test(self):
podman_uri=podman_uri,
image=image_name,
debug=1,
network_plugin="cni",
)

recipe_instance = HelloWorldRecipe()
Expand Down
55 changes: 52 additions & 3 deletions lnst/Controller/ContainerPoolManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import socket
from time import sleep
from json import loads
from typing import Optional
from lnst.Controller.AgentPoolManager import PoolManagerError
from lnst.Controller.Machine import Machine
from lnst.Common.DependencyError import DependencyError
Expand Down Expand Up @@ -38,10 +39,15 @@ class ContainerPoolManager(object):
:param image:
Mandatory parameter
:type image: str

:param network_plugin:
Podman network plugin, 'cni' or 'netavark', if unset, the network backend is auto-detected
:type network_plugin: Optional[str]
"""

def __init__(
self, pools, msg_dispatcher, ctl_config, podman_uri, image, pool_checks=True
self, pools, msg_dispatcher, ctl_config, podman_uri, image,
network_plugin='netavark', pool_checks=True
):
self._import_optionals()
self._pool = {}
Expand All @@ -54,6 +60,7 @@ def __init__(
self._image = ""
self._podman_connect(podman_uri)
self.image = image
self.network_plugin = network_plugin

self._networks = {}
self._network_prefix = "lnst_container_net_"
Expand All @@ -77,10 +84,12 @@ def _import_optionals():
global APIError
global Container
global Network
global IPAMConfig

from podman.errors import APIError
from podman.domain.containers import Container
from podman.domain.networks import Network
from podman.domain.ipam import IPAMConfig

except ModuleNotFoundError as e:
raise DependencyError(e)
Expand Down Expand Up @@ -207,7 +216,21 @@ def _create_container(self, name: str, reqs: dict):

return container, machine

def _create_network(self, network_name: str):
def _create_network(self, network_name: str, plugin: Optional[str]):
if not plugin:
podman_info = self._podman_client.info()
plugin = podman_info["host"]["networkBackend"]

logging.debug(f"Using {plugin} network backend for containers")

if plugin == "netavark":
return self._create_network_netavark(network_name)
elif plugin == "cni":
return self._create_network_cni(network_name)
else:
raise PoolManagerError(f"Unknown podman network plugin {plugin}")

def _create_network_cni(self, network_name: str):
"""Networks are created "manually" because podman does not
support creating L2 [1] networks. IPs in these networks are managed
by controller.
Expand Down Expand Up @@ -249,6 +272,32 @@ def _create_network(self, network_name: str):

return network

def _create_network_netavark(self, network_name: str):
name = self.get_network_name(network_name)
if name in self._networks:
return self._networks[name]

ipam_config = IPAMConfig(
driver="none",
)
logging.info(f"Creating network {name}")
try:
self._podman_client.networks.create(
jtluka marked this conversation as resolved.
Show resolved Hide resolved
name,
internal=True,
enable_ipv6=True,
ipam=ipam_config,
)
network = self._podman_client.networks.get(name)
except APIError as e:
raise PoolManagerError(f"Could not create network {name}: {e}")
except IOError as e:
raise PoolManagerError(f"Could not create network configuration file: {e}")

self._networks[name] = network

return network

def _connect_to_network(self, container: "Container", network: "Network"):
"""There is no way to get MAC address of remote interface except
executing "ip l" inside container.
Expand Down Expand Up @@ -292,7 +341,7 @@ def _connect_to_networks(self, container: "Container", network_reqs: dict):
name = params["network"]
logging.debug(f"Connecting {container.name} to {name}")

network = self._create_network(name)
network = self._create_network(name, self.network_plugin)

self._connect_to_network(container, network)

Expand Down
Loading