daemon: refactored how interfaces are configured, updated link edits to allow proper bi-directional support for network to network interfaces, improved and added more unit tests for link add/edit/delete
This commit is contained in:
parent
bc8c49c573
commit
e9b83b0d28
8 changed files with 348 additions and 252 deletions
|
@ -13,7 +13,7 @@ import netaddr
|
|||
|
||||
from core import utils
|
||||
from core.configservice.dependencies import ConfigServiceDependencies
|
||||
from core.emulator.data import InterfaceData, LinkData, LinkOptions
|
||||
from core.emulator.data import InterfaceData, LinkData
|
||||
from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.executables import MOUNT, TEST, VNODED
|
||||
|
@ -1000,20 +1000,6 @@ class CoreNetworkBase(NodeBase):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def linkconfig(
|
||||
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
|
||||
) -> None:
|
||||
"""
|
||||
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||
|
||||
:param iface: interface one
|
||||
:param options: options for configuring link
|
||||
:param iface2: interface two
|
||||
:return: nothing
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def custom_iface(self, node: CoreNode, iface_data: InterfaceData) -> CoreInterface:
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ virtual ethernet classes that implement the interfaces available under Linux.
|
|||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
|
||||
|
@ -13,6 +14,7 @@ from core import utils
|
|||
from core.emulator.data import LinkOptions
|
||||
from core.emulator.enumerations import TransportType
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.executables import TC
|
||||
from core.nodes.netclient import LinuxNetClient, get_net_client
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -336,6 +338,92 @@ class CoreInterface:
|
|||
"""
|
||||
return self.transport_type == TransportType.VIRTUAL
|
||||
|
||||
def _set_params_change(self, **kwargs: float) -> bool:
|
||||
"""
|
||||
Set parameters to change.
|
||||
|
||||
:param kwargs: parameter name and values to change
|
||||
:return: True if any parameter changed, False otherwise
|
||||
"""
|
||||
return any([self.setparam(k, v) for k, v in kwargs.items()])
|
||||
|
||||
def config(self, options: LinkOptions, use_local: bool = True) -> None:
|
||||
"""
|
||||
Configure interface using tc based on existing state and provided
|
||||
link options.
|
||||
|
||||
:param options: options to configure with
|
||||
:param use_local: True to use localname for device, False for name
|
||||
:return: nothing
|
||||
"""
|
||||
# determine if any settings have changed
|
||||
if use_local:
|
||||
devname = self.localname
|
||||
changed = self._set_params_change(
|
||||
bw=options.bandwidth,
|
||||
delay=options.delay,
|
||||
loss=options.loss,
|
||||
duplicate=options.dup,
|
||||
jitter=options.jitter,
|
||||
buffer=options.buffer,
|
||||
)
|
||||
else:
|
||||
devname = self.name
|
||||
changed = self._set_params_change(
|
||||
n_bw=options.bandwidth,
|
||||
n_delay=options.delay,
|
||||
n_loss=options.loss,
|
||||
n_duplicate=options.dup,
|
||||
n_jitter=options.jitter,
|
||||
n_buffer=options.buffer,
|
||||
)
|
||||
if not changed:
|
||||
return
|
||||
# delete tc configuration or create and add it
|
||||
if all(
|
||||
[
|
||||
options.delay is None or options.delay <= 0,
|
||||
options.jitter is None or options.jitter <= 0,
|
||||
options.loss is None or options.loss <= 0,
|
||||
options.dup is None or options.dup <= 0,
|
||||
options.bandwidth is None or options.bandwidth <= 0,
|
||||
options.buffer is None or options.buffer <= 0,
|
||||
]
|
||||
):
|
||||
if not self.getparam("has_netem"):
|
||||
return
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc delete dev {devname} root handle 10:"
|
||||
self.host_cmd(cmd)
|
||||
self.setparam("has_netem", False)
|
||||
else:
|
||||
netem = ""
|
||||
if options.bandwidth is not None:
|
||||
limit = 1000
|
||||
bw = options.bandwidth / 1000
|
||||
if options.buffer is not None and options.buffer > 0:
|
||||
limit = options.buffer
|
||||
elif options.delay and options.bandwidth:
|
||||
delay = options.delay / 1000
|
||||
limit = max(2, math.ceil((2 * bw * delay) / (8 * self.mtu)))
|
||||
netem += f" rate {bw}kbit"
|
||||
netem += f" limit {limit}"
|
||||
if options.delay is not None:
|
||||
netem += f" delay {options.delay}us"
|
||||
if options.jitter is not None:
|
||||
if options.delay is None:
|
||||
netem += f" delay 0us {options.jitter}us 25%"
|
||||
else:
|
||||
netem += f" {options.jitter}us 25%"
|
||||
if options.loss is not None and options.loss > 0:
|
||||
netem += f" loss {min(options.loss, 100)}%"
|
||||
if options.dup is not None and options.dup > 0:
|
||||
netem += f" duplicate {min(options.dup, 100)}%"
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc replace dev {devname} root handle 10: netem {netem}"
|
||||
self.host_cmd(cmd)
|
||||
self.setparam("has_netem", True)
|
||||
|
||||
|
||||
class Veth(CoreInterface):
|
||||
"""
|
||||
|
|
|
@ -3,7 +3,6 @@ Defines network nodes used within core.
|
|||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
import threading
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
|
@ -14,7 +13,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Type
|
|||
import netaddr
|
||||
|
||||
from core import utils
|
||||
from core.emulator.data import InterfaceData, LinkData, LinkOptions
|
||||
from core.emulator.data import InterfaceData, LinkData
|
||||
from core.emulator.enumerations import (
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
|
@ -23,7 +22,7 @@ from core.emulator.enumerations import (
|
|||
RegisterTlvs,
|
||||
)
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.executables import NFTABLES, TC
|
||||
from core.executables import NFTABLES
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.interface import CoreInterface, GreTap, Veth
|
||||
from core.nodes.netclient import get_net_client
|
||||
|
@ -400,77 +399,6 @@ class CoreNetwork(CoreNetworkBase):
|
|||
self.linked[iface1][iface2] = True
|
||||
nft_queue.update(self)
|
||||
|
||||
def linkconfig(
|
||||
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
|
||||
) -> None:
|
||||
"""
|
||||
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||
|
||||
:param iface: interface one
|
||||
:param options: options for configuring link
|
||||
:param iface2: interface two
|
||||
:return: nothing
|
||||
"""
|
||||
# determine if any settings have changed
|
||||
changed = any(
|
||||
[
|
||||
iface.setparam("bw", options.bandwidth),
|
||||
iface.setparam("delay", options.delay),
|
||||
iface.setparam("loss", options.loss),
|
||||
iface.setparam("duplicate", options.dup),
|
||||
iface.setparam("jitter", options.jitter),
|
||||
iface.setparam("buffer", options.buffer),
|
||||
]
|
||||
)
|
||||
if not changed:
|
||||
return
|
||||
|
||||
# delete tc configuration or create and add it
|
||||
devname = iface.localname
|
||||
if all(
|
||||
[
|
||||
options.delay is None or options.delay <= 0,
|
||||
options.jitter is None or options.jitter <= 0,
|
||||
options.loss is None or options.loss <= 0,
|
||||
options.dup is None or options.dup <= 0,
|
||||
options.bandwidth is None or options.bandwidth <= 0,
|
||||
options.buffer is None or options.buffer <= 0,
|
||||
]
|
||||
):
|
||||
if not iface.getparam("has_netem"):
|
||||
return
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc delete dev {devname} root handle 10:"
|
||||
iface.host_cmd(cmd)
|
||||
iface.setparam("has_netem", False)
|
||||
else:
|
||||
netem = ""
|
||||
if options.bandwidth is not None:
|
||||
limit = 1000
|
||||
bw = options.bandwidth / 1000
|
||||
if options.buffer is not None and options.buffer > 0:
|
||||
limit = options.buffer
|
||||
elif options.delay and options.bandwidth:
|
||||
delay = options.delay / 1000
|
||||
limit = max(2, math.ceil((2 * bw * delay) / (8 * iface.mtu)))
|
||||
netem += f" rate {bw}kbit"
|
||||
netem += f" limit {limit}"
|
||||
if options.delay is not None:
|
||||
netem += f" delay {options.delay}us"
|
||||
if options.jitter is not None:
|
||||
if options.delay is None:
|
||||
netem += f" delay 0us {options.jitter}us 25%"
|
||||
else:
|
||||
netem += f" {options.jitter}us 25%"
|
||||
if options.loss is not None and options.loss > 0:
|
||||
netem += f" loss {min(options.loss, 100)}%"
|
||||
if options.dup is not None and options.dup > 0:
|
||||
netem += f" duplicate {min(options.dup, 100)}%"
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc replace dev {devname} root handle 10: netem {netem}"
|
||||
iface.host_cmd(cmd)
|
||||
iface.setparam("has_netem", True)
|
||||
|
||||
def linknet(self, net: CoreNetworkBase) -> CoreInterface:
|
||||
"""
|
||||
Link this bridge with another by creating a veth pair and installing
|
||||
|
|
|
@ -7,14 +7,13 @@ import threading
|
|||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, List, Optional, Tuple
|
||||
|
||||
from core.emulator.data import InterfaceData, LinkOptions
|
||||
from core.emulator.data import InterfaceData
|
||||
from core.emulator.distributed import DistributedServer
|
||||
from core.emulator.enumerations import NodeTypes, TransportType
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.executables import MOUNT, TEST, UMOUNT
|
||||
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||
from core.nodes.network import CoreNetwork
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -143,17 +142,6 @@ class PhysicalNode(CoreNodeBase):
|
|||
if self.up:
|
||||
self.net_client.device_up(iface.localname)
|
||||
|
||||
def linkconfig(
|
||||
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
|
||||
) -> None:
|
||||
"""
|
||||
Apply tc queing disciplines using linkconfig.
|
||||
"""
|
||||
linux_bridge = CoreNetwork(self.session)
|
||||
linux_bridge.up = True
|
||||
linux_bridge.linkconfig(iface, options, iface2)
|
||||
del linux_bridge
|
||||
|
||||
def next_iface_id(self) -> int:
|
||||
with self.lock:
|
||||
while self.iface_id in self.ifaces:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue