From ceec61999a3b48774c83679ce210b907f5dccaa8 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Wed, 13 Dec 2023 11:04:25 -0800 Subject: [PATCH] daemon: adjustments to clean up the new net commands for nodes, will now function with ovs mode as well --- daemon/core/nodes/base.py | 73 +++++++++++++++++----------- daemon/core/nodes/docker.py | 11 ----- daemon/core/nodes/interface.py | 4 +- daemon/core/nodes/netclient.py | 88 ++++++++++++++-------------------- daemon/core/nodes/podman.py | 29 ----------- 5 files changed, 83 insertions(+), 122 deletions(-) diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index a527b670..1a2fa468 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -153,10 +153,19 @@ def __init__( self.net_client: LinuxNetClient = get_net_client( self.session.use_ovs(), self.host_cmd ) + self.node_net_client: LinuxNetClient = self._get_node_net_client() options = options if options else NodeOptions() self.canvas: Optional[int] = options.canvas self.icon: Optional[str] = options.icon + def _get_node_net_client(self) -> LinuxNetClient: + """ + Create and return network command client to run within context of the node. + + :return: network command client + """ + return get_net_client(self.session.use_ovs(), self.cmd) + @classmethod def create_options(cls) -> NodeOptions: return NodeOptions() @@ -228,19 +237,6 @@ def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: """ return self.host_cmd(args, wait=wait, shell=shell) - def net_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: - """ - Runs a network command that is in the context of a node, default is to run a - standard host command. - - :param args: command to run - :param wait: True to wait for status, False otherwise - :param shell: True to use shell, False otherwise - :return: combined stdout and stderr - :raises CoreCommandError: when a non-zero exit status occurs - """ - return self.cmd(args, wait, shell) - def setposition(self, x: float = None, y: float = None, z: float = None) -> bool: """ Set the (x,y,z) position of the object. @@ -590,9 +586,6 @@ def __init__( self.ctrlchnlname: Path = self.session.directory / self.name self.pid: Optional[int] = None self._mounts: list[tuple[Path, Path]] = [] - self.node_net_client: LinuxNetClient = self.create_node_net_client( - self.session.use_ovs() - ) options = options or CoreNodeOptions() self.model: Optional[str] = options.model # add services @@ -609,19 +602,17 @@ def __init__( service_class = self.session.service_manager.get_service(name) self.add_service(service_class) - @classmethod - def create_options(cls) -> CoreNodeOptions: - return CoreNodeOptions() - - def create_node_net_client(self, use_ovs: bool) -> LinuxNetClient: + def _get_node_net_client(self) -> LinuxNetClient: """ - Create node network client for running network commands within the nodes - container. + Create and return network command client to run within context of the node. - :param use_ovs: True for OVS bridges, False for Linux bridges - :return: node network client + :return: network command client """ - return get_net_client(use_ovs, self.cmd) + return get_net_client(self.session.use_ovs(), self.net_cmd) + + @classmethod + def create_options(cls) -> CoreNodeOptions: + return CoreNodeOptions() def alive(self) -> bool: """ @@ -665,7 +656,8 @@ def startup(self) -> None: self.node_net_client.device_up("lo") # set hostname for node logger.debug("setting hostname: %s", self.name) - self.node_net_client.set_hostname(self.name) + hostname = self.name.replace("_", "-") + self.cmd(f"hostname {hostname}") # mark node as up self.up = True # create private directories @@ -740,6 +732,33 @@ def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: else: return self.server.remote_cmd(args, wait=wait) + def create_net_cmd(self, args: str, shell: bool = False) -> str: + """ + Create command used to run network commands within the context of a node. + + :param args: command arguments + :param shell: True to run shell like, False otherwise + :return: node command + """ + return self.create_cmd(args, shell) + + def net_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: + """ + Runs a command that is used to configure and setup the network within a + node. + + :param args: command to run + :param wait: True to wait for status, False otherwise + :param shell: True to use shell, False otherwise + :return: combined stdout and stderr + :raises CoreCommandError: when a non-zero exit status occurs + """ + args = self.create_net_cmd(args, shell) + if self.server is None: + return utils.cmd(args, wait=wait, shell=shell) + else: + return self.server.remote_cmd(args, wait=wait) + def path_exists(self, path: str) -> bool: """ Determines if a file or directory path exists. diff --git a/daemon/core/nodes/docker.py b/daemon/core/nodes/docker.py index 85dc14d4..a989a5c3 100644 --- a/daemon/core/nodes/docker.py +++ b/daemon/core/nodes/docker.py @@ -13,7 +13,6 @@ from core.errors import CoreCommandError, CoreError from core.executables import BASH from core.nodes.base import CoreNode, CoreNodeOptions -from core.nodes.netclient import LinuxNetClient, get_net_client logger = logging.getLogger(__name__) @@ -100,16 +99,6 @@ def create_options(cls) -> DockerOptions: """ return DockerOptions() - def create_node_net_client(self, use_ovs: bool) -> LinuxNetClient: - """ - Create node network client for running network commands within the nodes - container. - - :param use_ovs: True for OVS bridges, False for Linux bridges - :return: node network client - """ - return get_net_client(use_ovs, self.cmd, self.net_cmd) - def create_cmd(self, args: str, shell: bool = False) -> str: """ Create command used to run commands within the context of a node. diff --git a/daemon/core/nodes/interface.py b/daemon/core/nodes/interface.py index 5520feab..2b125f1f 100644 --- a/daemon/core/nodes/interface.py +++ b/daemon/core/nodes/interface.py @@ -317,7 +317,7 @@ def set_config(self) -> None: if self.has_netem: cmd = tc_clear_cmd(self.name) if self.node: - self.node.net_cmd(cmd) + self.node.node_net_client.run(cmd) else: self.host_cmd(cmd) self.has_netem = False @@ -325,7 +325,7 @@ def set_config(self) -> None: else: cmd = tc_cmd(self.name, self.options, self.mtu) if self.node: - self.node.net_cmd(cmd) + self.node.node_net_client.run(cmd) else: self.host_cmd(cmd) self.has_netem = True diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py index 908f8bc8..593b4761 100644 --- a/daemon/core/nodes/netclient.py +++ b/daemon/core/nodes/netclient.py @@ -14,25 +14,13 @@ class LinuxNetClient: Client for creating Linux bridges and ip interfaces for nodes. """ - def __init__(self, run: Callable[..., str], run_net: Callable[..., str]) -> None: + def __init__(self, run: Callable[..., str]) -> None: """ Create LinuxNetClient instance. :param run: function to run commands within node context - :param run_net: function to run commands within network context only """ self.run: Callable[..., str] = run - self.run_net: Callable[..., str] = run_net - - def set_hostname(self, name: str) -> None: - """ - Set network hostname. - - :param name: name for hostname - :return: nothing - """ - name = name.replace("_", "-") - self.run(f"hostname {name}") def create_route(self, route: str, device: str) -> None: """ @@ -42,7 +30,7 @@ def create_route(self, route: str, device: str) -> None: :param device: device to add route to :return: nothing """ - self.run_net(f"{IP} route replace {route} dev {device}") + self.run(f"{IP} route replace {route} dev {device}") def device_up(self, device: str) -> None: """ @@ -51,7 +39,7 @@ def device_up(self, device: str) -> None: :param device: device to bring up :return: nothing """ - self.run_net(f"{IP} link set {device} up") + self.run(f"{IP} link set {device} up") def device_down(self, device: str) -> None: """ @@ -60,7 +48,7 @@ def device_down(self, device: str) -> None: :param device: device to bring down :return: nothing """ - self.run_net(f"{IP} link set {device} down") + self.run(f"{IP} link set {device} down") def device_name(self, device: str, name: str) -> None: """ @@ -70,7 +58,7 @@ def device_name(self, device: str, name: str) -> None: :param name: name to set :return: nothing """ - self.run_net(f"{IP} link set {device} name {name}") + self.run(f"{IP} link set {device} name {name}") def device_show(self, device: str) -> str: """ @@ -79,7 +67,7 @@ def device_show(self, device: str) -> str: :param device: device to get information for :return: device information """ - return self.run_net(f"{IP} link show {device}") + return self.run(f"{IP} link show {device}") def address_show(self, device: str) -> str: """ @@ -88,7 +76,7 @@ def address_show(self, device: str) -> str: :param device: device name :return: address information """ - return self.run_net(f"{IP} address show {device}") + return self.run(f"{IP} address show {device}") def get_ifindex(self, device: str) -> int: """ @@ -97,7 +85,8 @@ def get_ifindex(self, device: str) -> int: :param device: device to get ifindex for :return: ifindex """ - return int(self.run(f"cat /sys/class/net/{device}/ifindex")) + output = self.run(f"{IP} link show {device}") + return int(output.split()[0].strip(":")) def device_ns(self, device: str, namespace: str) -> None: """ @@ -107,7 +96,7 @@ def device_ns(self, device: str, namespace: str) -> None: :param namespace: namespace to set device to :return: nothing """ - self.run_net(f"{IP} link set {device} netns {namespace}") + self.run(f"{IP} link set {device} netns {namespace}") def device_flush(self, device: str) -> None: """ @@ -116,7 +105,7 @@ def device_flush(self, device: str) -> None: :param device: device to flush :return: nothing """ - self.run_net(f"{IP} address flush dev {device}") + self.run(f"{IP} address flush dev {device}") def device_mac(self, device: str, mac: str) -> None: """ @@ -126,7 +115,7 @@ def device_mac(self, device: str, mac: str) -> None: :param mac: mac to set :return: nothing """ - self.run_net(f"{IP} link set dev {device} address {mac}") + self.run(f"{IP} link set dev {device} address {mac}") def delete_device(self, device: str) -> None: """ @@ -135,7 +124,7 @@ def delete_device(self, device: str) -> None: :param device: device to delete :return: nothing """ - self.run_net(f"{IP} link delete {device}") + self.run(f"{IP} link delete {device}") def delete_tc(self, device: str) -> None: """ @@ -144,7 +133,7 @@ def delete_tc(self, device: str) -> None: :param device: device to remove tc :return: nothing """ - self.run_net(f"{TC} qdisc delete dev {device} root") + self.run(f"{TC} qdisc delete dev {device} root") def checksums_off(self, iface_name: str) -> None: """ @@ -153,7 +142,7 @@ def checksums_off(self, iface_name: str) -> None: :param iface_name: interface to update :return: nothing """ - self.run_net(f"{ETHTOOL} -K {iface_name} rx off tx off") + self.run(f"{ETHTOOL} -K {iface_name} rx off tx off") def create_address(self, device: str, address: str, broadcast: str = None) -> None: """ @@ -165,16 +154,14 @@ def create_address(self, device: str, address: str, broadcast: str = None) -> No :return: nothing """ if broadcast is not None: - self.run_net( - f"{IP} address add {address} broadcast {broadcast} dev {device}" - ) + self.run(f"{IP} address add {address} broadcast {broadcast} dev {device}") else: - self.run_net(f"{IP} address add {address} dev {device}") + self.run(f"{IP} address add {address} dev {device}") if netaddr.valid_ipv6(address.split("/")[0]): # IPv6 addresses are removed by default on interface down. # Make sure that the IPv6 address we add is not removed device = utils.sysctl_devname(device) - self.run_net(f"{SYSCTL} -w net.ipv6.conf.{device}.keep_addr_on_down=1") + self.run(f"{SYSCTL} -w net.ipv6.conf.{device}.keep_addr_on_down=1") def delete_address(self, device: str, address: str) -> None: """ @@ -184,7 +171,7 @@ def delete_address(self, device: str, address: str) -> None: :param address: address to remove :return: nothing """ - self.run_net(f"{IP} address delete {address} dev {device}") + self.run(f"{IP} address delete {address} dev {device}") def create_veth(self, name: str, peer: str) -> None: """ @@ -194,7 +181,7 @@ def create_veth(self, name: str, peer: str) -> None: :param peer: peer name :return: nothing """ - self.run_net(f"{IP} link add name {name} type veth peer name {peer}") + self.run(f"{IP} link add name {name} type veth peer name {peer}") def create_gretap( self, device: str, address: str, local: str, ttl: int, key: int @@ -216,7 +203,7 @@ def create_gretap( cmd += f" ttl {ttl}" if key is not None: cmd += f" key {key}" - self.run_net(cmd) + self.run(cmd) def create_bridge(self, name: str) -> None: """ @@ -225,11 +212,11 @@ def create_bridge(self, name: str) -> None: :param name: bridge name :return: nothing """ - self.run_net(f"{IP} link add name {name} type bridge") - self.run_net(f"{IP} link set {name} type bridge stp_state 0") - self.run_net(f"{IP} link set {name} type bridge forward_delay 0") - self.run_net(f"{IP} link set {name} type bridge mcast_snooping 0") - self.run_net(f"{IP} link set {name} type bridge group_fwd_mask 65528") + self.run(f"{IP} link add name {name} type bridge") + self.run(f"{IP} link set {name} type bridge stp_state 0") + self.run(f"{IP} link set {name} type bridge forward_delay 0") + self.run(f"{IP} link set {name} type bridge mcast_snooping 0") + self.run(f"{IP} link set {name} type bridge group_fwd_mask 65528") self.device_up(name) def delete_bridge(self, name: str) -> None: @@ -240,7 +227,7 @@ def delete_bridge(self, name: str) -> None: :return: nothing """ self.device_down(name) - self.run_net(f"{IP} link delete {name} type bridge") + self.run(f"{IP} link delete {name} type bridge") def set_iface_master(self, bridge_name: str, iface_name: str) -> None: """ @@ -250,7 +237,7 @@ def set_iface_master(self, bridge_name: str, iface_name: str) -> None: :param iface_name: interface name :return: nothing """ - self.run_net(f"{IP} link set dev {iface_name} master {bridge_name}") + self.run(f"{IP} link set dev {iface_name} master {bridge_name}") self.device_up(iface_name) def delete_iface(self, bridge_name: str, iface_name: str) -> None: @@ -261,7 +248,7 @@ def delete_iface(self, bridge_name: str, iface_name: str) -> None: :param iface_name: interface name :return: nothing """ - self.run_net(f"{IP} link set dev {iface_name} nomaster") + self.run(f"{IP} link set dev {iface_name} nomaster") def existing_bridges(self, _id: int) -> bool: """ @@ -270,7 +257,7 @@ def existing_bridges(self, _id: int) -> bool: :param _id: node id to check bridges for :return: True if there are existing bridges, False otherwise """ - output = self.run_net(f"{IP} -o link show type bridge") + output = self.run(f"{IP} -o link show type bridge") lines = output.split("\n") for line in lines: values = line.split(":") @@ -292,7 +279,7 @@ def set_mac_learning(self, name: str, value: int) -> None: :param value: ageing time value :return: nothing """ - self.run_net(f"{IP} link set {name} type bridge ageing_time {value}") + self.run(f"{IP} link set {name} type bridge ageing_time {value}") def set_mtu(self, name: str, value: int) -> None: """ @@ -302,7 +289,7 @@ def set_mtu(self, name: str, value: int) -> None: :param value: mtu value to set :return: nothing """ - self.run_net(f"{IP} link set {name} mtu {value}") + self.run(f"{IP} link set {name} mtu {value}") class OvsNetClient(LinuxNetClient): @@ -380,20 +367,15 @@ def set_mac_learning(self, name: str, value: int) -> None: self.run(f"{OVS_VSCTL} set bridge {name} other_config:mac-aging-time={value}") -def get_net_client( - use_ovs: bool, run: Callable[..., str], run_net: Callable[..., str] = None -) -> LinuxNetClient: +def get_net_client(use_ovs: bool, run: Callable[..., str]) -> LinuxNetClient: """ Retrieve desired net client for running network commands. :param use_ovs: True for OVS bridges, False for Linux bridges :param run: function to run commands within node context - :param run_net: function to run commands within network context only :return: net client class """ - if run_net is None: - run_net = run if use_ovs: - return OvsNetClient(run, run_net) + return OvsNetClient(run) else: - return LinuxNetClient(run, run_net) + return LinuxNetClient(run) diff --git a/daemon/core/nodes/podman.py b/daemon/core/nodes/podman.py index 3d58bb93..dd6bcc36 100644 --- a/daemon/core/nodes/podman.py +++ b/daemon/core/nodes/podman.py @@ -8,12 +8,10 @@ from mako.template import Template -from core import utils from core.emulator.distributed import DistributedServer from core.errors import CoreCommandError, CoreError from core.executables import BASH from core.nodes.base import CoreNode, CoreNodeOptions -from core.nodes.netclient import LinuxNetClient, get_net_client logger = logging.getLogger(__name__) @@ -99,16 +97,6 @@ def create_options(cls) -> PodmanOptions: """ return PodmanOptions() - def create_node_net_client(self, use_ovs: bool) -> LinuxNetClient: - """ - Create node network client for running network commands within the nodes - container. - - :param use_ovs: True for OVS bridges, False for Linux bridges - :return: node network client - """ - return get_net_client(use_ovs, self.cmd, self.net_cmd) - def create_cmd(self, args: str, shell: bool = False) -> str: """ Create command used to run commands within the context of a node. @@ -133,23 +121,6 @@ def create_net_cmd(self, args: str, shell: bool = False) -> str: args = f"{BASH} -c {shlex.quote(args)}" return f"nsenter -t {self.pid} -n -- {args}" - def net_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: - """ - Runs a command that is used to configure and setup the network within a - node. - - :param args: command to run - :param wait: True to wait for status, False otherwise - :param shell: True to use shell, False otherwise - :return: combined stdout and stderr - :raises CoreCommandError: when a non-zero exit status occurs - """ - args = self.create_net_cmd(args, shell) - if self.server is None: - return utils.cmd(args, wait=wait, shell=shell) - else: - return self.server.remote_cmd(args, wait=wait) - def _unique_name(self, name: str) -> str: """ Creates a session/node unique prefixed name for the provided input.