added type hinting for core.nodes
This commit is contained in:
parent
4e71759ac9
commit
c0fcc91d10
10 changed files with 534 additions and 360 deletions
|
|
@ -5,7 +5,7 @@ gRpc client for interfacing with CORE, when gRPC mode is enabled.
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Any, Callable, Dict, List
|
from typing import Any, Callable, Dict, Generator, List
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
@ -1144,7 +1144,7 @@ class CoreGrpcClient:
|
||||||
self.channel = None
|
self.channel = None
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def context_connect(self) -> None:
|
def context_connect(self) -> Generator:
|
||||||
"""
|
"""
|
||||||
Makes a context manager based connection to the server, will close after context ends.
|
Makes a context manager based connection to the server, will close after context ends.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from queue import Empty, Queue
|
from queue import Empty, Queue
|
||||||
from typing import List
|
from typing import Iterable
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
from core.api.grpc.grpcutils import convert_value
|
from core.api.grpc.grpcutils import convert_value
|
||||||
|
|
@ -181,7 +181,9 @@ class EventStreamer:
|
||||||
Processes session events to generate grpc events.
|
Processes session events to generate grpc events.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session: Session, event_types: List[core_pb2.EventType]) -> None:
|
def __init__(
|
||||||
|
self, session: Session, event_types: Iterable[core_pb2.EventType]
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a EventStreamer instance.
|
Create a EventStreamer instance.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import threading
|
import threading
|
||||||
|
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
|
|
@ -15,8 +16,12 @@ from core.emulator.data import LinkData, NodeData
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core.errors import CoreCommandError
|
from core.errors import CoreCommandError
|
||||||
from core.nodes import client
|
from core.nodes import client
|
||||||
from core.nodes.interface import TunTap, Veth
|
from core.nodes.interface import CoreInterface, TunTap, Veth
|
||||||
from core.nodes.netclient import get_net_client
|
from core.nodes.netclient import LinuxNetClient, get_net_client
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
|
from core.emulator.session import Session
|
||||||
|
|
||||||
_DEFAULT_MTU = 1500
|
_DEFAULT_MTU = 1500
|
||||||
|
|
||||||
|
|
@ -29,9 +34,16 @@ class NodeBase:
|
||||||
apitype = None
|
apitype = None
|
||||||
|
|
||||||
# TODO: appears start has no usage, verify and remove
|
# TODO: appears start has no usage, verify and remove
|
||||||
def __init__(self, session, _id=None, name=None, start=True, server=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
session: "Session",
|
||||||
|
_id: int = None,
|
||||||
|
name: str = None,
|
||||||
|
start: bool = True,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a PyCoreObj instance.
|
Creates a NodeBase instance.
|
||||||
|
|
||||||
:param core.emulator.session.Session session: CORE session object
|
:param core.emulator.session.Session session: CORE session object
|
||||||
:param int _id: id
|
:param int _id: id
|
||||||
|
|
@ -63,7 +75,7 @@ class NodeBase:
|
||||||
use_ovs = session.options.get_config("ovs") == "True"
|
use_ovs = session.options.get_config("ovs") == "True"
|
||||||
self.net_client = get_net_client(use_ovs, self.host_cmd)
|
self.net_client = get_net_client(use_ovs, self.host_cmd)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Each object implements its own startup method.
|
Each object implements its own startup method.
|
||||||
|
|
||||||
|
|
@ -71,7 +83,7 @@ class NodeBase:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Each object implements its own shutdown method.
|
Each object implements its own shutdown method.
|
||||||
|
|
||||||
|
|
@ -79,7 +91,14 @@ class NodeBase:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def host_cmd(self, args, env=None, cwd=None, wait=True, shell=False):
|
def host_cmd(
|
||||||
|
self,
|
||||||
|
args: str,
|
||||||
|
env: Dict[str, str] = None,
|
||||||
|
cwd: str = None,
|
||||||
|
wait: bool = True,
|
||||||
|
shell: bool = False,
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Runs a command on the host system or distributed server.
|
Runs a command on the host system or distributed server.
|
||||||
|
|
||||||
|
|
@ -97,7 +116,7 @@ class NodeBase:
|
||||||
else:
|
else:
|
||||||
return self.server.remote_cmd(args, env, cwd, wait)
|
return self.server.remote_cmd(args, env, cwd, wait)
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x: float = None, y: float = None, z: float = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Set the (x,y,z) position of the object.
|
Set the (x,y,z) position of the object.
|
||||||
|
|
||||||
|
|
@ -109,7 +128,7 @@ class NodeBase:
|
||||||
"""
|
"""
|
||||||
return self.position.set(x=x, y=y, z=z)
|
return self.position.set(x=x, y=y, z=z)
|
||||||
|
|
||||||
def getposition(self):
|
def getposition(self) -> Tuple[float, float, float]:
|
||||||
"""
|
"""
|
||||||
Return an (x,y,z) tuple representing this object's position.
|
Return an (x,y,z) tuple representing this object's position.
|
||||||
|
|
||||||
|
|
@ -118,7 +137,7 @@ class NodeBase:
|
||||||
"""
|
"""
|
||||||
return self.position.get()
|
return self.position.get()
|
||||||
|
|
||||||
def ifname(self, ifindex):
|
def ifname(self, ifindex: int) -> str:
|
||||||
"""
|
"""
|
||||||
Retrieve interface name for index.
|
Retrieve interface name for index.
|
||||||
|
|
||||||
|
|
@ -128,7 +147,7 @@ class NodeBase:
|
||||||
"""
|
"""
|
||||||
return self._netif[ifindex].name
|
return self._netif[ifindex].name
|
||||||
|
|
||||||
def netifs(self, sort=False):
|
def netifs(self, sort: bool = False) -> List[CoreInterface]:
|
||||||
"""
|
"""
|
||||||
Retrieve network interfaces, sorted if desired.
|
Retrieve network interfaces, sorted if desired.
|
||||||
|
|
||||||
|
|
@ -141,7 +160,7 @@ class NodeBase:
|
||||||
else:
|
else:
|
||||||
return list(self._netif.values())
|
return list(self._netif.values())
|
||||||
|
|
||||||
def numnetif(self):
|
def numnetif(self) -> int:
|
||||||
"""
|
"""
|
||||||
Return the attached interface count.
|
Return the attached interface count.
|
||||||
|
|
||||||
|
|
@ -150,7 +169,7 @@ class NodeBase:
|
||||||
"""
|
"""
|
||||||
return len(self._netif)
|
return len(self._netif)
|
||||||
|
|
||||||
def getifindex(self, netif):
|
def getifindex(self, netif: CoreInterface) -> int:
|
||||||
"""
|
"""
|
||||||
Retrieve index for an interface.
|
Retrieve index for an interface.
|
||||||
|
|
||||||
|
|
@ -163,7 +182,7 @@ class NodeBase:
|
||||||
return ifindex
|
return ifindex
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self) -> int:
|
||||||
"""
|
"""
|
||||||
Create a new interface index.
|
Create a new interface index.
|
||||||
|
|
||||||
|
|
@ -176,7 +195,14 @@ class NodeBase:
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def data(self, message_type, lat=None, lon=None, alt=None, source=None):
|
def data(
|
||||||
|
self,
|
||||||
|
message_type: int,
|
||||||
|
lat: float = None,
|
||||||
|
lon: float = None,
|
||||||
|
alt: float = None,
|
||||||
|
source: str = None,
|
||||||
|
) -> NodeData:
|
||||||
"""
|
"""
|
||||||
Build a data object for this node.
|
Build a data object for this node.
|
||||||
|
|
||||||
|
|
@ -223,7 +249,7 @@ class NodeBase:
|
||||||
|
|
||||||
return node_data
|
return node_data
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags: int) -> List:
|
||||||
"""
|
"""
|
||||||
Build CORE Link data for this object. There is no default
|
Build CORE Link data for this object. There is no default
|
||||||
method for PyCoreObjs as PyCoreNodes do not implement this but
|
method for PyCoreObjs as PyCoreNodes do not implement this but
|
||||||
|
|
@ -241,7 +267,14 @@ class CoreNodeBase(NodeBase):
|
||||||
Base class for CORE nodes.
|
Base class for CORE nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, start=True, server=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
session: "Session",
|
||||||
|
_id: int = None,
|
||||||
|
name: str = None,
|
||||||
|
start: bool = True,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a CoreNodeBase instance.
|
Create a CoreNodeBase instance.
|
||||||
|
|
||||||
|
|
@ -257,7 +290,7 @@ class CoreNodeBase(NodeBase):
|
||||||
self.nodedir = None
|
self.nodedir = None
|
||||||
self.tmpnodedir = False
|
self.tmpnodedir = False
|
||||||
|
|
||||||
def makenodedir(self):
|
def makenodedir(self) -> None:
|
||||||
"""
|
"""
|
||||||
Create the node directory.
|
Create the node directory.
|
||||||
|
|
||||||
|
|
@ -270,7 +303,7 @@ class CoreNodeBase(NodeBase):
|
||||||
else:
|
else:
|
||||||
self.tmpnodedir = False
|
self.tmpnodedir = False
|
||||||
|
|
||||||
def rmnodedir(self):
|
def rmnodedir(self) -> None:
|
||||||
"""
|
"""
|
||||||
Remove the node directory, unless preserve directory has been set.
|
Remove the node directory, unless preserve directory has been set.
|
||||||
|
|
||||||
|
|
@ -283,7 +316,7 @@ class CoreNodeBase(NodeBase):
|
||||||
if self.tmpnodedir:
|
if self.tmpnodedir:
|
||||||
self.host_cmd(f"rm -rf {self.nodedir}")
|
self.host_cmd(f"rm -rf {self.nodedir}")
|
||||||
|
|
||||||
def addnetif(self, netif, ifindex):
|
def addnetif(self, netif: CoreInterface, ifindex: int) -> None:
|
||||||
"""
|
"""
|
||||||
Add network interface to node and set the network interface index if successful.
|
Add network interface to node and set the network interface index if successful.
|
||||||
|
|
||||||
|
|
@ -296,7 +329,7 @@ class CoreNodeBase(NodeBase):
|
||||||
self._netif[ifindex] = netif
|
self._netif[ifindex] = netif
|
||||||
netif.netindex = ifindex
|
netif.netindex = ifindex
|
||||||
|
|
||||||
def delnetif(self, ifindex):
|
def delnetif(self, ifindex: int) -> None:
|
||||||
"""
|
"""
|
||||||
Delete a network interface
|
Delete a network interface
|
||||||
|
|
||||||
|
|
@ -309,7 +342,7 @@ class CoreNodeBase(NodeBase):
|
||||||
netif.shutdown()
|
netif.shutdown()
|
||||||
del netif
|
del netif
|
||||||
|
|
||||||
def netif(self, ifindex):
|
def netif(self, ifindex: int) -> Optional[CoreInterface]:
|
||||||
"""
|
"""
|
||||||
Retrieve network interface.
|
Retrieve network interface.
|
||||||
|
|
||||||
|
|
@ -322,7 +355,7 @@ class CoreNodeBase(NodeBase):
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def attachnet(self, ifindex, net):
|
def attachnet(self, ifindex: int, net: "CoreNetworkBase") -> None:
|
||||||
"""
|
"""
|
||||||
Attach a network.
|
Attach a network.
|
||||||
|
|
||||||
|
|
@ -334,7 +367,7 @@ class CoreNodeBase(NodeBase):
|
||||||
raise ValueError(f"ifindex {ifindex} does not exist")
|
raise ValueError(f"ifindex {ifindex} does not exist")
|
||||||
self._netif[ifindex].attachnet(net)
|
self._netif[ifindex].attachnet(net)
|
||||||
|
|
||||||
def detachnet(self, ifindex):
|
def detachnet(self, ifindex: int) -> None:
|
||||||
"""
|
"""
|
||||||
Detach network interface.
|
Detach network interface.
|
||||||
|
|
||||||
|
|
@ -345,7 +378,7 @@ class CoreNodeBase(NodeBase):
|
||||||
raise ValueError(f"ifindex {ifindex} does not exist")
|
raise ValueError(f"ifindex {ifindex} does not exist")
|
||||||
self._netif[ifindex].detachnet()
|
self._netif[ifindex].detachnet()
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x: float = None, y: float = None, z: float = None) -> None:
|
||||||
"""
|
"""
|
||||||
Set position.
|
Set position.
|
||||||
|
|
||||||
|
|
@ -359,7 +392,9 @@ class CoreNodeBase(NodeBase):
|
||||||
for netif in self.netifs(sort=True):
|
for netif in self.netifs(sort=True):
|
||||||
netif.setposition(x, y, z)
|
netif.setposition(x, y, z)
|
||||||
|
|
||||||
def commonnets(self, obj, want_ctrl=False):
|
def commonnets(
|
||||||
|
self, obj: "CoreNodeBase", want_ctrl: bool = False
|
||||||
|
) -> List[Tuple[NodeBase, CoreInterface, CoreInterface]]:
|
||||||
"""
|
"""
|
||||||
Given another node or net object, return common networks between
|
Given another node or net object, return common networks between
|
||||||
this node and that object. A list of tuples is returned, with each tuple
|
this node and that object. A list of tuples is returned, with each tuple
|
||||||
|
|
@ -377,10 +412,9 @@ class CoreNodeBase(NodeBase):
|
||||||
for netif2 in obj.netifs():
|
for netif2 in obj.netifs():
|
||||||
if netif1.net == netif2.net:
|
if netif1.net == netif2.net:
|
||||||
common.append((netif1.net, netif1, netif2))
|
common.append((netif1.net, netif1, netif2))
|
||||||
|
|
||||||
return common
|
return common
|
||||||
|
|
||||||
def cmd(self, args, wait=True, shell=False):
|
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
Runs a command within a node container.
|
Runs a command within a node container.
|
||||||
|
|
||||||
|
|
@ -393,7 +427,7 @@ class CoreNodeBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def termcmdstring(self, sh):
|
def termcmdstring(self, sh: str) -> str:
|
||||||
"""
|
"""
|
||||||
Create a terminal command string.
|
Create a terminal command string.
|
||||||
|
|
||||||
|
|
@ -413,14 +447,14 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session,
|
session: "Session",
|
||||||
_id=None,
|
_id: int = None,
|
||||||
name=None,
|
name: str = None,
|
||||||
nodedir=None,
|
nodedir: str = None,
|
||||||
bootsh="boot.sh",
|
bootsh: str = "boot.sh",
|
||||||
start=True,
|
start: bool = True,
|
||||||
server=None,
|
server: "DistributedServer" = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a CoreNode instance.
|
Create a CoreNode instance.
|
||||||
|
|
||||||
|
|
@ -451,7 +485,7 @@ class CoreNode(CoreNodeBase):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def create_node_net_client(self, use_ovs):
|
def create_node_net_client(self, use_ovs: bool) -> LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
Create node network client for running network commands within the nodes
|
Create node network client for running network commands within the nodes
|
||||||
container.
|
container.
|
||||||
|
|
@ -461,7 +495,7 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
return get_net_client(use_ovs, self.cmd)
|
return get_net_client(use_ovs, self.cmd)
|
||||||
|
|
||||||
def alive(self):
|
def alive(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the node is alive.
|
Check if the node is alive.
|
||||||
|
|
||||||
|
|
@ -475,7 +509,7 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Start a new namespace node by invoking the vnoded process that
|
Start a new namespace node by invoking the vnoded process that
|
||||||
allocates a new namespace. Bring up the loopback device and set
|
allocates a new namespace. Bring up the loopback device and set
|
||||||
|
|
@ -521,7 +555,7 @@ class CoreNode(CoreNodeBase):
|
||||||
self.privatedir("/var/run")
|
self.privatedir("/var/run")
|
||||||
self.privatedir("/var/log")
|
self.privatedir("/var/log")
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Shutdown logic for simple lxc nodes.
|
Shutdown logic for simple lxc nodes.
|
||||||
|
|
||||||
|
|
@ -562,7 +596,7 @@ class CoreNode(CoreNodeBase):
|
||||||
finally:
|
finally:
|
||||||
self.rmnodedir()
|
self.rmnodedir()
|
||||||
|
|
||||||
def cmd(self, args, wait=True, shell=False):
|
def 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
|
Runs a command that is used to configure and setup the network within a
|
||||||
node.
|
node.
|
||||||
|
|
@ -580,7 +614,7 @@ class CoreNode(CoreNodeBase):
|
||||||
args = self.client.create_cmd(args)
|
args = self.client.create_cmd(args)
|
||||||
return self.server.remote_cmd(args, wait=wait)
|
return self.server.remote_cmd(args, wait=wait)
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh: str = "/bin/sh") -> str:
|
||||||
"""
|
"""
|
||||||
Create a terminal command string.
|
Create a terminal command string.
|
||||||
|
|
||||||
|
|
@ -593,7 +627,7 @@ class CoreNode(CoreNodeBase):
|
||||||
else:
|
else:
|
||||||
return f"ssh -X -f {self.server.host} xterm -e {terminal}"
|
return f"ssh -X -f {self.server.host} xterm -e {terminal}"
|
||||||
|
|
||||||
def privatedir(self, path):
|
def privatedir(self, path: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a private directory.
|
Create a private directory.
|
||||||
|
|
||||||
|
|
@ -608,7 +642,7 @@ class CoreNode(CoreNodeBase):
|
||||||
self.host_cmd(f"mkdir -p {hostpath}")
|
self.host_cmd(f"mkdir -p {hostpath}")
|
||||||
self.mount(hostpath, path)
|
self.mount(hostpath, path)
|
||||||
|
|
||||||
def mount(self, source, target):
|
def mount(self, source: str, target: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create and mount a directory.
|
Create and mount a directory.
|
||||||
|
|
||||||
|
|
@ -623,7 +657,7 @@ class CoreNode(CoreNodeBase):
|
||||||
self.cmd(f"{MOUNT_BIN} -n --bind {source} {target}")
|
self.cmd(f"{MOUNT_BIN} -n --bind {source} {target}")
|
||||||
self._mounts.append((source, target))
|
self._mounts.append((source, target))
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self) -> int:
|
||||||
"""
|
"""
|
||||||
Retrieve a new interface index.
|
Retrieve a new interface index.
|
||||||
|
|
||||||
|
|
@ -633,7 +667,7 @@ class CoreNode(CoreNodeBase):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
return super().newifindex()
|
return super().newifindex()
|
||||||
|
|
||||||
def newveth(self, ifindex=None, ifname=None):
|
def newveth(self, ifindex: int = None, ifname: str = None) -> int:
|
||||||
"""
|
"""
|
||||||
Create a new interface.
|
Create a new interface.
|
||||||
|
|
||||||
|
|
@ -690,7 +724,7 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def newtuntap(self, ifindex=None, ifname=None):
|
def newtuntap(self, ifindex: int = None, ifname: str = None) -> int:
|
||||||
"""
|
"""
|
||||||
Create a new tunnel tap.
|
Create a new tunnel tap.
|
||||||
|
|
||||||
|
|
@ -720,7 +754,7 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def sethwaddr(self, ifindex, addr):
|
def sethwaddr(self, ifindex: int, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set hardware addres for an interface.
|
Set hardware addres for an interface.
|
||||||
|
|
||||||
|
|
@ -735,7 +769,7 @@ class CoreNode(CoreNodeBase):
|
||||||
if self.up:
|
if self.up:
|
||||||
self.node_net_client.device_mac(interface.name, addr)
|
self.node_net_client.device_mac(interface.name, addr)
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex: int, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Add interface address.
|
Add interface address.
|
||||||
|
|
||||||
|
|
@ -753,7 +787,7 @@ class CoreNode(CoreNodeBase):
|
||||||
broadcast = "+"
|
broadcast = "+"
|
||||||
self.node_net_client.create_address(interface.name, addr, broadcast)
|
self.node_net_client.create_address(interface.name, addr, broadcast)
|
||||||
|
|
||||||
def deladdr(self, ifindex, addr):
|
def deladdr(self, ifindex: int, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete address from an interface.
|
Delete address from an interface.
|
||||||
|
|
||||||
|
|
@ -772,7 +806,7 @@ class CoreNode(CoreNodeBase):
|
||||||
if self.up:
|
if self.up:
|
||||||
self.node_net_client.delete_address(interface.name, addr)
|
self.node_net_client.delete_address(interface.name, addr)
|
||||||
|
|
||||||
def ifup(self, ifindex):
|
def ifup(self, ifindex: int) -> None:
|
||||||
"""
|
"""
|
||||||
Bring an interface up.
|
Bring an interface up.
|
||||||
|
|
||||||
|
|
@ -783,7 +817,14 @@ class CoreNode(CoreNodeBase):
|
||||||
interface_name = self.ifname(ifindex)
|
interface_name = self.ifname(ifindex)
|
||||||
self.node_net_client.device_up(interface_name)
|
self.node_net_client.device_up(interface_name)
|
||||||
|
|
||||||
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
def newnetif(
|
||||||
|
self,
|
||||||
|
net: "CoreNetworkBase" = None,
|
||||||
|
addrlist: List[str] = None,
|
||||||
|
hwaddr: str = None,
|
||||||
|
ifindex: int = None,
|
||||||
|
ifname: str = None,
|
||||||
|
) -> int:
|
||||||
"""
|
"""
|
||||||
Create a new network interface.
|
Create a new network interface.
|
||||||
|
|
||||||
|
|
@ -827,7 +868,7 @@ class CoreNode(CoreNodeBase):
|
||||||
self.ifup(ifindex)
|
self.ifup(ifindex)
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def addfile(self, srcname, filename):
|
def addfile(self, srcname: str, filename: str) -> None:
|
||||||
"""
|
"""
|
||||||
Add a file.
|
Add a file.
|
||||||
|
|
||||||
|
|
@ -846,7 +887,7 @@ class CoreNode(CoreNodeBase):
|
||||||
self.host_cmd(f"mkdir -p {directory}")
|
self.host_cmd(f"mkdir -p {directory}")
|
||||||
self.server.remote_put(srcname, filename)
|
self.server.remote_put(srcname, filename)
|
||||||
|
|
||||||
def hostfilename(self, filename):
|
def hostfilename(self, filename: str) -> str:
|
||||||
"""
|
"""
|
||||||
Return the name of a node"s file on the host filesystem.
|
Return the name of a node"s file on the host filesystem.
|
||||||
|
|
||||||
|
|
@ -862,7 +903,7 @@ class CoreNode(CoreNodeBase):
|
||||||
dirname = os.path.join(self.nodedir, dirname)
|
dirname = os.path.join(self.nodedir, dirname)
|
||||||
return os.path.join(dirname, basename)
|
return os.path.join(dirname, basename)
|
||||||
|
|
||||||
def nodefile(self, filename, contents, mode=0o644):
|
def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None:
|
||||||
"""
|
"""
|
||||||
Create a node file with a given mode.
|
Create a node file with a given mode.
|
||||||
|
|
||||||
|
|
@ -887,7 +928,7 @@ class CoreNode(CoreNodeBase):
|
||||||
"node(%s) added file: %s; mode: 0%o", self.name, hostfilename, mode
|
"node(%s) added file: %s; mode: 0%o", self.name, hostfilename, mode
|
||||||
)
|
)
|
||||||
|
|
||||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
def nodefilecopy(self, filename: str, srcfilename: str, mode: int = None) -> None:
|
||||||
"""
|
"""
|
||||||
Copy a file to a node, following symlinks and preserving metadata.
|
Copy a file to a node, following symlinks and preserving metadata.
|
||||||
Change file mode if specified.
|
Change file mode if specified.
|
||||||
|
|
@ -917,7 +958,14 @@ class CoreNetworkBase(NodeBase):
|
||||||
linktype = LinkTypes.WIRED.value
|
linktype = LinkTypes.WIRED.value
|
||||||
is_emane = False
|
is_emane = False
|
||||||
|
|
||||||
def __init__(self, session, _id, name, start=True, server=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
session: "Session",
|
||||||
|
_id: int,
|
||||||
|
name: str,
|
||||||
|
start: bool = True,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a CoreNetworkBase instance.
|
Create a CoreNetworkBase instance.
|
||||||
|
|
||||||
|
|
@ -932,7 +980,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
self._linked = {}
|
self._linked = {}
|
||||||
self._linked_lock = threading.Lock()
|
self._linked_lock = threading.Lock()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Each object implements its own startup method.
|
Each object implements its own startup method.
|
||||||
|
|
||||||
|
|
@ -940,7 +988,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Each object implements its own shutdown method.
|
Each object implements its own shutdown method.
|
||||||
|
|
||||||
|
|
@ -948,7 +996,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def linknet(self, net):
|
def linknet(self, net: "CoreNetworkBase") -> CoreInterface:
|
||||||
"""
|
"""
|
||||||
Link network to another.
|
Link network to another.
|
||||||
|
|
||||||
|
|
@ -958,7 +1006,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def getlinknetif(self, net):
|
def getlinknetif(self, net: "CoreNetworkBase") -> CoreInterface:
|
||||||
"""
|
"""
|
||||||
Return the interface of that links this net with another net.
|
Return the interface of that links this net with another net.
|
||||||
|
|
||||||
|
|
@ -971,7 +1019,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
return netif
|
return netif
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Attach network interface.
|
Attach network interface.
|
||||||
|
|
||||||
|
|
@ -984,7 +1032,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
with self._linked_lock:
|
with self._linked_lock:
|
||||||
self._linked[netif] = {}
|
self._linked[netif] = {}
|
||||||
|
|
||||||
def detach(self, netif):
|
def detach(self, netif: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Detach network interface.
|
Detach network interface.
|
||||||
|
|
||||||
|
|
@ -996,7 +1044,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
with self._linked_lock:
|
with self._linked_lock:
|
||||||
del self._linked[netif]
|
del self._linked[netif]
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags: int) -> List[LinkData]:
|
||||||
"""
|
"""
|
||||||
Build link data objects for this network. Each link object describes a link
|
Build link data objects for this network. Each link object describes a link
|
||||||
between this network and a node.
|
between this network and a node.
|
||||||
|
|
@ -1004,7 +1052,6 @@ class CoreNetworkBase(NodeBase):
|
||||||
:param int flags: message type
|
:param int flags: message type
|
||||||
:return: list of link data
|
:return: list of link data
|
||||||
:rtype: list[core.data.LinkData]
|
:rtype: list[core.data.LinkData]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
all_links = []
|
all_links = []
|
||||||
|
|
||||||
|
|
@ -1095,7 +1142,7 @@ class Position:
|
||||||
Helper class for Cartesian coordinate position
|
Helper class for Cartesian coordinate position
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, x=None, y=None, z=None):
|
def __init__(self, x: float = None, y: float = None, z: float = None) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a Position instance.
|
Creates a Position instance.
|
||||||
|
|
||||||
|
|
@ -1108,7 +1155,7 @@ class Position:
|
||||||
self.y = y
|
self.y = y
|
||||||
self.z = z
|
self.z = z
|
||||||
|
|
||||||
def set(self, x=None, y=None, z=None):
|
def set(self, x: float = None, y: float = None, z: float = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns True if the position has actually changed.
|
Returns True if the position has actually changed.
|
||||||
|
|
||||||
|
|
@ -1125,7 +1172,7 @@ class Position:
|
||||||
self.z = z
|
self.z = z
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get(self):
|
def get(self) -> Tuple[float, float, float]:
|
||||||
"""
|
"""
|
||||||
Retrieve x,y,z position.
|
Retrieve x,y,z position.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class VnodeClient:
|
||||||
Provides client functionality for interacting with a virtual node.
|
Provides client functionality for interacting with a virtual node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, ctrlchnlname):
|
def __init__(self, name: str, ctrlchnlname: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a VnodeClient instance.
|
Create a VnodeClient instance.
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ class VnodeClient:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.ctrlchnlname = ctrlchnlname
|
self.ctrlchnlname = ctrlchnlname
|
||||||
|
|
||||||
def _verify_connection(self):
|
def _verify_connection(self) -> None:
|
||||||
"""
|
"""
|
||||||
Checks that the vcmd client is properly connected.
|
Checks that the vcmd client is properly connected.
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ class VnodeClient:
|
||||||
if not self.connected():
|
if not self.connected():
|
||||||
raise IOError("vcmd not connected")
|
raise IOError("vcmd not connected")
|
||||||
|
|
||||||
def connected(self):
|
def connected(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if node is connected or not.
|
Check if node is connected or not.
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ class VnodeClient:
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
Close the client connection.
|
Close the client connection.
|
||||||
|
|
||||||
|
|
@ -50,10 +50,10 @@ class VnodeClient:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create_cmd(self, args):
|
def create_cmd(self, args: str) -> str:
|
||||||
return f"{VCMD_BIN} -c {self.ctrlchnlname} -- {args}"
|
return f"{VCMD_BIN} -c {self.ctrlchnlname} -- {args}"
|
||||||
|
|
||||||
def check_cmd(self, args, wait=True, shell=False):
|
def check_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
Run command and return exit status and combined stdout and stderr.
|
Run command and return exit status and combined stdout and stderr.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,27 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
from typing import TYPE_CHECKING, Callable, Dict
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.errors import CoreCommandError
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
from core.nodes.netclient import get_net_client
|
from core.nodes.netclient import LinuxNetClient, get_net_client
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.session import Session
|
||||||
|
|
||||||
|
|
||||||
class DockerClient:
|
class DockerClient:
|
||||||
def __init__(self, name, image, run):
|
def __init__(self, name: str, image: str, run: Callable) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.image = image
|
self.image = image
|
||||||
self.run = run
|
self.run = run
|
||||||
self.pid = None
|
self.pid = None
|
||||||
|
|
||||||
def create_container(self):
|
def create_container(self) -> str:
|
||||||
self.run(
|
self.run(
|
||||||
f"docker run -td --init --net=none --hostname {self.name} --name {self.name} "
|
f"docker run -td --init --net=none --hostname {self.name} --name {self.name} "
|
||||||
f"--sysctl net.ipv6.conf.all.disable_ipv6=0 {self.image} /bin/bash"
|
f"--sysctl net.ipv6.conf.all.disable_ipv6=0 {self.image} /bin/bash"
|
||||||
|
|
@ -25,7 +30,7 @@ class DockerClient:
|
||||||
self.pid = self.get_pid()
|
self.pid = self.get_pid()
|
||||||
return self.pid
|
return self.pid
|
||||||
|
|
||||||
def get_info(self):
|
def get_info(self) -> Dict:
|
||||||
args = f"docker inspect {self.name}"
|
args = f"docker inspect {self.name}"
|
||||||
output = self.run(args)
|
output = self.run(args)
|
||||||
data = json.loads(output)
|
data = json.loads(output)
|
||||||
|
|
@ -33,35 +38,35 @@ class DockerClient:
|
||||||
raise CoreCommandError(-1, args, f"docker({self.name}) not present")
|
raise CoreCommandError(-1, args, f"docker({self.name}) not present")
|
||||||
return data[0]
|
return data[0]
|
||||||
|
|
||||||
def is_alive(self):
|
def is_alive(self) -> bool:
|
||||||
try:
|
try:
|
||||||
data = self.get_info()
|
data = self.get_info()
|
||||||
return data["State"]["Running"]
|
return data["State"]["Running"]
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def stop_container(self):
|
def stop_container(self) -> None:
|
||||||
self.run(f"docker rm -f {self.name}")
|
self.run(f"docker rm -f {self.name}")
|
||||||
|
|
||||||
def check_cmd(self, cmd, wait=True, shell=False):
|
def check_cmd(self, cmd: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
logging.info("docker cmd output: %s", cmd)
|
logging.info("docker cmd output: %s", cmd)
|
||||||
return utils.cmd(f"docker exec {self.name} {cmd}", wait=wait, shell=shell)
|
return utils.cmd(f"docker exec {self.name} {cmd}", wait=wait, shell=shell)
|
||||||
|
|
||||||
def create_ns_cmd(self, cmd):
|
def create_ns_cmd(self, cmd: str) -> str:
|
||||||
return f"nsenter -t {self.pid} -u -i -p -n {cmd}"
|
return f"nsenter -t {self.pid} -u -i -p -n {cmd}"
|
||||||
|
|
||||||
def ns_cmd(self, cmd, wait):
|
def ns_cmd(self, cmd: str, wait: bool) -> str:
|
||||||
args = f"nsenter -t {self.pid} -u -i -p -n {cmd}"
|
args = f"nsenter -t {self.pid} -u -i -p -n {cmd}"
|
||||||
return utils.cmd(args, wait=wait)
|
return utils.cmd(args, wait=wait)
|
||||||
|
|
||||||
def get_pid(self):
|
def get_pid(self) -> str:
|
||||||
args = f"docker inspect -f '{{{{.State.Pid}}}}' {self.name}"
|
args = f"docker inspect -f '{{{{.State.Pid}}}}' {self.name}"
|
||||||
output = self.run(args)
|
output = self.run(args)
|
||||||
self.pid = output
|
self.pid = output
|
||||||
logging.debug("node(%s) pid: %s", self.name, self.pid)
|
logging.debug("node(%s) pid: %s", self.name, self.pid)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def copy_file(self, source, destination):
|
def copy_file(self, source: str, destination: str) -> str:
|
||||||
args = f"docker cp {source} {self.name}:{destination}"
|
args = f"docker cp {source} {self.name}:{destination}"
|
||||||
return self.run(args)
|
return self.run(args)
|
||||||
|
|
||||||
|
|
@ -71,15 +76,15 @@ class DockerNode(CoreNode):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session,
|
session: "Session",
|
||||||
_id=None,
|
_id: int = None,
|
||||||
name=None,
|
name: str = None,
|
||||||
nodedir=None,
|
nodedir: str = None,
|
||||||
bootsh="boot.sh",
|
bootsh: str = "boot.sh",
|
||||||
start=True,
|
start: bool = True,
|
||||||
server=None,
|
server: DistributedServer = None,
|
||||||
image=None
|
image: str = None
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a DockerNode instance.
|
Create a DockerNode instance.
|
||||||
|
|
||||||
|
|
@ -98,7 +103,7 @@ class DockerNode(CoreNode):
|
||||||
self.image = image
|
self.image = image
|
||||||
super().__init__(session, _id, name, nodedir, bootsh, start, server)
|
super().__init__(session, _id, name, nodedir, bootsh, start, server)
|
||||||
|
|
||||||
def create_node_net_client(self, use_ovs):
|
def create_node_net_client(self, use_ovs: bool) -> LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
Create node network client for running network commands within the nodes
|
Create node network client for running network commands within the nodes
|
||||||
container.
|
container.
|
||||||
|
|
@ -108,7 +113,7 @@ class DockerNode(CoreNode):
|
||||||
"""
|
"""
|
||||||
return get_net_client(use_ovs, self.nsenter_cmd)
|
return get_net_client(use_ovs, self.nsenter_cmd)
|
||||||
|
|
||||||
def alive(self):
|
def alive(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the node is alive.
|
Check if the node is alive.
|
||||||
|
|
||||||
|
|
@ -117,7 +122,7 @@ class DockerNode(CoreNode):
|
||||||
"""
|
"""
|
||||||
return self.client.is_alive()
|
return self.client.is_alive()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Start a new namespace node by invoking the vnoded process that
|
Start a new namespace node by invoking the vnoded process that
|
||||||
allocates a new namespace. Bring up the loopback device and set
|
allocates a new namespace. Bring up the loopback device and set
|
||||||
|
|
@ -133,7 +138,7 @@ class DockerNode(CoreNode):
|
||||||
self.pid = self.client.create_container()
|
self.pid = self.client.create_container()
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Shutdown logic.
|
Shutdown logic.
|
||||||
|
|
||||||
|
|
@ -148,7 +153,7 @@ class DockerNode(CoreNode):
|
||||||
self.client.stop_container()
|
self.client.stop_container()
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def nsenter_cmd(self, args, wait=True, shell=False):
|
def nsenter_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
if self.server is None:
|
if self.server is None:
|
||||||
args = self.client.create_ns_cmd(args)
|
args = self.client.create_ns_cmd(args)
|
||||||
return utils.cmd(args, wait=wait, shell=shell)
|
return utils.cmd(args, wait=wait, shell=shell)
|
||||||
|
|
@ -156,7 +161,7 @@ class DockerNode(CoreNode):
|
||||||
args = self.client.create_ns_cmd(args)
|
args = self.client.create_ns_cmd(args)
|
||||||
return self.server.remote_cmd(args, wait=wait)
|
return self.server.remote_cmd(args, wait=wait)
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh: str = "/bin/sh") -> str:
|
||||||
"""
|
"""
|
||||||
Create a terminal command string.
|
Create a terminal command string.
|
||||||
|
|
||||||
|
|
@ -165,7 +170,7 @@ class DockerNode(CoreNode):
|
||||||
"""
|
"""
|
||||||
return f"docker exec -it {self.name} bash"
|
return f"docker exec -it {self.name} bash"
|
||||||
|
|
||||||
def privatedir(self, path):
|
def privatedir(self, path: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a private directory.
|
Create a private directory.
|
||||||
|
|
||||||
|
|
@ -176,7 +181,7 @@ class DockerNode(CoreNode):
|
||||||
args = f"mkdir -p {path}"
|
args = f"mkdir -p {path}"
|
||||||
self.cmd(args)
|
self.cmd(args)
|
||||||
|
|
||||||
def mount(self, source, target):
|
def mount(self, source: str, target: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create and mount a directory.
|
Create and mount a directory.
|
||||||
|
|
||||||
|
|
@ -188,7 +193,7 @@ class DockerNode(CoreNode):
|
||||||
logging.debug("mounting source(%s) target(%s)", source, target)
|
logging.debug("mounting source(%s) target(%s)", source, target)
|
||||||
raise Exception("not supported")
|
raise Exception("not supported")
|
||||||
|
|
||||||
def nodefile(self, filename, contents, mode=0o644):
|
def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None:
|
||||||
"""
|
"""
|
||||||
Create a node file with a given mode.
|
Create a node file with a given mode.
|
||||||
|
|
||||||
|
|
@ -216,7 +221,7 @@ class DockerNode(CoreNode):
|
||||||
"node(%s) added file: %s; mode: 0%o", self.name, filename, mode
|
"node(%s) added file: %s; mode: 0%o", self.name, filename, mode
|
||||||
)
|
)
|
||||||
|
|
||||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
def nodefilecopy(self, filename: str, srcfilename: str, mode: int = None) -> None:
|
||||||
"""
|
"""
|
||||||
Copy a file to a node, following symlinks and preserving metadata.
|
Copy a file to a node, following symlinks and preserving metadata.
|
||||||
Change file mode if specified.
|
Change file mode if specified.
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,31 @@ virtual ethernet classes that implement the interfaces available under Linux.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
from typing import TYPE_CHECKING, Callable, Dict, List, Tuple
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.errors import CoreCommandError
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.netclient import get_net_client
|
from core.nodes.netclient import get_net_client
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
|
from core.emulator.session import Session
|
||||||
|
from core.nodes.base import CoreNetworkBase, CoreNode
|
||||||
|
|
||||||
|
|
||||||
class CoreInterface:
|
class CoreInterface:
|
||||||
"""
|
"""
|
||||||
Base class for network interfaces.
|
Base class for network interfaces.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session, node, name, mtu, server=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
session: "Session",
|
||||||
|
node: "CoreNode",
|
||||||
|
name: str,
|
||||||
|
mtu: int,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a CoreInterface instance.
|
Creates a CoreInterface instance.
|
||||||
|
|
||||||
|
|
@ -50,7 +63,14 @@ class CoreInterface:
|
||||||
use_ovs = session.options.get_config("ovs") == "True"
|
use_ovs = session.options.get_config("ovs") == "True"
|
||||||
self.net_client = get_net_client(use_ovs, self.host_cmd)
|
self.net_client = get_net_client(use_ovs, self.host_cmd)
|
||||||
|
|
||||||
def host_cmd(self, args, env=None, cwd=None, wait=True, shell=False):
|
def host_cmd(
|
||||||
|
self,
|
||||||
|
args: str,
|
||||||
|
env: Dict[str, str] = None,
|
||||||
|
cwd: str = None,
|
||||||
|
wait: bool = True,
|
||||||
|
shell: bool = False,
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Runs a command on the host system or distributed server.
|
Runs a command on the host system or distributed server.
|
||||||
|
|
||||||
|
|
@ -68,7 +88,7 @@ class CoreInterface:
|
||||||
else:
|
else:
|
||||||
return self.server.remote_cmd(args, env, cwd, wait)
|
return self.server.remote_cmd(args, env, cwd, wait)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Startup method for the interface.
|
Startup method for the interface.
|
||||||
|
|
||||||
|
|
@ -76,7 +96,7 @@ class CoreInterface:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Shutdown method for the interface.
|
Shutdown method for the interface.
|
||||||
|
|
||||||
|
|
@ -84,7 +104,7 @@ class CoreInterface:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def attachnet(self, net):
|
def attachnet(self, net: "CoreNetworkBase") -> None:
|
||||||
"""
|
"""
|
||||||
Attach network.
|
Attach network.
|
||||||
|
|
||||||
|
|
@ -98,7 +118,7 @@ class CoreInterface:
|
||||||
net.attach(self)
|
net.attach(self)
|
||||||
self.net = net
|
self.net = net
|
||||||
|
|
||||||
def detachnet(self):
|
def detachnet(self) -> None:
|
||||||
"""
|
"""
|
||||||
Detach from a network.
|
Detach from a network.
|
||||||
|
|
||||||
|
|
@ -107,7 +127,7 @@ class CoreInterface:
|
||||||
if self.net is not None:
|
if self.net is not None:
|
||||||
self.net.detach(self)
|
self.net.detach(self)
|
||||||
|
|
||||||
def addaddr(self, addr):
|
def addaddr(self, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Add address.
|
Add address.
|
||||||
|
|
||||||
|
|
@ -117,7 +137,7 @@ class CoreInterface:
|
||||||
addr = utils.validate_ip(addr)
|
addr = utils.validate_ip(addr)
|
||||||
self.addrlist.append(addr)
|
self.addrlist.append(addr)
|
||||||
|
|
||||||
def deladdr(self, addr):
|
def deladdr(self, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete address.
|
Delete address.
|
||||||
|
|
||||||
|
|
@ -126,7 +146,7 @@ class CoreInterface:
|
||||||
"""
|
"""
|
||||||
self.addrlist.remove(addr)
|
self.addrlist.remove(addr)
|
||||||
|
|
||||||
def sethwaddr(self, addr):
|
def sethwaddr(self, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set hardware address.
|
Set hardware address.
|
||||||
|
|
||||||
|
|
@ -136,7 +156,7 @@ class CoreInterface:
|
||||||
addr = utils.validate_mac(addr)
|
addr = utils.validate_mac(addr)
|
||||||
self.hwaddr = addr
|
self.hwaddr = addr
|
||||||
|
|
||||||
def getparam(self, key):
|
def getparam(self, key: str) -> float:
|
||||||
"""
|
"""
|
||||||
Retrieve a parameter from the, or None if the parameter does not exist.
|
Retrieve a parameter from the, or None if the parameter does not exist.
|
||||||
|
|
||||||
|
|
@ -145,7 +165,7 @@ class CoreInterface:
|
||||||
"""
|
"""
|
||||||
return self._params.get(key)
|
return self._params.get(key)
|
||||||
|
|
||||||
def getparams(self):
|
def getparams(self) -> List[Tuple[str, float]]:
|
||||||
"""
|
"""
|
||||||
Return (key, value) pairs for parameters.
|
Return (key, value) pairs for parameters.
|
||||||
"""
|
"""
|
||||||
|
|
@ -154,7 +174,7 @@ class CoreInterface:
|
||||||
parameters.append((k, self._params[k]))
|
parameters.append((k, self._params[k]))
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
def setparam(self, key, value):
|
def setparam(self, key: str, value: float) -> bool:
|
||||||
"""
|
"""
|
||||||
Set a parameter value, returns True if the parameter has changed.
|
Set a parameter value, returns True if the parameter has changed.
|
||||||
|
|
||||||
|
|
@ -174,7 +194,7 @@ class CoreInterface:
|
||||||
self._params[key] = value
|
self._params[key] = value
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def swapparams(self, name):
|
def swapparams(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Swap out parameters dict for name. If name does not exist,
|
Swap out parameters dict for name. If name does not exist,
|
||||||
intialize it. This is for supporting separate upstream/downstream
|
intialize it. This is for supporting separate upstream/downstream
|
||||||
|
|
@ -189,7 +209,7 @@ class CoreInterface:
|
||||||
self._params = getattr(self, name)
|
self._params = getattr(self, name)
|
||||||
setattr(self, name, tmp)
|
setattr(self, name, tmp)
|
||||||
|
|
||||||
def setposition(self, x, y, z):
|
def setposition(self, x: float, y: float, z: float) -> None:
|
||||||
"""
|
"""
|
||||||
Dispatch position hook handler.
|
Dispatch position hook handler.
|
||||||
|
|
||||||
|
|
@ -200,7 +220,7 @@ class CoreInterface:
|
||||||
"""
|
"""
|
||||||
self.poshook(self, x, y, z)
|
self.poshook(self, x, y, z)
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other: "CoreInterface") -> bool:
|
||||||
"""
|
"""
|
||||||
Used for comparisons of this object.
|
Used for comparisons of this object.
|
||||||
|
|
||||||
|
|
@ -217,8 +237,15 @@ class Veth(CoreInterface):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, session, node, name, localname, mtu=1500, server=None, start=True
|
self,
|
||||||
):
|
session: "Session",
|
||||||
|
node: "CoreNode",
|
||||||
|
name: str,
|
||||||
|
localname: str,
|
||||||
|
mtu: int = 1500,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
start: bool = True,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a VEth instance.
|
Creates a VEth instance.
|
||||||
|
|
||||||
|
|
@ -239,7 +266,7 @@ class Veth(CoreInterface):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Interface startup logic.
|
Interface startup logic.
|
||||||
|
|
||||||
|
|
@ -250,7 +277,7 @@ class Veth(CoreInterface):
|
||||||
self.net_client.device_up(self.localname)
|
self.net_client.device_up(self.localname)
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Interface shutdown logic.
|
Interface shutdown logic.
|
||||||
|
|
||||||
|
|
@ -280,8 +307,15 @@ class TunTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, session, node, name, localname, mtu=1500, server=None, start=True
|
self,
|
||||||
):
|
session: "Session",
|
||||||
|
node: "CoreNode",
|
||||||
|
name: str,
|
||||||
|
localname: str,
|
||||||
|
mtu: int = 1500,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
start: bool = True,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a TunTap instance.
|
Create a TunTap instance.
|
||||||
|
|
||||||
|
|
@ -301,7 +335,7 @@ class TunTap(CoreInterface):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Startup logic for a tunnel tap.
|
Startup logic for a tunnel tap.
|
||||||
|
|
||||||
|
|
@ -315,7 +349,7 @@ class TunTap(CoreInterface):
|
||||||
# self.install()
|
# self.install()
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Shutdown functionality for a tunnel tap.
|
Shutdown functionality for a tunnel tap.
|
||||||
|
|
||||||
|
|
@ -331,7 +365,9 @@ class TunTap(CoreInterface):
|
||||||
|
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
|
def waitfor(
|
||||||
|
self, func: Callable, attempts: int = 10, maxretrydelay: float = 0.25
|
||||||
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Wait for func() to return zero with exponential backoff.
|
Wait for func() to return zero with exponential backoff.
|
||||||
|
|
||||||
|
|
@ -362,7 +398,7 @@ class TunTap(CoreInterface):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def waitfordevicelocal(self):
|
def waitfordevicelocal(self) -> None:
|
||||||
"""
|
"""
|
||||||
Check for presence of a local device - tap device may not
|
Check for presence of a local device - tap device may not
|
||||||
appear right away waits
|
appear right away waits
|
||||||
|
|
@ -381,7 +417,7 @@ class TunTap(CoreInterface):
|
||||||
|
|
||||||
self.waitfor(localdevexists)
|
self.waitfor(localdevexists)
|
||||||
|
|
||||||
def waitfordevicenode(self):
|
def waitfordevicenode(self) -> None:
|
||||||
"""
|
"""
|
||||||
Check for presence of a node device - tap device may not appear right away waits.
|
Check for presence of a node device - tap device may not appear right away waits.
|
||||||
|
|
||||||
|
|
@ -412,7 +448,7 @@ class TunTap(CoreInterface):
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("node device failed to exist")
|
raise RuntimeError("node device failed to exist")
|
||||||
|
|
||||||
def install(self):
|
def install(self) -> None:
|
||||||
"""
|
"""
|
||||||
Install this TAP into its namespace. This is not done from the
|
Install this TAP into its namespace. This is not done from the
|
||||||
startup() method but called at a later time when a userspace
|
startup() method but called at a later time when a userspace
|
||||||
|
|
@ -428,7 +464,7 @@ class TunTap(CoreInterface):
|
||||||
self.node.node_net_client.device_name(self.localname, self.name)
|
self.node.node_net_client.device_name(self.localname, self.name)
|
||||||
self.node.node_net_client.device_up(self.name)
|
self.node.node_net_client.device_up(self.name)
|
||||||
|
|
||||||
def setaddrs(self):
|
def setaddrs(self) -> None:
|
||||||
"""
|
"""
|
||||||
Set interface addresses based on self.addrlist.
|
Set interface addresses based on self.addrlist.
|
||||||
|
|
||||||
|
|
@ -448,18 +484,18 @@ class GreTap(CoreInterface):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
node=None,
|
node: "CoreNode" = None,
|
||||||
name=None,
|
name: str = None,
|
||||||
session=None,
|
session: "Session" = None,
|
||||||
mtu=1458,
|
mtu: int = 1458,
|
||||||
remoteip=None,
|
remoteip: str = None,
|
||||||
_id=None,
|
_id: int = None,
|
||||||
localip=None,
|
localip: str = None,
|
||||||
ttl=255,
|
ttl: int = 255,
|
||||||
key=None,
|
key: int = None,
|
||||||
start=True,
|
start: bool = True,
|
||||||
server=None,
|
server: "DistributedServer" = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a GreTap instance.
|
Creates a GreTap instance.
|
||||||
|
|
||||||
|
|
@ -497,7 +533,7 @@ class GreTap(CoreInterface):
|
||||||
self.net_client.device_up(self.localname)
|
self.net_client.device_up(self.localname)
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Shutdown logic for a GreTap.
|
Shutdown logic for a GreTap.
|
||||||
|
|
||||||
|
|
@ -512,7 +548,7 @@ class GreTap(CoreInterface):
|
||||||
|
|
||||||
self.localname = None
|
self.localname = None
|
||||||
|
|
||||||
def data(self, message_type):
|
def data(self, message_type: int) -> None:
|
||||||
"""
|
"""
|
||||||
Data for a gre tap.
|
Data for a gre tap.
|
||||||
|
|
||||||
|
|
@ -521,7 +557,7 @@ class GreTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags: int) -> List:
|
||||||
"""
|
"""
|
||||||
Retrieve link data.
|
Retrieve link data.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,33 @@ import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
from typing import TYPE_CHECKING, Callable, Dict
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.errors import CoreCommandError
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
|
from core.nodes.interface import CoreInterface
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.session import Session
|
||||||
|
|
||||||
|
|
||||||
class LxdClient:
|
class LxdClient:
|
||||||
def __init__(self, name, image, run):
|
def __init__(self, name: str, image: str, run: Callable) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.image = image
|
self.image = image
|
||||||
self.run = run
|
self.run = run
|
||||||
self.pid = None
|
self.pid = None
|
||||||
|
|
||||||
def create_container(self):
|
def create_container(self) -> int:
|
||||||
self.run(f"lxc launch {self.image} {self.name}")
|
self.run(f"lxc launch {self.image} {self.name}")
|
||||||
data = self.get_info()
|
data = self.get_info()
|
||||||
self.pid = data["state"]["pid"]
|
self.pid = data["state"]["pid"]
|
||||||
return self.pid
|
return self.pid
|
||||||
|
|
||||||
def get_info(self):
|
def get_info(self) -> Dict:
|
||||||
args = f"lxc list {self.name} --format json"
|
args = f"lxc list {self.name} --format json"
|
||||||
output = self.run(args)
|
output = self.run(args)
|
||||||
data = json.loads(output)
|
data = json.loads(output)
|
||||||
|
|
@ -31,27 +37,27 @@ class LxdClient:
|
||||||
raise CoreCommandError(-1, args, f"LXC({self.name}) not present")
|
raise CoreCommandError(-1, args, f"LXC({self.name}) not present")
|
||||||
return data[0]
|
return data[0]
|
||||||
|
|
||||||
def is_alive(self):
|
def is_alive(self) -> bool:
|
||||||
try:
|
try:
|
||||||
data = self.get_info()
|
data = self.get_info()
|
||||||
return data["state"]["status"] == "Running"
|
return data["state"]["status"] == "Running"
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def stop_container(self):
|
def stop_container(self) -> None:
|
||||||
self.run(f"lxc delete --force {self.name}")
|
self.run(f"lxc delete --force {self.name}")
|
||||||
|
|
||||||
def create_cmd(self, cmd):
|
def create_cmd(self, cmd: str) -> str:
|
||||||
return f"lxc exec -nT {self.name} -- {cmd}"
|
return f"lxc exec -nT {self.name} -- {cmd}"
|
||||||
|
|
||||||
def create_ns_cmd(self, cmd):
|
def create_ns_cmd(self, cmd: str) -> str:
|
||||||
return f"nsenter -t {self.pid} -m -u -i -p -n {cmd}"
|
return f"nsenter -t {self.pid} -m -u -i -p -n {cmd}"
|
||||||
|
|
||||||
def check_cmd(self, cmd, wait=True, shell=False):
|
def check_cmd(self, cmd: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
args = self.create_cmd(cmd)
|
args = self.create_cmd(cmd)
|
||||||
return utils.cmd(args, wait=wait, shell=shell)
|
return utils.cmd(args, wait=wait, shell=shell)
|
||||||
|
|
||||||
def copy_file(self, source, destination):
|
def copy_file(self, source: str, destination: str) -> None:
|
||||||
if destination[0] != "/":
|
if destination[0] != "/":
|
||||||
destination = os.path.join("/root/", destination)
|
destination = os.path.join("/root/", destination)
|
||||||
|
|
||||||
|
|
@ -64,15 +70,15 @@ class LxcNode(CoreNode):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session,
|
session: "Session",
|
||||||
_id=None,
|
_id: int = None,
|
||||||
name=None,
|
name: str = None,
|
||||||
nodedir=None,
|
nodedir: str = None,
|
||||||
bootsh="boot.sh",
|
bootsh: str = "boot.sh",
|
||||||
start=True,
|
start: bool = True,
|
||||||
server=None,
|
server: DistributedServer = None,
|
||||||
image=None,
|
image: str = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a LxcNode instance.
|
Create a LxcNode instance.
|
||||||
|
|
||||||
|
|
@ -91,7 +97,7 @@ class LxcNode(CoreNode):
|
||||||
self.image = image
|
self.image = image
|
||||||
super().__init__(session, _id, name, nodedir, bootsh, start, server)
|
super().__init__(session, _id, name, nodedir, bootsh, start, server)
|
||||||
|
|
||||||
def alive(self):
|
def alive(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the node is alive.
|
Check if the node is alive.
|
||||||
|
|
||||||
|
|
@ -100,7 +106,7 @@ class LxcNode(CoreNode):
|
||||||
"""
|
"""
|
||||||
return self.client.is_alive()
|
return self.client.is_alive()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Startup logic.
|
Startup logic.
|
||||||
|
|
||||||
|
|
@ -114,7 +120,7 @@ class LxcNode(CoreNode):
|
||||||
self.pid = self.client.create_container()
|
self.pid = self.client.create_container()
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Shutdown logic.
|
Shutdown logic.
|
||||||
|
|
||||||
|
|
@ -129,7 +135,7 @@ class LxcNode(CoreNode):
|
||||||
self.client.stop_container()
|
self.client.stop_container()
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh: str = "/bin/sh") -> str:
|
||||||
"""
|
"""
|
||||||
Create a terminal command string.
|
Create a terminal command string.
|
||||||
|
|
||||||
|
|
@ -138,7 +144,7 @@ class LxcNode(CoreNode):
|
||||||
"""
|
"""
|
||||||
return f"lxc exec {self.name} -- {sh}"
|
return f"lxc exec {self.name} -- {sh}"
|
||||||
|
|
||||||
def privatedir(self, path):
|
def privatedir(self, path: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a private directory.
|
Create a private directory.
|
||||||
|
|
||||||
|
|
@ -147,9 +153,9 @@ class LxcNode(CoreNode):
|
||||||
"""
|
"""
|
||||||
logging.info("creating node dir: %s", path)
|
logging.info("creating node dir: %s", path)
|
||||||
args = f"mkdir -p {path}"
|
args = f"mkdir -p {path}"
|
||||||
return self.cmd(args)
|
self.cmd(args)
|
||||||
|
|
||||||
def mount(self, source, target):
|
def mount(self, source: str, target: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create and mount a directory.
|
Create and mount a directory.
|
||||||
|
|
||||||
|
|
@ -161,7 +167,7 @@ class LxcNode(CoreNode):
|
||||||
logging.debug("mounting source(%s) target(%s)", source, target)
|
logging.debug("mounting source(%s) target(%s)", source, target)
|
||||||
raise Exception("not supported")
|
raise Exception("not supported")
|
||||||
|
|
||||||
def nodefile(self, filename, contents, mode=0o644):
|
def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None:
|
||||||
"""
|
"""
|
||||||
Create a node file with a given mode.
|
Create a node file with a given mode.
|
||||||
|
|
||||||
|
|
@ -188,7 +194,7 @@ class LxcNode(CoreNode):
|
||||||
os.unlink(temp.name)
|
os.unlink(temp.name)
|
||||||
logging.debug("node(%s) added file: %s; mode: 0%o", self.name, filename, mode)
|
logging.debug("node(%s) added file: %s; mode: 0%o", self.name, filename, mode)
|
||||||
|
|
||||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
def nodefilecopy(self, filename: str, srcfilename: str, mode: int = None) -> None:
|
||||||
"""
|
"""
|
||||||
Copy a file to a node, following symlinks and preserving metadata.
|
Copy a file to a node, following symlinks and preserving metadata.
|
||||||
Change file mode if specified.
|
Change file mode if specified.
|
||||||
|
|
@ -214,7 +220,7 @@ class LxcNode(CoreNode):
|
||||||
self.client.copy_file(source, filename)
|
self.client.copy_file(source, filename)
|
||||||
self.cmd(f"chmod {mode:o} {filename}")
|
self.cmd(f"chmod {mode:o} {filename}")
|
||||||
|
|
||||||
def addnetif(self, netif, ifindex):
|
def addnetif(self, netif: CoreInterface, ifindex: int) -> None:
|
||||||
super().addnetif(netif, ifindex)
|
super().addnetif(netif, ifindex)
|
||||||
# adding small delay to allow time for adding addresses to work correctly
|
# adding small delay to allow time for adding addresses to work correctly
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,17 @@
|
||||||
Clients for dealing with bridge/interface commands.
|
Clients for dealing with bridge/interface commands.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from core.constants import ETHTOOL_BIN, IP_BIN, OVS_BIN, TC_BIN
|
from core.constants import ETHTOOL_BIN, IP_BIN, OVS_BIN, TC_BIN
|
||||||
|
|
||||||
|
|
||||||
def get_net_client(use_ovs, run):
|
|
||||||
"""
|
|
||||||
Retrieve desired net client for running network commands.
|
|
||||||
|
|
||||||
:param bool use_ovs: True for OVS bridges, False for Linux bridges
|
|
||||||
:param func run: function used to run net client commands
|
|
||||||
:return: net client class
|
|
||||||
"""
|
|
||||||
if use_ovs:
|
|
||||||
return OvsNetClient(run)
|
|
||||||
else:
|
|
||||||
return LinuxNetClient(run)
|
|
||||||
|
|
||||||
|
|
||||||
class LinuxNetClient:
|
class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
Client for creating Linux bridges and ip interfaces for nodes.
|
Client for creating Linux bridges and ip interfaces for nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, run):
|
def __init__(self, run: Callable) -> None:
|
||||||
"""
|
"""
|
||||||
Create LinuxNetClient instance.
|
Create LinuxNetClient instance.
|
||||||
|
|
||||||
|
|
@ -33,7 +20,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run = run
|
self.run = run
|
||||||
|
|
||||||
def set_hostname(self, name):
|
def set_hostname(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set network hostname.
|
Set network hostname.
|
||||||
|
|
||||||
|
|
@ -42,7 +29,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"hostname {name}")
|
self.run(f"hostname {name}")
|
||||||
|
|
||||||
def create_route(self, route, device):
|
def create_route(self, route: str, device: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a new route for a device.
|
Create a new route for a device.
|
||||||
|
|
||||||
|
|
@ -52,7 +39,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} route add {route} dev {device}")
|
self.run(f"{IP_BIN} route add {route} dev {device}")
|
||||||
|
|
||||||
def device_up(self, device):
|
def device_up(self, device: str) -> None:
|
||||||
"""
|
"""
|
||||||
Bring a device up.
|
Bring a device up.
|
||||||
|
|
||||||
|
|
@ -61,7 +48,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link set {device} up")
|
self.run(f"{IP_BIN} link set {device} up")
|
||||||
|
|
||||||
def device_down(self, device):
|
def device_down(self, device: str) -> None:
|
||||||
"""
|
"""
|
||||||
Bring a device down.
|
Bring a device down.
|
||||||
|
|
||||||
|
|
@ -70,7 +57,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link set {device} down")
|
self.run(f"{IP_BIN} link set {device} down")
|
||||||
|
|
||||||
def device_name(self, device, name):
|
def device_name(self, device: str, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set a device name.
|
Set a device name.
|
||||||
|
|
||||||
|
|
@ -80,7 +67,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link set {device} name {name}")
|
self.run(f"{IP_BIN} link set {device} name {name}")
|
||||||
|
|
||||||
def device_show(self, device):
|
def device_show(self, device: str) -> str:
|
||||||
"""
|
"""
|
||||||
Show information for a device.
|
Show information for a device.
|
||||||
|
|
||||||
|
|
@ -90,7 +77,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
return self.run(f"{IP_BIN} link show {device}")
|
return self.run(f"{IP_BIN} link show {device}")
|
||||||
|
|
||||||
def get_mac(self, device):
|
def get_mac(self, device: str) -> str:
|
||||||
"""
|
"""
|
||||||
Retrieve MAC address for a given device.
|
Retrieve MAC address for a given device.
|
||||||
|
|
||||||
|
|
@ -100,7 +87,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
return self.run(f"cat /sys/class/net/{device}/address")
|
return self.run(f"cat /sys/class/net/{device}/address")
|
||||||
|
|
||||||
def get_ifindex(self, device):
|
def get_ifindex(self, device: str) -> str:
|
||||||
"""
|
"""
|
||||||
Retrieve ifindex for a given device.
|
Retrieve ifindex for a given device.
|
||||||
|
|
||||||
|
|
@ -110,7 +97,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
return self.run(f"cat /sys/class/net/{device}/ifindex")
|
return self.run(f"cat /sys/class/net/{device}/ifindex")
|
||||||
|
|
||||||
def device_ns(self, device, namespace):
|
def device_ns(self, device: str, namespace: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set netns for a device.
|
Set netns for a device.
|
||||||
|
|
||||||
|
|
@ -120,7 +107,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link set {device} netns {namespace}")
|
self.run(f"{IP_BIN} link set {device} netns {namespace}")
|
||||||
|
|
||||||
def device_flush(self, device):
|
def device_flush(self, device: str) -> None:
|
||||||
"""
|
"""
|
||||||
Flush device addresses.
|
Flush device addresses.
|
||||||
|
|
||||||
|
|
@ -132,7 +119,7 @@ class LinuxNetClient:
|
||||||
shell=True,
|
shell=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def device_mac(self, device, mac):
|
def device_mac(self, device: str, mac: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set MAC address for a device.
|
Set MAC address for a device.
|
||||||
|
|
||||||
|
|
@ -142,7 +129,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link set dev {device} address {mac}")
|
self.run(f"{IP_BIN} link set dev {device} address {mac}")
|
||||||
|
|
||||||
def delete_device(self, device):
|
def delete_device(self, device: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete device.
|
Delete device.
|
||||||
|
|
||||||
|
|
@ -151,7 +138,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link delete {device}")
|
self.run(f"{IP_BIN} link delete {device}")
|
||||||
|
|
||||||
def delete_tc(self, device):
|
def delete_tc(self, device: str) -> None:
|
||||||
"""
|
"""
|
||||||
Remove traffic control settings for a device.
|
Remove traffic control settings for a device.
|
||||||
|
|
||||||
|
|
@ -160,7 +147,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{TC_BIN} qdisc delete dev {device} root")
|
self.run(f"{TC_BIN} qdisc delete dev {device} root")
|
||||||
|
|
||||||
def checksums_off(self, interface_name):
|
def checksums_off(self, interface_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Turns interface checksums off.
|
Turns interface checksums off.
|
||||||
|
|
||||||
|
|
@ -169,7 +156,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{ETHTOOL_BIN} -K {interface_name} rx off tx off")
|
self.run(f"{ETHTOOL_BIN} -K {interface_name} rx off tx off")
|
||||||
|
|
||||||
def create_address(self, device, address, broadcast=None):
|
def create_address(self, device: str, address: str, broadcast: str = None) -> None:
|
||||||
"""
|
"""
|
||||||
Create address for a device.
|
Create address for a device.
|
||||||
|
|
||||||
|
|
@ -185,7 +172,7 @@ class LinuxNetClient:
|
||||||
else:
|
else:
|
||||||
self.run(f"{IP_BIN} address add {address} dev {device}")
|
self.run(f"{IP_BIN} address add {address} dev {device}")
|
||||||
|
|
||||||
def delete_address(self, device, address):
|
def delete_address(self, device: str, address: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete an address from a device.
|
Delete an address from a device.
|
||||||
|
|
||||||
|
|
@ -195,7 +182,7 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} address delete {address} dev {device}")
|
self.run(f"{IP_BIN} address delete {address} dev {device}")
|
||||||
|
|
||||||
def create_veth(self, name, peer):
|
def create_veth(self, name: str, peer: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a veth pair.
|
Create a veth pair.
|
||||||
|
|
||||||
|
|
@ -205,7 +192,9 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link add name {name} type veth peer name {peer}")
|
self.run(f"{IP_BIN} link add name {name} type veth peer name {peer}")
|
||||||
|
|
||||||
def create_gretap(self, device, address, local, ttl, key):
|
def create_gretap(
|
||||||
|
self, device: str, address: str, local: str, ttl: int, key: int
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a GRE tap on a device.
|
Create a GRE tap on a device.
|
||||||
|
|
||||||
|
|
@ -225,7 +214,7 @@ class LinuxNetClient:
|
||||||
cmd += f" key {key}"
|
cmd += f" key {key}"
|
||||||
self.run(cmd)
|
self.run(cmd)
|
||||||
|
|
||||||
def create_bridge(self, name):
|
def create_bridge(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a Linux bridge and bring it up.
|
Create a Linux bridge and bring it up.
|
||||||
|
|
||||||
|
|
@ -238,7 +227,7 @@ class LinuxNetClient:
|
||||||
self.run(f"{IP_BIN} link set {name} type bridge mcast_snooping 0")
|
self.run(f"{IP_BIN} link set {name} type bridge mcast_snooping 0")
|
||||||
self.device_up(name)
|
self.device_up(name)
|
||||||
|
|
||||||
def delete_bridge(self, name):
|
def delete_bridge(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Bring down and delete a Linux bridge.
|
Bring down and delete a Linux bridge.
|
||||||
|
|
||||||
|
|
@ -248,7 +237,7 @@ class LinuxNetClient:
|
||||||
self.device_down(name)
|
self.device_down(name)
|
||||||
self.run(f"{IP_BIN} link delete {name} type bridge")
|
self.run(f"{IP_BIN} link delete {name} type bridge")
|
||||||
|
|
||||||
def create_interface(self, bridge_name, interface_name):
|
def create_interface(self, bridge_name: str, interface_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create an interface associated with a Linux bridge.
|
Create an interface associated with a Linux bridge.
|
||||||
|
|
||||||
|
|
@ -259,7 +248,7 @@ class LinuxNetClient:
|
||||||
self.run(f"{IP_BIN} link set dev {interface_name} master {bridge_name}")
|
self.run(f"{IP_BIN} link set dev {interface_name} master {bridge_name}")
|
||||||
self.device_up(interface_name)
|
self.device_up(interface_name)
|
||||||
|
|
||||||
def delete_interface(self, bridge_name, interface_name):
|
def delete_interface(self, bridge_name: str, interface_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete an interface associated with a Linux bridge.
|
Delete an interface associated with a Linux bridge.
|
||||||
|
|
||||||
|
|
@ -269,11 +258,12 @@ class LinuxNetClient:
|
||||||
"""
|
"""
|
||||||
self.run(f"{IP_BIN} link set dev {interface_name} nomaster")
|
self.run(f"{IP_BIN} link set dev {interface_name} nomaster")
|
||||||
|
|
||||||
def existing_bridges(self, _id):
|
def existing_bridges(self, _id: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if there are any existing Linux bridges for a node.
|
Checks if there are any existing Linux bridges for a node.
|
||||||
|
|
||||||
:param _id: node id to check bridges for
|
:param _id: node id to check bridges for
|
||||||
|
:return: True if there are existing bridges, False otherwise
|
||||||
"""
|
"""
|
||||||
output = self.run(f"{IP_BIN} -j link show type bridge")
|
output = self.run(f"{IP_BIN} -j link show type bridge")
|
||||||
bridges = json.loads(output)
|
bridges = json.loads(output)
|
||||||
|
|
@ -286,7 +276,7 @@ class LinuxNetClient:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def disable_mac_learning(self, name):
|
def disable_mac_learning(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Disable mac learning for a Linux bridge.
|
Disable mac learning for a Linux bridge.
|
||||||
|
|
||||||
|
|
@ -301,7 +291,7 @@ class OvsNetClient(LinuxNetClient):
|
||||||
Client for creating OVS bridges and ip interfaces for nodes.
|
Client for creating OVS bridges and ip interfaces for nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def create_bridge(self, name):
|
def create_bridge(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create a OVS bridge and bring it up.
|
Create a OVS bridge and bring it up.
|
||||||
|
|
||||||
|
|
@ -314,7 +304,7 @@ class OvsNetClient(LinuxNetClient):
|
||||||
self.run(f"{OVS_BIN} set bridge {name} other_config:stp-forward-delay=4")
|
self.run(f"{OVS_BIN} set bridge {name} other_config:stp-forward-delay=4")
|
||||||
self.device_up(name)
|
self.device_up(name)
|
||||||
|
|
||||||
def delete_bridge(self, name):
|
def delete_bridge(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Bring down and delete a OVS bridge.
|
Bring down and delete a OVS bridge.
|
||||||
|
|
||||||
|
|
@ -324,7 +314,7 @@ class OvsNetClient(LinuxNetClient):
|
||||||
self.device_down(name)
|
self.device_down(name)
|
||||||
self.run(f"{OVS_BIN} del-br {name}")
|
self.run(f"{OVS_BIN} del-br {name}")
|
||||||
|
|
||||||
def create_interface(self, bridge_name, interface_name):
|
def create_interface(self, bridge_name: str, interface_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Create an interface associated with a network bridge.
|
Create an interface associated with a network bridge.
|
||||||
|
|
||||||
|
|
@ -335,7 +325,7 @@ class OvsNetClient(LinuxNetClient):
|
||||||
self.run(f"{OVS_BIN} add-port {bridge_name} {interface_name}")
|
self.run(f"{OVS_BIN} add-port {bridge_name} {interface_name}")
|
||||||
self.device_up(interface_name)
|
self.device_up(interface_name)
|
||||||
|
|
||||||
def delete_interface(self, bridge_name, interface_name):
|
def delete_interface(self, bridge_name: str, interface_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete an interface associated with a OVS bridge.
|
Delete an interface associated with a OVS bridge.
|
||||||
|
|
||||||
|
|
@ -345,11 +335,12 @@ class OvsNetClient(LinuxNetClient):
|
||||||
"""
|
"""
|
||||||
self.run(f"{OVS_BIN} del-port {bridge_name} {interface_name}")
|
self.run(f"{OVS_BIN} del-port {bridge_name} {interface_name}")
|
||||||
|
|
||||||
def existing_bridges(self, _id):
|
def existing_bridges(self, _id: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if there are any existing OVS bridges for a node.
|
Checks if there are any existing OVS bridges for a node.
|
||||||
|
|
||||||
:param _id: node id to check bridges for
|
:param _id: node id to check bridges for
|
||||||
|
:return: True if there are existing bridges, False otherwise
|
||||||
"""
|
"""
|
||||||
output = self.run(f"{OVS_BIN} list-br")
|
output = self.run(f"{OVS_BIN} list-br")
|
||||||
if output:
|
if output:
|
||||||
|
|
@ -359,7 +350,7 @@ class OvsNetClient(LinuxNetClient):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def disable_mac_learning(self, name):
|
def disable_mac_learning(self, name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Disable mac learning for a OVS bridge.
|
Disable mac learning for a OVS bridge.
|
||||||
|
|
||||||
|
|
@ -367,3 +358,17 @@ class OvsNetClient(LinuxNetClient):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
self.run(f"{OVS_BIN} set bridge {name} other_config:mac-aging-time=0")
|
self.run(f"{OVS_BIN} set bridge {name} other_config:mac-aging-time=0")
|
||||||
|
|
||||||
|
|
||||||
|
def get_net_client(use_ovs: bool, run: Callable) -> LinuxNetClient:
|
||||||
|
"""
|
||||||
|
Retrieve desired net client for running network commands.
|
||||||
|
|
||||||
|
:param bool use_ovs: True for OVS bridges, False for Linux bridges
|
||||||
|
:param func run: function used to run net client commands
|
||||||
|
:return: net client class
|
||||||
|
"""
|
||||||
|
if use_ovs:
|
||||||
|
return OvsNetClient(run)
|
||||||
|
else:
|
||||||
|
return LinuxNetClient(run)
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,26 @@ Defines network nodes used within core.
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.constants import EBTABLES_BIN, TC_BIN
|
from core.constants import EBTABLES_BIN, TC_BIN
|
||||||
from core.emulator.data import LinkData
|
from core.emulator.data import LinkData, NodeData
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||||
from core.errors import CoreCommandError, CoreError
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.nodes.base import CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.interface import GreTap, Veth
|
from core.nodes.interface import CoreInterface, GreTap, Veth
|
||||||
from core.nodes.netclient import get_net_client
|
from core.nodes.netclient import get_net_client
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
|
from core.emulator.session import Session
|
||||||
|
from core.location.mobility import WirelessModel
|
||||||
|
|
||||||
|
WirelessModelType = Type[WirelessModel]
|
||||||
|
|
||||||
ebtables_lock = threading.Lock()
|
ebtables_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,7 +40,7 @@ class EbtablesQueue:
|
||||||
# ebtables
|
# ebtables
|
||||||
atomic_file = "/tmp/pycore.ebtables.atomic"
|
atomic_file = "/tmp/pycore.ebtables.atomic"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize the helper class, but don't start the update thread
|
Initialize the helper class, but don't start the update thread
|
||||||
until a WLAN is instantiated.
|
until a WLAN is instantiated.
|
||||||
|
|
@ -49,7 +57,7 @@ class EbtablesQueue:
|
||||||
# using this queue
|
# using this queue
|
||||||
self.last_update_time = {}
|
self.last_update_time = {}
|
||||||
|
|
||||||
def startupdateloop(self, wlan):
|
def startupdateloop(self, wlan: "CoreNetwork") -> None:
|
||||||
"""
|
"""
|
||||||
Kick off the update loop; only needs to be invoked once.
|
Kick off the update loop; only needs to be invoked once.
|
||||||
|
|
||||||
|
|
@ -66,7 +74,7 @@ class EbtablesQueue:
|
||||||
self.updatethread.daemon = True
|
self.updatethread.daemon = True
|
||||||
self.updatethread.start()
|
self.updatethread.start()
|
||||||
|
|
||||||
def stopupdateloop(self, wlan):
|
def stopupdateloop(self, wlan: "CoreNetwork") -> None:
|
||||||
"""
|
"""
|
||||||
Kill the update loop thread if there are no more WLANs using it.
|
Kill the update loop thread if there are no more WLANs using it.
|
||||||
|
|
||||||
|
|
@ -88,17 +96,17 @@ class EbtablesQueue:
|
||||||
self.updatethread.join()
|
self.updatethread.join()
|
||||||
self.updatethread = None
|
self.updatethread = None
|
||||||
|
|
||||||
def ebatomiccmd(self, cmd):
|
def ebatomiccmd(self, cmd: str) -> str:
|
||||||
"""
|
"""
|
||||||
Helper for building ebtables atomic file command list.
|
Helper for building ebtables atomic file command list.
|
||||||
|
|
||||||
:param str cmd: ebtable command
|
:param str cmd: ebtable command
|
||||||
:return: ebtable atomic command
|
:return: ebtable atomic command
|
||||||
:rtype: list[str]
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return f"{EBTABLES_BIN} --atomic-file {self.atomic_file} {cmd}"
|
return f"{EBTABLES_BIN} --atomic-file {self.atomic_file} {cmd}"
|
||||||
|
|
||||||
def lastupdate(self, wlan):
|
def lastupdate(self, wlan: "CoreNetwork") -> float:
|
||||||
"""
|
"""
|
||||||
Return the time elapsed since this WLAN was last updated.
|
Return the time elapsed since this WLAN was last updated.
|
||||||
|
|
||||||
|
|
@ -114,7 +122,7 @@ class EbtablesQueue:
|
||||||
|
|
||||||
return elapsed
|
return elapsed
|
||||||
|
|
||||||
def updated(self, wlan):
|
def updated(self, wlan: "CoreNetwork") -> None:
|
||||||
"""
|
"""
|
||||||
Keep track of when this WLAN was last updated.
|
Keep track of when this WLAN was last updated.
|
||||||
|
|
||||||
|
|
@ -124,7 +132,7 @@ class EbtablesQueue:
|
||||||
self.last_update_time[wlan] = time.monotonic()
|
self.last_update_time[wlan] = time.monotonic()
|
||||||
self.updates.remove(wlan)
|
self.updates.remove(wlan)
|
||||||
|
|
||||||
def updateloop(self):
|
def updateloop(self) -> None:
|
||||||
"""
|
"""
|
||||||
Thread target that looks for WLANs needing update, and
|
Thread target that looks for WLANs needing update, and
|
||||||
rate limits the amount of ebtables activity. Only one userspace program
|
rate limits the amount of ebtables activity. Only one userspace program
|
||||||
|
|
@ -153,7 +161,7 @@ class EbtablesQueue:
|
||||||
|
|
||||||
time.sleep(self.rate)
|
time.sleep(self.rate)
|
||||||
|
|
||||||
def ebcommit(self, wlan):
|
def ebcommit(self, wlan: "CoreNetwork") -> None:
|
||||||
"""
|
"""
|
||||||
Perform ebtables atomic commit using commands built in the self.cmds list.
|
Perform ebtables atomic commit using commands built in the self.cmds list.
|
||||||
|
|
||||||
|
|
@ -178,7 +186,7 @@ class EbtablesQueue:
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error removing atomic file: %s", self.atomic_file)
|
logging.exception("error removing atomic file: %s", self.atomic_file)
|
||||||
|
|
||||||
def ebchange(self, wlan):
|
def ebchange(self, wlan: "CoreNetwork") -> None:
|
||||||
"""
|
"""
|
||||||
Flag a change to the given WLAN's _linked dict, so the ebtables
|
Flag a change to the given WLAN's _linked dict, so the ebtables
|
||||||
chain will be rebuilt at the next interval.
|
chain will be rebuilt at the next interval.
|
||||||
|
|
@ -189,7 +197,7 @@ class EbtablesQueue:
|
||||||
if wlan not in self.updates:
|
if wlan not in self.updates:
|
||||||
self.updates.append(wlan)
|
self.updates.append(wlan)
|
||||||
|
|
||||||
def buildcmds(self, wlan):
|
def buildcmds(self, wlan: "CoreNetwork") -> None:
|
||||||
"""
|
"""
|
||||||
Inspect a _linked dict from a wlan, and rebuild the ebtables chain for that WLAN.
|
Inspect a _linked dict from a wlan, and rebuild the ebtables chain for that WLAN.
|
||||||
|
|
||||||
|
|
@ -231,7 +239,7 @@ class EbtablesQueue:
|
||||||
ebq = EbtablesQueue()
|
ebq = EbtablesQueue()
|
||||||
|
|
||||||
|
|
||||||
def ebtablescmds(call, cmds):
|
def ebtablescmds(call: Callable, cmds: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Run ebtable commands.
|
Run ebtable commands.
|
||||||
|
|
||||||
|
|
@ -252,8 +260,14 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, session, _id=None, name=None, start=True, server=None, policy=None
|
self,
|
||||||
):
|
session: "Session",
|
||||||
|
_id: int = None,
|
||||||
|
name: str = None,
|
||||||
|
start: bool = True,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
policy: str = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a LxBrNet instance.
|
Creates a LxBrNet instance.
|
||||||
|
|
||||||
|
|
@ -279,7 +293,14 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
self.startup()
|
self.startup()
|
||||||
ebq.startupdateloop(self)
|
ebq.startupdateloop(self)
|
||||||
|
|
||||||
def host_cmd(self, args, env=None, cwd=None, wait=True, shell=False):
|
def host_cmd(
|
||||||
|
self,
|
||||||
|
args: str,
|
||||||
|
env: Dict[str, str] = None,
|
||||||
|
cwd: str = None,
|
||||||
|
wait: bool = True,
|
||||||
|
shell: bool = False,
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Runs a command that is used to configure and setup the network on the host
|
Runs a command that is used to configure and setup the network on the host
|
||||||
system and all configured distributed servers.
|
system and all configured distributed servers.
|
||||||
|
|
@ -298,7 +319,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
self.session.distributed.execute(lambda x: x.remote_cmd(args, env, cwd, wait))
|
self.session.distributed.execute(lambda x: x.remote_cmd(args, env, cwd, wait))
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Linux bridge starup logic.
|
Linux bridge starup logic.
|
||||||
|
|
||||||
|
|
@ -309,7 +330,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
self.has_ebtables_chain = False
|
self.has_ebtables_chain = False
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Linux bridge shutdown logic.
|
Linux bridge shutdown logic.
|
||||||
|
|
||||||
|
|
@ -340,18 +361,18 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
del self.session
|
del self.session
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Attach a network interface.
|
Attach a network interface.
|
||||||
|
|
||||||
:param core.nodes.interface.Veth netif: network interface to attach
|
:param core.nodes.interface.CoreInterface netif: network interface to attach
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
netif.net_client.create_interface(self.brname, netif.localname)
|
netif.net_client.create_interface(self.brname, netif.localname)
|
||||||
super().attach(netif)
|
super().attach(netif)
|
||||||
|
|
||||||
def detach(self, netif):
|
def detach(self, netif: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Detach a network interface.
|
Detach a network interface.
|
||||||
|
|
||||||
|
|
@ -362,7 +383,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
netif.net_client.delete_interface(self.brname, netif.localname)
|
netif.net_client.delete_interface(self.brname, netif.localname)
|
||||||
super().detach(netif)
|
super().detach(netif)
|
||||||
|
|
||||||
def linked(self, netif1, netif2):
|
def linked(self, netif1: CoreInterface, netif2: CoreInterface) -> bool:
|
||||||
"""
|
"""
|
||||||
Determine if the provided network interfaces are linked.
|
Determine if the provided network interfaces are linked.
|
||||||
|
|
||||||
|
|
@ -391,9 +412,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
|
|
||||||
return linked
|
return linked
|
||||||
|
|
||||||
def unlink(self, netif1, netif2):
|
def unlink(self, netif1: CoreInterface, netif2: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
|
Unlink two interfaces, resulting in adding or removing ebtables
|
||||||
filtering rules.
|
filtering rules.
|
||||||
|
|
||||||
:param core.nodes.interface.CoreInterface netif1: interface one
|
:param core.nodes.interface.CoreInterface netif1: interface one
|
||||||
|
|
@ -407,9 +428,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
|
|
||||||
ebq.ebchange(self)
|
ebq.ebchange(self)
|
||||||
|
|
||||||
def link(self, netif1, netif2):
|
def link(self, netif1: CoreInterface, netif2: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Link two PyCoreNetIfs together, resulting in adding or removing
|
Link two interfaces together, resulting in adding or removing
|
||||||
ebtables filtering rules.
|
ebtables filtering rules.
|
||||||
|
|
||||||
:param core.nodes.interface.CoreInterface netif1: interface one
|
:param core.nodes.interface.CoreInterface netif1: interface one
|
||||||
|
|
@ -425,19 +446,19 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
|
|
||||||
def linkconfig(
|
def linkconfig(
|
||||||
self,
|
self,
|
||||||
netif,
|
netif: CoreInterface,
|
||||||
bw=None,
|
bw: float = None,
|
||||||
delay=None,
|
delay: float = None,
|
||||||
loss=None,
|
loss: float = None,
|
||||||
duplicate=None,
|
duplicate: float = None,
|
||||||
jitter=None,
|
jitter: float = None,
|
||||||
netif2=None,
|
netif2: float = None,
|
||||||
devname=None,
|
devname: str = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Configure link parameters by applying tc queuing disciplines on the interface.
|
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||||
|
|
||||||
:param core.nodes.interface.Veth netif: interface one
|
:param core.nodes.interface.CoreInterface netif: interface one
|
||||||
:param bw: bandwidth to set to
|
:param bw: bandwidth to set to
|
||||||
:param delay: packet delay to set to
|
:param delay: packet delay to set to
|
||||||
:param loss: packet loss to set to
|
:param loss: packet loss to set to
|
||||||
|
|
@ -520,14 +541,14 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
netif.host_cmd(cmd)
|
netif.host_cmd(cmd)
|
||||||
netif.setparam("has_netem", True)
|
netif.setparam("has_netem", True)
|
||||||
|
|
||||||
def linknet(self, net):
|
def linknet(self, net: CoreNetworkBase) -> CoreInterface:
|
||||||
"""
|
"""
|
||||||
Link this bridge with another by creating a veth pair and installing
|
Link this bridge with another by creating a veth pair and installing
|
||||||
each device into each bridge.
|
each device into each bridge.
|
||||||
|
|
||||||
:param core.nodes.base.CoreNetworkBase net: network to link with
|
:param core.nodes.base.CoreNetworkBase net: network to link with
|
||||||
:return: created interface
|
:return: created interface
|
||||||
:rtype: core.nodes.interface.Veth
|
:rtype: core.nodes.interface.CoreInterface
|
||||||
"""
|
"""
|
||||||
sessionid = self.session.short_session_id()
|
sessionid = self.session.short_session_id()
|
||||||
try:
|
try:
|
||||||
|
|
@ -561,7 +582,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
netif.othernet = net
|
netif.othernet = net
|
||||||
return netif
|
return netif
|
||||||
|
|
||||||
def getlinknetif(self, net):
|
def getlinknetif(self, net: CoreNetworkBase) -> Optional[CoreInterface]:
|
||||||
"""
|
"""
|
||||||
Return the interface of that links this net with another net
|
Return the interface of that links this net with another net
|
||||||
(that were linked using linknet()).
|
(that were linked using linknet()).
|
||||||
|
|
@ -573,10 +594,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
if hasattr(netif, "othernet") and netif.othernet == net:
|
if hasattr(netif, "othernet") and netif.othernet == net:
|
||||||
return netif
|
return netif
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def addrconfig(self, addrlist):
|
def addrconfig(self, addrlist: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Set addresses on the bridge.
|
Set addresses on the bridge.
|
||||||
|
|
||||||
|
|
@ -598,17 +618,17 @@ class GreTapBridge(CoreNetwork):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session,
|
session: "Session",
|
||||||
remoteip=None,
|
remoteip: str = None,
|
||||||
_id=None,
|
_id: int = None,
|
||||||
name=None,
|
name: str = None,
|
||||||
policy="ACCEPT",
|
policy: str = "ACCEPT",
|
||||||
localip=None,
|
localip: str = None,
|
||||||
ttl=255,
|
ttl: int = 255,
|
||||||
key=None,
|
key: int = None,
|
||||||
start=True,
|
start: bool = True,
|
||||||
server=None,
|
server: "DistributedServer" = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a GreTapBridge instance.
|
Create a GreTapBridge instance.
|
||||||
|
|
||||||
|
|
@ -647,7 +667,7 @@ class GreTapBridge(CoreNetwork):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a bridge and adds the gretap device to it.
|
Creates a bridge and adds the gretap device to it.
|
||||||
|
|
||||||
|
|
@ -657,7 +677,7 @@ class GreTapBridge(CoreNetwork):
|
||||||
if self.gretap:
|
if self.gretap:
|
||||||
self.attach(self.gretap)
|
self.attach(self.gretap)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Detach the gretap device and remove the bridge.
|
Detach the gretap device and remove the bridge.
|
||||||
|
|
||||||
|
|
@ -669,7 +689,7 @@ class GreTapBridge(CoreNetwork):
|
||||||
self.gretap = None
|
self.gretap = None
|
||||||
super().shutdown()
|
super().shutdown()
|
||||||
|
|
||||||
def addrconfig(self, addrlist):
|
def addrconfig(self, addrlist: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Set the remote tunnel endpoint. This is a one-time method for
|
Set the remote tunnel endpoint. This is a one-time method for
|
||||||
creating the GreTap device, which requires the remoteip at startup.
|
creating the GreTap device, which requires the remoteip at startup.
|
||||||
|
|
@ -694,7 +714,7 @@ class GreTapBridge(CoreNetwork):
|
||||||
)
|
)
|
||||||
self.attach(self.gretap)
|
self.attach(self.gretap)
|
||||||
|
|
||||||
def setkey(self, key):
|
def setkey(self, key: int) -> None:
|
||||||
"""
|
"""
|
||||||
Set the GRE key used for the GreTap device. This needs to be set
|
Set the GRE key used for the GreTap device. This needs to be set
|
||||||
prior to instantiating the GreTap device (before addrconfig).
|
prior to instantiating the GreTap device (before addrconfig).
|
||||||
|
|
@ -722,17 +742,17 @@ class CtrlNet(CoreNetwork):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session,
|
session: "Session",
|
||||||
_id=None,
|
_id: int = None,
|
||||||
name=None,
|
name: str = None,
|
||||||
prefix=None,
|
prefix: str = None,
|
||||||
hostid=None,
|
hostid: int = None,
|
||||||
start=True,
|
start: bool = True,
|
||||||
server=None,
|
server: "DistributedServer" = None,
|
||||||
assign_address=True,
|
assign_address: bool = True,
|
||||||
updown_script=None,
|
updown_script: str = None,
|
||||||
serverintf=None,
|
serverintf: CoreInterface = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a CtrlNet instance.
|
Creates a CtrlNet instance.
|
||||||
|
|
||||||
|
|
@ -756,7 +776,7 @@ class CtrlNet(CoreNetwork):
|
||||||
self.serverintf = serverintf
|
self.serverintf = serverintf
|
||||||
super().__init__(session, _id, name, start, server)
|
super().__init__(session, _id, name, start, server)
|
||||||
|
|
||||||
def add_addresses(self, index):
|
def add_addresses(self, index: int) -> None:
|
||||||
"""
|
"""
|
||||||
Add addresses used for created control networks,
|
Add addresses used for created control networks,
|
||||||
|
|
||||||
|
|
@ -777,7 +797,7 @@ class CtrlNet(CoreNetwork):
|
||||||
net_client = get_net_client(use_ovs, server.remote_cmd)
|
net_client = get_net_client(use_ovs, server.remote_cmd)
|
||||||
net_client.create_address(self.brname, current)
|
net_client.create_address(self.brname, current)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Startup functionality for the control network.
|
Startup functionality for the control network.
|
||||||
|
|
||||||
|
|
@ -806,7 +826,7 @@ class CtrlNet(CoreNetwork):
|
||||||
if self.serverintf:
|
if self.serverintf:
|
||||||
self.net_client.create_interface(self.brname, self.serverintf)
|
self.net_client.create_interface(self.brname, self.serverintf)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Control network shutdown.
|
Control network shutdown.
|
||||||
|
|
||||||
|
|
@ -835,7 +855,7 @@ class CtrlNet(CoreNetwork):
|
||||||
|
|
||||||
super().shutdown()
|
super().shutdown()
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags: int) -> List[LinkData]:
|
||||||
"""
|
"""
|
||||||
Do not include CtrlNet in link messages describing this session.
|
Do not include CtrlNet in link messages describing this session.
|
||||||
|
|
||||||
|
|
@ -853,11 +873,11 @@ class PtpNet(CoreNetwork):
|
||||||
|
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Attach a network interface, but limit attachment to two interfaces.
|
Attach a network interface, but limit attachment to two interfaces.
|
||||||
|
|
||||||
:param core.netns.vif.VEth netif: network interface
|
:param core.nodes.interface.CoreInterface netif: network interface
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if len(self._netif) >= 2:
|
if len(self._netif) >= 2:
|
||||||
|
|
@ -866,7 +886,14 @@ class PtpNet(CoreNetwork):
|
||||||
)
|
)
|
||||||
super().attach(netif)
|
super().attach(netif)
|
||||||
|
|
||||||
def data(self, message_type, lat=None, lon=None, alt=None):
|
def data(
|
||||||
|
self,
|
||||||
|
message_type: int,
|
||||||
|
lat: float = None,
|
||||||
|
lon: float = None,
|
||||||
|
alt: float = None,
|
||||||
|
source: str = None,
|
||||||
|
) -> NodeData:
|
||||||
"""
|
"""
|
||||||
Do not generate a Node Message for point-to-point links. They are
|
Do not generate a Node Message for point-to-point links. They are
|
||||||
built using a link message instead.
|
built using a link message instead.
|
||||||
|
|
@ -875,12 +902,13 @@ class PtpNet(CoreNetwork):
|
||||||
:param float lat: latitude
|
:param float lat: latitude
|
||||||
:param float lon: longitude
|
:param float lon: longitude
|
||||||
:param float alt: altitude
|
:param float alt: altitude
|
||||||
|
:param str source: source of node data
|
||||||
:return: node data object
|
:return: node data object
|
||||||
:rtype: core.emulator.data.NodeData
|
:rtype: core.emulator.data.NodeData
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags: int) -> List[LinkData]:
|
||||||
"""
|
"""
|
||||||
Build CORE API TLVs for a point-to-point link. One Link message
|
Build CORE API TLVs for a point-to-point link. One Link message
|
||||||
describes this network.
|
describes this network.
|
||||||
|
|
@ -997,7 +1025,7 @@ class HubNode(CoreNetwork):
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "hub"
|
type = "hub"
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Startup for a hub node, that disables mac learning after normal startup.
|
Startup for a hub node, that disables mac learning after normal startup.
|
||||||
|
|
||||||
|
|
@ -1018,8 +1046,14 @@ class WlanNode(CoreNetwork):
|
||||||
type = "wlan"
|
type = "wlan"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, session, _id=None, name=None, start=True, server=None, policy=None
|
self,
|
||||||
):
|
session: "Session",
|
||||||
|
_id: int = None,
|
||||||
|
name: str = None,
|
||||||
|
start: bool = True,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
policy: str = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a WlanNode instance.
|
Create a WlanNode instance.
|
||||||
|
|
||||||
|
|
@ -1036,7 +1070,7 @@ class WlanNode(CoreNetwork):
|
||||||
self.model = None
|
self.model = None
|
||||||
self.mobility = None
|
self.mobility = None
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Startup for a wlan node, that disables mac learning after normal startup.
|
Startup for a wlan node, that disables mac learning after normal startup.
|
||||||
|
|
||||||
|
|
@ -1045,11 +1079,11 @@ class WlanNode(CoreNetwork):
|
||||||
super().startup()
|
super().startup()
|
||||||
self.net_client.disable_mac_learning(self.brname)
|
self.net_client.disable_mac_learning(self.brname)
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif: CoreInterface) -> None:
|
||||||
"""
|
"""
|
||||||
Attach a network interface.
|
Attach a network interface.
|
||||||
|
|
||||||
:param core.nodes.interface.Veth netif: network interface
|
:param core.nodes.interface.CoreInterface netif: network interface
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
super().attach(netif)
|
super().attach(netif)
|
||||||
|
|
@ -1061,7 +1095,7 @@ class WlanNode(CoreNetwork):
|
||||||
# invokes any netif.poshook
|
# invokes any netif.poshook
|
||||||
netif.setposition(x, y, z)
|
netif.setposition(x, y, z)
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model: WirelessModelType, config: Dict[str, str]):
|
||||||
"""
|
"""
|
||||||
Sets the mobility and wireless model.
|
Sets the mobility and wireless model.
|
||||||
|
|
||||||
|
|
@ -1082,12 +1116,12 @@ class WlanNode(CoreNetwork):
|
||||||
self.mobility = model(session=self.session, _id=self.id)
|
self.mobility = model(session=self.session, _id=self.id)
|
||||||
self.mobility.update_config(config)
|
self.mobility.update_config(config)
|
||||||
|
|
||||||
def update_mobility(self, config):
|
def update_mobility(self, config: Dict[str, str]) -> None:
|
||||||
if not self.mobility:
|
if not self.mobility:
|
||||||
raise ValueError(f"no mobility set to update for node({self.id})")
|
raise ValueError(f"no mobility set to update for node({self.id})")
|
||||||
self.mobility.update_config(config)
|
self.mobility.update_config(config)
|
||||||
|
|
||||||
def updatemodel(self, config):
|
def updatemodel(self, config: Dict[str, str]) -> None:
|
||||||
if not self.model:
|
if not self.model:
|
||||||
raise ValueError(f"no model set to update for node({self.id})")
|
raise ValueError(f"no model set to update for node({self.id})")
|
||||||
logging.debug(
|
logging.debug(
|
||||||
|
|
@ -1099,7 +1133,7 @@ class WlanNode(CoreNetwork):
|
||||||
x, y, z = netif.node.position.get()
|
x, y, z = netif.node.position.get()
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags: int) -> List[LinkData]:
|
||||||
"""
|
"""
|
||||||
Retrieve all link data.
|
Retrieve all link data.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,31 @@ PhysicalNode class for including real systems in the emulated network.
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
from typing import IO, TYPE_CHECKING, List, Optional
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.constants import MOUNT_BIN, UMOUNT_BIN
|
from core.constants import MOUNT_BIN, UMOUNT_BIN
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.errors import CoreCommandError, CoreError
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import CoreInterface, Veth
|
||||||
from core.nodes.network import CoreNetwork, GreTap
|
from core.nodes.network import CoreNetwork, GreTap
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.session import Session
|
||||||
|
|
||||||
|
|
||||||
class PhysicalNode(CoreNodeBase):
|
class PhysicalNode(CoreNodeBase):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, session, _id=None, name=None, nodedir=None, start=True, server=None
|
self,
|
||||||
):
|
session,
|
||||||
|
_id: int = None,
|
||||||
|
name: str = None,
|
||||||
|
nodedir: str = None,
|
||||||
|
start: bool = True,
|
||||||
|
server: DistributedServer = None,
|
||||||
|
) -> None:
|
||||||
super().__init__(session, _id, name, start, server)
|
super().__init__(session, _id, name, start, server)
|
||||||
if not self.server:
|
if not self.server:
|
||||||
raise CoreError("physical nodes must be assigned to a remote server")
|
raise CoreError("physical nodes must be assigned to a remote server")
|
||||||
|
|
@ -29,11 +40,11 @@ class PhysicalNode(CoreNodeBase):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.makenodedir()
|
self.makenodedir()
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -47,7 +58,7 @@ class PhysicalNode(CoreNodeBase):
|
||||||
|
|
||||||
self.rmnodedir()
|
self.rmnodedir()
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh: str = "/bin/sh") -> str:
|
||||||
"""
|
"""
|
||||||
Create a terminal command string.
|
Create a terminal command string.
|
||||||
|
|
||||||
|
|
@ -56,7 +67,7 @@ class PhysicalNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
return sh
|
return sh
|
||||||
|
|
||||||
def sethwaddr(self, ifindex, addr):
|
def sethwaddr(self, ifindex: int, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Set hardware address for an interface.
|
Set hardware address for an interface.
|
||||||
|
|
||||||
|
|
@ -71,7 +82,7 @@ class PhysicalNode(CoreNodeBase):
|
||||||
if self.up:
|
if self.up:
|
||||||
self.net_client.device_mac(interface.name, addr)
|
self.net_client.device_mac(interface.name, addr)
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex: int, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Add an address to an interface.
|
Add an address to an interface.
|
||||||
|
|
||||||
|
|
@ -85,9 +96,13 @@ class PhysicalNode(CoreNodeBase):
|
||||||
self.net_client.create_address(interface.name, addr)
|
self.net_client.create_address(interface.name, addr)
|
||||||
interface.addaddr(addr)
|
interface.addaddr(addr)
|
||||||
|
|
||||||
def deladdr(self, ifindex, addr):
|
def deladdr(self, ifindex: int, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete an address from an interface.
|
Delete an address from an interface.
|
||||||
|
|
||||||
|
:param int ifindex: index of interface to delete
|
||||||
|
:param str addr: address to delete
|
||||||
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
interface = self._netif[ifindex]
|
interface = self._netif[ifindex]
|
||||||
|
|
||||||
|
|
@ -99,7 +114,9 @@ class PhysicalNode(CoreNodeBase):
|
||||||
if self.up:
|
if self.up:
|
||||||
self.net_client.delete_address(interface.name, str(addr))
|
self.net_client.delete_address(interface.name, str(addr))
|
||||||
|
|
||||||
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
def adoptnetif(
|
||||||
|
self, netif: CoreInterface, ifindex: int, hwaddr: str, addrlist: List[str]
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
When a link message is received linking this node to another part of
|
When a link message is received linking this node to another part of
|
||||||
the emulation, no new interface is created; instead, adopt the
|
the emulation, no new interface is created; instead, adopt the
|
||||||
|
|
@ -127,18 +144,17 @@ class PhysicalNode(CoreNodeBase):
|
||||||
|
|
||||||
def linkconfig(
|
def linkconfig(
|
||||||
self,
|
self,
|
||||||
netif,
|
netif: CoreInterface,
|
||||||
bw=None,
|
bw: float = None,
|
||||||
delay=None,
|
delay: float = None,
|
||||||
loss=None,
|
loss: float = None,
|
||||||
duplicate=None,
|
duplicate: float = None,
|
||||||
jitter=None,
|
jitter: float = None,
|
||||||
netif2=None,
|
netif2: CoreInterface = None,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Apply tc queing disciplines using LxBrNet.linkconfig()
|
Apply tc queing disciplines using linkconfig.
|
||||||
"""
|
"""
|
||||||
# borrow the tc qdisc commands from LxBrNet.linkconfig()
|
|
||||||
linux_bridge = CoreNetwork(session=self.session, start=False)
|
linux_bridge = CoreNetwork(session=self.session, start=False)
|
||||||
linux_bridge.up = True
|
linux_bridge.up = True
|
||||||
linux_bridge.linkconfig(
|
linux_bridge.linkconfig(
|
||||||
|
|
@ -152,7 +168,7 @@ class PhysicalNode(CoreNodeBase):
|
||||||
)
|
)
|
||||||
del linux_bridge
|
del linux_bridge
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self) -> int:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
while self.ifindex in self._netif:
|
while self.ifindex in self._netif:
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
|
|
@ -160,7 +176,14 @@ class PhysicalNode(CoreNodeBase):
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
def newnetif(
|
||||||
|
self,
|
||||||
|
net: Veth = None,
|
||||||
|
addrlist: List[str] = None,
|
||||||
|
hwaddr: str = None,
|
||||||
|
ifindex: int = None,
|
||||||
|
ifname: str = None,
|
||||||
|
) -> int:
|
||||||
logging.info("creating interface")
|
logging.info("creating interface")
|
||||||
if not addrlist:
|
if not addrlist:
|
||||||
addrlist = []
|
addrlist = []
|
||||||
|
|
@ -186,7 +209,7 @@ class PhysicalNode(CoreNodeBase):
|
||||||
self.adoptnetif(netif, ifindex, hwaddr, addrlist)
|
self.adoptnetif(netif, ifindex, hwaddr, addrlist)
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def privatedir(self, path):
|
def privatedir(self, path: str) -> None:
|
||||||
if path[0] != "/":
|
if path[0] != "/":
|
||||||
raise ValueError(f"path not fully qualified: {path}")
|
raise ValueError(f"path not fully qualified: {path}")
|
||||||
hostpath = os.path.join(
|
hostpath = os.path.join(
|
||||||
|
|
@ -195,21 +218,21 @@ class PhysicalNode(CoreNodeBase):
|
||||||
os.mkdir(hostpath)
|
os.mkdir(hostpath)
|
||||||
self.mount(hostpath, path)
|
self.mount(hostpath, path)
|
||||||
|
|
||||||
def mount(self, source, target):
|
def mount(self, source: str, target: str) -> None:
|
||||||
source = os.path.abspath(source)
|
source = os.path.abspath(source)
|
||||||
logging.info("mounting %s at %s", source, target)
|
logging.info("mounting %s at %s", source, target)
|
||||||
os.makedirs(target)
|
os.makedirs(target)
|
||||||
self.host_cmd(f"{MOUNT_BIN} --bind {source} {target}", cwd=self.nodedir)
|
self.host_cmd(f"{MOUNT_BIN} --bind {source} {target}", cwd=self.nodedir)
|
||||||
self._mounts.append((source, target))
|
self._mounts.append((source, target))
|
||||||
|
|
||||||
def umount(self, target):
|
def umount(self, target: str) -> None:
|
||||||
logging.info("unmounting '%s'", target)
|
logging.info("unmounting '%s'", target)
|
||||||
try:
|
try:
|
||||||
self.host_cmd(f"{UMOUNT_BIN} -l {target}", cwd=self.nodedir)
|
self.host_cmd(f"{UMOUNT_BIN} -l {target}", cwd=self.nodedir)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("unmounting failed for %s", target)
|
logging.exception("unmounting failed for %s", target)
|
||||||
|
|
||||||
def opennodefile(self, filename, mode="w"):
|
def opennodefile(self, filename: str, mode: str = "w") -> IO:
|
||||||
dirname, basename = os.path.split(filename)
|
dirname, basename = os.path.split(filename)
|
||||||
if not basename:
|
if not basename:
|
||||||
raise ValueError("no basename for filename: " + filename)
|
raise ValueError("no basename for filename: " + filename)
|
||||||
|
|
@ -225,13 +248,13 @@ class PhysicalNode(CoreNodeBase):
|
||||||
hostfilename = os.path.join(dirname, basename)
|
hostfilename = os.path.join(dirname, basename)
|
||||||
return open(hostfilename, mode)
|
return open(hostfilename, mode)
|
||||||
|
|
||||||
def nodefile(self, filename, contents, mode=0o644):
|
def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None:
|
||||||
with self.opennodefile(filename, "w") as node_file:
|
with self.opennodefile(filename, "w") as node_file:
|
||||||
node_file.write(contents)
|
node_file.write(contents)
|
||||||
os.chmod(node_file.name, mode)
|
os.chmod(node_file.name, mode)
|
||||||
logging.info("created nodefile: '%s'; mode: 0%o", node_file.name, mode)
|
logging.info("created nodefile: '%s'; mode: 0%o", node_file.name, mode)
|
||||||
|
|
||||||
def cmd(self, args, wait=True):
|
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
return self.host_cmd(args, wait=wait)
|
return self.host_cmd(args, wait=wait)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -244,7 +267,15 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
apitype = NodeTypes.RJ45.value
|
apitype = NodeTypes.RJ45.value
|
||||||
type = "rj45"
|
type = "rj45"
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, mtu=1500, start=True, server=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
session: "Session",
|
||||||
|
_id: int = None,
|
||||||
|
name: str = None,
|
||||||
|
mtu: int = 1500,
|
||||||
|
start: bool = True,
|
||||||
|
server: DistributedServer = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create an RJ45Node instance.
|
Create an RJ45Node instance.
|
||||||
|
|
||||||
|
|
@ -270,7 +301,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self) -> None:
|
||||||
"""
|
"""
|
||||||
Set the interface in the up state.
|
Set the interface in the up state.
|
||||||
|
|
||||||
|
|
@ -282,7 +313,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
self.net_client.device_up(self.localname)
|
self.net_client.device_up(self.localname)
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
Bring the interface down. Remove any addresses and queuing
|
Bring the interface down. Remove any addresses and queuing
|
||||||
disciplines.
|
disciplines.
|
||||||
|
|
@ -304,18 +335,18 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
|
|
||||||
# TODO: issue in that both classes inherited from provide the same method with
|
# TODO: issue in that both classes inherited from provide the same method with
|
||||||
# different signatures
|
# different signatures
|
||||||
def attachnet(self, net):
|
def attachnet(self, net: CoreNetworkBase) -> None:
|
||||||
"""
|
"""
|
||||||
Attach a network.
|
Attach a network.
|
||||||
|
|
||||||
:param core.coreobj.PyCoreNet net: network to attach
|
:param core.nodes.base.CoreNetworkBase net: network to attach
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
CoreInterface.attachnet(self, net)
|
CoreInterface.attachnet(self, net)
|
||||||
|
|
||||||
# TODO: issue in that both classes inherited from provide the same method with
|
# TODO: issue in that both classes inherited from provide the same method with
|
||||||
# different signatures
|
# different signatures
|
||||||
def detachnet(self):
|
def detachnet(self) -> None:
|
||||||
"""
|
"""
|
||||||
Detach a network.
|
Detach a network.
|
||||||
|
|
||||||
|
|
@ -323,7 +354,14 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
CoreInterface.detachnet(self)
|
CoreInterface.detachnet(self)
|
||||||
|
|
||||||
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
def newnetif(
|
||||||
|
self,
|
||||||
|
net: CoreNetworkBase = None,
|
||||||
|
addrlist: List[str] = None,
|
||||||
|
hwaddr: str = None,
|
||||||
|
ifindex: int = None,
|
||||||
|
ifname: str = None,
|
||||||
|
) -> int:
|
||||||
"""
|
"""
|
||||||
This is called when linking with another node. Since this node
|
This is called when linking with another node. Since this node
|
||||||
represents an interface, we do not create another object here,
|
represents an interface, we do not create another object here,
|
||||||
|
|
@ -359,7 +397,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
|
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def delnetif(self, ifindex):
|
def delnetif(self, ifindex: int) -> None:
|
||||||
"""
|
"""
|
||||||
Delete a network interface.
|
Delete a network interface.
|
||||||
|
|
||||||
|
|
@ -376,7 +414,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"ifindex {ifindex} does not exist")
|
raise ValueError(f"ifindex {ifindex} does not exist")
|
||||||
|
|
||||||
def netif(self, ifindex, net=None):
|
def netif(
|
||||||
|
self, ifindex: int, net: CoreNetworkBase = None
|
||||||
|
) -> Optional[CoreInterface]:
|
||||||
"""
|
"""
|
||||||
This object is considered the network interface, so we only
|
This object is considered the network interface, so we only
|
||||||
return self here. This keeps the RJ45Node compatible with
|
return self here. This keeps the RJ45Node compatible with
|
||||||
|
|
@ -398,20 +438,20 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getifindex(self, netif):
|
def getifindex(self, netif: CoreInterface) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Retrieve network interface index.
|
Retrieve network interface index.
|
||||||
|
|
||||||
:param core.nodes.interface.CoreInterface netif: network interface to retrieve index for
|
:param core.nodes.interface.CoreInterface netif: network interface to retrieve
|
||||||
|
index for
|
||||||
:return: interface index, None otherwise
|
:return: interface index, None otherwise
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
if netif != self:
|
if netif != self:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.ifindex
|
return self.ifindex
|
||||||
|
|
||||||
def addaddr(self, addr):
|
def addaddr(self, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Add address to to network interface.
|
Add address to to network interface.
|
||||||
|
|
||||||
|
|
@ -424,7 +464,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
self.net_client.create_address(self.name, addr)
|
self.net_client.create_address(self.name, addr)
|
||||||
CoreInterface.addaddr(self, addr)
|
CoreInterface.addaddr(self, addr)
|
||||||
|
|
||||||
def deladdr(self, addr):
|
def deladdr(self, addr: str) -> None:
|
||||||
"""
|
"""
|
||||||
Delete address from network interface.
|
Delete address from network interface.
|
||||||
|
|
||||||
|
|
@ -434,10 +474,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
self.net_client.delete_address(self.name, str(addr))
|
self.net_client.delete_address(self.name, str(addr))
|
||||||
|
|
||||||
CoreInterface.deladdr(self, addr)
|
CoreInterface.deladdr(self, addr)
|
||||||
|
|
||||||
def savestate(self):
|
def savestate(self) -> None:
|
||||||
"""
|
"""
|
||||||
Save the addresses and other interface state before using the
|
Save the addresses and other interface state before using the
|
||||||
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
||||||
|
|
@ -464,7 +503,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
continue
|
continue
|
||||||
self.old_addrs.append((items[1], None))
|
self.old_addrs.append((items[1], None))
|
||||||
|
|
||||||
def restorestate(self):
|
def restorestate(self) -> None:
|
||||||
"""
|
"""
|
||||||
Restore the addresses and other interface state after using it.
|
Restore the addresses and other interface state after using it.
|
||||||
|
|
||||||
|
|
@ -482,7 +521,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
if self.old_up:
|
if self.old_up:
|
||||||
self.net_client.device_up(self.localname)
|
self.net_client.device_up(self.localname)
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x: float = None, y: float = None, z: float = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Uses setposition from both parent classes.
|
Uses setposition from both parent classes.
|
||||||
|
|
||||||
|
|
@ -496,7 +535,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
CoreInterface.setposition(self, x, y, z)
|
CoreInterface.setposition(self, x, y, z)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def termcmdstring(self, sh):
|
def termcmdstring(self, sh: str) -> str:
|
||||||
"""
|
"""
|
||||||
Create a terminal command string.
|
Create a terminal command string.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue