Skip to content

Commit

Permalink
Add lab_hash to Docker networks names (#256)
Browse files Browse the repository at this point in the history
This commit adds the `lab_hash` to Docker networks names and adds an option in the Shared Collision Domain of the `DockerSettingsAddon` to share collision domains between network scenarios of the same user or between different users.
  • Loading branch information
tcaiazzi committed Dec 21, 2023
1 parent 5b2c51b commit fdbc515
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 42 deletions.
31 changes: 20 additions & 11 deletions src/Kathara/cli/ui/setting/DockerOptionsHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ....trdparty.consolemenu import *
from ....trdparty.consolemenu.items import *
from ....trdparty.consolemenu.validators.regex import RegexValidator
from ....types import SharedCollisionDomainsOption
from ....utils import exec_by_platform


Expand Down Expand Up @@ -130,28 +131,36 @@ def add_items(self, current_menu: ConsoleMenu, menu_formatter: MenuFormatBuilder

image_update_policy_item = SubmenuItem(image_update_policy_string, image_update_policy_menu, current_menu)

# Shared Links Option
shared_cd_string = "Enable Shared Collision Domains between users"
# Shared Collision Domains Option
shared_cd_string = "Enable Shared Collision Domains"
shared_cd_menu = SelectionMenu(strings=[],
title=shared_cd_string,
subtitle=setting_utils.current_bool("shared_cd"),
prologue_text="""This option allows to connect devices of different users to """
"""the same collision domains.
subtitle=setting_utils.current_enum("shared_cd",
SharedCollisionDomainsOption.to_string
),
prologue_text="""This option allows sharing collision domains between """
"""network scenarios and users.
Default is %s.""" %
setting_utils.format_bool(DEFAULTS['shared_cd']),
Default is: %s.""" % DEFAULTS['shared_cd'],
formatter=menu_formatter
)

shared_cd_menu.append_item(FunctionItem(text="Yes",
shared_cd_menu.append_item(FunctionItem(text="Share collision domains between network scenarios",
function=setting_utils.update_setting_value,
args=["shared_cd", True],
args=["shared_cd", SharedCollisionDomainsOption.LABS],
should_exit=True
)
)
shared_cd_menu.append_item(FunctionItem(text="No",
shared_cd_menu.append_item(FunctionItem(text="Share collision domains between users",
function=setting_utils.update_setting_value,
args=["shared_cd", False],
args=["shared_cd", SharedCollisionDomainsOption.USERS],
should_exit=True
)
)

shared_cd_menu.append_item(FunctionItem(text="Do not share collision domains",
function=setting_utils.update_setting_value,
args=["shared_cd", SharedCollisionDomainsOption.NOT_SHARED],
should_exit=True
)
)
Expand Down
7 changes: 7 additions & 0 deletions src/Kathara/cli/ui/setting/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from enum import Enum
from typing import Optional, Callable, Any, Tuple, List

from ....exceptions import SettingsError
Expand Down Expand Up @@ -34,6 +35,12 @@ def current_string(attribute_name: str, text: Optional[str] = None) -> Callable[
")" if text else ""
)

def current_enum(attribute_name: str, to_string: Callable[[int], str], text: Optional[str] = None) -> Callable[[], str]:
return lambda: "%sCurrent: %s%s" % (text + " (" if text else "",
to_string(getattr(Setting.get_instance(), attribute_name)),
")" if text else ""
)


def update_setting_value(attribute_name: str, value: Any, stdout: bool = True) -> None:
reload = False
Expand Down
25 changes: 16 additions & 9 deletions src/Kathara/manager/docker/DockerLink.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from ...model.Link import BRIDGE_LINK_NAME, Link
from ...os.Networking import Networking
from ...setting.Setting import Setting
from ...types import SharedCollisionDomainsOption


class DockerLink(object):
Expand Down Expand Up @@ -95,21 +96,23 @@ def create(self, link: Link) -> None:
return

# If a network with the same name exists, return it instead of creating a new one.
link_name = self.get_network_name(link.name)
link_name = self.get_network_name(link)
networks = self.get_links_api_objects_by_filters(link_name=link_name)
if networks:
link.api_object = networks.pop()
else:
network_ipam_config = docker.types.IPAMConfig(driver='null')

user_label = "shared_cd" if Setting.get_instance().shared_cd else utils.get_current_user_name()
user_label = "shared_cd" if Setting.get_instance().shared_cd == SharedCollisionDomainsOption.USERS \
else utils.get_current_user_name()
link.api_object = self.client.networks.create(
name=link_name,
driver=f"{Setting.get_instance().network_plugin}:{utils.get_architecture()}",
check_duplicate=True,
ipam=network_ipam_config,
labels={
"lab_hash": link.lab.hash,
"lab_hash": link.lab.hash if
Setting.get_instance().shared_cd == SharedCollisionDomainsOption.NOT_SHARED else None,
"name": link.name,
"user": user_label,
"app": "kathara",
Expand Down Expand Up @@ -161,7 +164,7 @@ def wipe(self, user: str = None) -> None:
Returns:
None
"""
user_label = "shared_cd" if Setting.get_instance().shared_cd else user
user_label = "shared_cd" if Setting.get_instance().shared_cd == SharedCollisionDomainsOption.USERS else user
networks = self.get_links_api_objects_by_filters(user=user_label)
for item in networks:
item.reload()
Expand Down Expand Up @@ -350,18 +353,22 @@ def _get_bridge_name(network: docker.models.networks.Network) -> str:
Returns:
str: The name of the Docker bridge in the format "kt-<network.id[:12]>".
"""
return "kt-%s" % network.id[:12]
return f"kt-{network.id[:12]}"

@staticmethod
def get_network_name(name: str) -> str:
def get_network_name(link: Link) -> str:
"""Return the name of a Docker network.
Args:
name (str): The name of a Kathara collision domain.
link (Kathara.model.Link): A Kathara collision domain.
Returns:
str: The name of the Docker network in the format "|net_prefix|_|username_prefix|_|name|".
If shared collision domains, the format is: "|net_prefix|_|lab_hash|".
"""
username_prefix = "_%s" % utils.get_current_user_name() if not Setting.get_instance().shared_cd else ""
return "%s%s_%s" % (Setting.get_instance().net_prefix, username_prefix, name)
if Setting.get_instance().shared_cd == SharedCollisionDomainsOption.LABS:
return f"{Setting.get_instance().net_prefix}_{utils.get_current_user_name()}_{link.name}"
elif Setting.get_instance().shared_cd == SharedCollisionDomainsOption.USERS:
return f"{Setting.get_instance().net_prefix}_{link.name}"
elif Setting.get_instance().shared_cd == SharedCollisionDomainsOption.NOT_SHARED:
return f"{Setting.get_instance().net_prefix}_{utils.get_current_user_name()}_{link.lab.hash}_{link.name}"
15 changes: 12 additions & 3 deletions src/Kathara/manager/docker/DockerManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ...model.Link import Link
from ...model.Machine import Machine
from ...setting.Setting import Setting
from ...types import SharedCollisionDomainsOption
from ...utils import pack_files_for_tar, import_pywintypes

pywintypes = import_pywintypes()
Expand Down Expand Up @@ -516,7 +517,11 @@ def get_lab_from_api(self, lab_hash: str = None, lab_name: str = None) -> Lab:

lab_containers = self.get_machines_api_objects(lab_hash=reconstructed_lab.hash)
lab_networks = dict(
map(lambda x: (x.name, x), self.get_links_api_objects(lab_hash=reconstructed_lab.hash))
map(lambda x: (x.name, x), self.get_links_api_objects(
lab_hash=reconstructed_lab.hash \
if Setting.get_instance().shared_cd == SharedCollisionDomainsOption.NOT_SHARED else None,
all_users=Setting.get_instance().shared_cd == SharedCollisionDomainsOption.USERS
))
)

for container in lab_containers:
Expand Down Expand Up @@ -580,13 +585,17 @@ def update_lab_from_api(self, lab: Lab) -> None:
running_containers = self.get_machines_api_objects(lab_hash=lab.hash)

deployed_networks = dict(
map(lambda x: (x.name, x), self.get_links_api_objects(lab_hash=lab.hash))
map(lambda x: (x.name, x), self.get_links_api_objects(
lab_hash=lab.hash \
if Setting.get_instance().shared_cd == SharedCollisionDomainsOption.NOT_SHARED else None,
all_users=Setting.get_instance().shared_cd == SharedCollisionDomainsOption.USERS
))
)
for network in deployed_networks.values():
network.reload()

deployed_networks_by_link_name = dict(
map(lambda x: (x.attrs["Labels"]["name"], x), self.get_links_api_objects(lab_hash=lab.hash))
map(lambda x: (x.attrs["Labels"]["name"], x), deployed_networks.values())
)

for container in running_containers:
Expand Down
9 changes: 5 additions & 4 deletions src/Kathara/setting/addon/DockerSettingsAddon.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
from typing import Optional, Dict, Any

from ...foundation.setting.SettingsAddon import SettingsAddon
from ...types import SharedCollisionDomainsOption

DEFAULTS = {
"hosthome_mount": False,
"shared_mount": True,
"image_update_policy": "Prompt",
"shared_cd": False,
"shared_cd": SharedCollisionDomainsOption.NOT_SHARED,
"remote_url": None,
"cert_path": None,
"network_plugin": "kathara/katharanp_vde"
}


class DockerSettingsAddon(SettingsAddon):
__slots__ = ['hosthome_mount', 'shared_mount', 'image_update_policy', 'shared_cd', 'remote_url', 'cert_path',
'network_plugin']
__slots__ = ['hosthome_mount', 'shared_mount', 'image_update_policy', 'shared_cd',
'remote_url', 'cert_path', 'network_plugin']

def __init__(self) -> None:
self.hosthome_mount: bool = False
self.shared_mount: bool = True
self.image_update_policy: str = 'Prompt'
self.shared_cd: bool = False
self.shared_cd: int = SharedCollisionDomainsOption.NOT_SHARED
self.remote_url: Optional[str] = None
self.cert_path: Optional[str] = None
self.network_plugin: Optional[str] = "kathara/katharanp_vde"
Expand Down
25 changes: 25 additions & 0 deletions src/Kathara/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from enum import IntEnum


class SharedCollisionDomainsOption(IntEnum):
"""Enum representing options for shared collision domains option.
Attributes:
NOT_SHARED (int): Represents the option for not sharing collision domains (value: 1).
LABS (int): Represents the option for sharing collision domains among network scenarios of the same user
(value: 2).
USERS (int): Represents the option for sharing collision domains among network scenarios of different0 users
(value: 3).
"""
NOT_SHARED = 1
LABS = 2
USERS = 3

@staticmethod
def to_string(value):
if value == 1:
return "Not shared"
elif value == 2:
return "Share collision domain between network scenarios"
elif value == 3:
return "Share collision domain between users"
Loading

0 comments on commit fdbc515

Please sign in to comment.