2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
PhysicalNode class for including real systems in the emulated network.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import threading
|
2020-05-25 19:33:59 +01:00
|
|
|
from typing import IO, TYPE_CHECKING, List, Optional, Tuple
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-16 20:50:24 +01:00
|
|
|
from core.emulator.data import InterfaceData, LinkOptions
|
2020-01-13 22:08:49 +00:00
|
|
|
from core.emulator.distributed import DistributedServer
|
2020-05-30 06:48:00 +01:00
|
|
|
from core.emulator.enumerations import NodeTypes, TransportType
|
2019-10-17 01:11:21 +01:00
|
|
|
from core.errors import CoreCommandError, CoreError
|
2020-06-23 17:11:37 +01:00
|
|
|
from core.executables import MOUNT_BIN, UMOUNT_BIN
|
2020-01-13 22:08:49 +00:00
|
|
|
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
2020-06-09 20:42:15 +01:00
|
|
|
from core.nodes.interface import CoreInterface
|
2019-09-10 22:20:51 +01:00
|
|
|
from core.nodes.network import CoreNetwork, GreTap
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from core.emulator.session import Session
|
|
|
|
|
2019-04-30 07:31:47 +01:00
|
|
|
|
|
|
|
class PhysicalNode(CoreNodeBase):
|
2019-10-17 01:11:21 +01:00
|
|
|
def __init__(
|
2020-01-13 22:08:49 +00:00
|
|
|
self,
|
2020-05-25 19:33:59 +01:00
|
|
|
session: "Session",
|
2020-01-13 22:08:49 +00:00
|
|
|
_id: int = None,
|
|
|
|
name: str = None,
|
|
|
|
nodedir: str = None,
|
|
|
|
server: DistributedServer = None,
|
|
|
|
) -> None:
|
2020-06-14 17:37:58 +01:00
|
|
|
super().__init__(session, _id, name, server)
|
2019-10-17 01:11:21 +01:00
|
|
|
if not self.server:
|
|
|
|
raise CoreError("physical nodes must be assigned to a remote server")
|
2020-05-25 19:33:59 +01:00
|
|
|
self.nodedir: Optional[str] = nodedir
|
|
|
|
self.lock: threading.RLock = threading.RLock()
|
|
|
|
self._mounts: List[Tuple[str, str]] = []
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def startup(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
with self.lock:
|
|
|
|
self.makenodedir()
|
2020-06-14 17:37:58 +01:00
|
|
|
self.up = True
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def shutdown(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
if not self.up:
|
|
|
|
return
|
|
|
|
|
|
|
|
with self.lock:
|
|
|
|
while self._mounts:
|
|
|
|
_source, target = self._mounts.pop(-1)
|
|
|
|
self.umount(target)
|
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
for iface in self.get_ifaces():
|
|
|
|
iface.shutdown()
|
2019-04-30 07:31:47 +01:00
|
|
|
|
|
|
|
self.rmnodedir()
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def termcmdstring(self, sh: str = "/bin/sh") -> str:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Create a terminal command string.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param sh: shell to execute command in
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: str
|
|
|
|
"""
|
|
|
|
return sh
|
|
|
|
|
2020-06-17 07:25:26 +01:00
|
|
|
def set_mac(self, iface_id: int, mac: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-17 07:25:26 +01:00
|
|
|
Set mac address for an interface.
|
2020-01-09 04:44:15 +00:00
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
:param iface_id: index of interface to set hardware address for
|
2020-06-17 07:25:26 +01:00
|
|
|
:param mac: mac address to set
|
2020-01-09 04:44:15 +00:00
|
|
|
:return: nothing
|
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
iface = self.get_iface(iface_id)
|
2020-06-17 07:25:26 +01:00
|
|
|
iface.set_mac(mac)
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
2020-06-23 17:11:37 +01:00
|
|
|
self.net_client.device_mac(iface.name, str(iface.mac))
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
def add_ip(self, iface_id: int, ip: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
Add an ip address to an interface in the format "10.0.0.1/24".
|
2020-01-09 04:44:15 +00:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
:param iface_id: id of interface to add address to
|
|
|
|
:param ip: address to add to interface
|
2020-01-09 04:44:15 +00:00
|
|
|
:return: nothing
|
2020-06-19 18:54:58 +01:00
|
|
|
:raises CoreError: when ip address provided is invalid
|
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-16 17:30:16 +01:00
|
|
|
iface = self.get_iface(iface_id)
|
2020-06-19 18:54:58 +01:00
|
|
|
iface.add_ip(ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
2020-06-19 18:54:58 +01:00
|
|
|
self.net_client.create_address(iface.name, ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
def remove_ip(self, iface_id: int, ip: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
Remove an ip address from an interface in the format "10.0.0.1/24".
|
2020-01-13 22:08:49 +00:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
:param iface_id: id of interface to delete address from
|
|
|
|
:param ip: ip address to remove from interface
|
2020-01-13 22:08:49 +00:00
|
|
|
:return: nothing
|
2020-06-19 18:54:58 +01:00
|
|
|
:raises CoreError: when ip address provided is invalid
|
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
iface = self.get_iface(iface_id)
|
|
|
|
iface.remove_ip(ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
2020-06-19 18:54:58 +01:00
|
|
|
self.net_client.delete_address(iface.name, ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
def adopt_iface(
|
2020-06-19 18:54:58 +01:00
|
|
|
self, iface: CoreInterface, iface_id: int, mac: str, ips: List[str]
|
2020-01-13 22:08:49 +00:00
|
|
|
) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
When a link message is received linking this node to another part of
|
|
|
|
the emulation, no new interface is created; instead, adopt the
|
2020-06-16 17:30:16 +01:00
|
|
|
GreTap interface as the node interface.
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-16 17:30:16 +01:00
|
|
|
iface.name = f"gt{iface_id}"
|
|
|
|
iface.node = self
|
|
|
|
self.add_iface(iface, iface_id)
|
2019-04-30 07:31:47 +01:00
|
|
|
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
|
|
|
if self.up:
|
2020-06-16 17:30:16 +01:00
|
|
|
self.net_client.device_down(iface.localname)
|
|
|
|
self.net_client.device_name(iface.localname, iface.name)
|
|
|
|
iface.localname = iface.name
|
2020-06-17 07:25:26 +01:00
|
|
|
if mac:
|
|
|
|
self.set_mac(iface_id, mac)
|
2020-06-19 18:54:58 +01:00
|
|
|
for ip in ips:
|
|
|
|
self.add_ip(iface_id, ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
2020-06-16 17:30:16 +01:00
|
|
|
self.net_client.device_up(iface.localname)
|
2019-09-10 23:10:24 +01:00
|
|
|
|
|
|
|
def linkconfig(
|
2020-06-16 17:30:16 +01:00
|
|
|
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
|
2020-01-13 22:08:49 +00:00
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Apply tc queing disciplines using linkconfig.
|
|
|
|
"""
|
2020-06-14 17:37:58 +01:00
|
|
|
linux_bridge = CoreNetwork(self.session)
|
2019-04-30 07:31:47 +01:00
|
|
|
linux_bridge.up = True
|
2020-06-16 17:30:16 +01:00
|
|
|
linux_bridge.linkconfig(iface, options, iface2)
|
2019-04-30 07:31:47 +01:00
|
|
|
del linux_bridge
|
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
def next_iface_id(self) -> int:
|
2019-04-30 07:31:47 +01:00
|
|
|
with self.lock:
|
2020-06-16 17:30:16 +01:00
|
|
|
while self.iface_id in self.ifaces:
|
|
|
|
self.iface_id += 1
|
|
|
|
iface_id = self.iface_id
|
|
|
|
self.iface_id += 1
|
|
|
|
return iface_id
|
|
|
|
|
|
|
|
def new_iface(
|
|
|
|
self, net: CoreNetworkBase, iface_data: InterfaceData
|
2020-06-13 01:32:55 +01:00
|
|
|
) -> CoreInterface:
|
2019-04-30 07:31:47 +01:00
|
|
|
logging.info("creating interface")
|
2020-06-19 19:11:45 +01:00
|
|
|
ips = iface_data.get_ips()
|
2020-06-16 17:30:16 +01:00
|
|
|
iface_id = iface_data.id
|
|
|
|
if iface_id is None:
|
|
|
|
iface_id = self.next_iface_id()
|
|
|
|
name = iface_data.name
|
2020-06-09 20:42:15 +01:00
|
|
|
if name is None:
|
2020-06-16 17:30:16 +01:00
|
|
|
name = f"gt{iface_id}"
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
|
|
|
# this is reached when this node is linked to a network node
|
|
|
|
# tunnel to net not built yet, so build it now and adopt it
|
2019-10-17 19:10:59 +01:00
|
|
|
_, remote_tap = self.session.distributed.create_gre_tunnel(net, self.server)
|
2020-06-19 19:11:45 +01:00
|
|
|
self.adopt_iface(remote_tap, iface_id, iface_data.mac, ips)
|
2020-06-11 21:59:29 +01:00
|
|
|
return remote_tap
|
2019-10-17 01:11:21 +01:00
|
|
|
else:
|
|
|
|
# this is reached when configuring services (self.up=False)
|
2020-06-16 17:30:16 +01:00
|
|
|
iface = GreTap(node=self, name=name, session=self.session, start=False)
|
2020-06-19 19:11:45 +01:00
|
|
|
self.adopt_iface(iface, iface_id, iface_data.mac, ips)
|
2020-06-16 17:30:16 +01:00
|
|
|
return iface
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def privatedir(self, path: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
if path[0] != "/":
|
2019-10-18 02:59:50 +01:00
|
|
|
raise ValueError(f"path not fully qualified: {path}")
|
2019-09-10 23:10:24 +01:00
|
|
|
hostpath = os.path.join(
|
|
|
|
self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")
|
|
|
|
)
|
2019-04-30 07:31:47 +01:00
|
|
|
os.mkdir(hostpath)
|
|
|
|
self.mount(hostpath, path)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def mount(self, source: str, target: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
source = os.path.abspath(source)
|
|
|
|
logging.info("mounting %s at %s", source, target)
|
|
|
|
os.makedirs(target)
|
2019-10-19 07:28:09 +01:00
|
|
|
self.host_cmd(f"{MOUNT_BIN} --bind {source} {target}", cwd=self.nodedir)
|
2019-04-30 07:31:47 +01:00
|
|
|
self._mounts.append((source, target))
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def umount(self, target: str) -> None:
|
2019-10-18 02:59:50 +01:00
|
|
|
logging.info("unmounting '%s'", target)
|
2019-04-30 07:31:47 +01:00
|
|
|
try:
|
2019-10-19 07:28:09 +01:00
|
|
|
self.host_cmd(f"{UMOUNT_BIN} -l {target}", cwd=self.nodedir)
|
2019-04-30 07:31:47 +01:00
|
|
|
except CoreCommandError:
|
|
|
|
logging.exception("unmounting failed for %s", target)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def opennodefile(self, filename: str, mode: str = "w") -> IO:
|
2019-04-30 07:31:47 +01:00
|
|
|
dirname, basename = os.path.split(filename)
|
|
|
|
if not basename:
|
|
|
|
raise ValueError("no basename for filename: " + filename)
|
|
|
|
|
|
|
|
if dirname and dirname[0] == "/":
|
|
|
|
dirname = dirname[1:]
|
|
|
|
|
|
|
|
dirname = dirname.replace("/", ".")
|
|
|
|
dirname = os.path.join(self.nodedir, dirname)
|
|
|
|
if not os.path.isdir(dirname):
|
2019-05-06 01:11:07 +01:00
|
|
|
os.makedirs(dirname, mode=0o755)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
|
|
|
hostfilename = os.path.join(dirname, basename)
|
|
|
|
return open(hostfilename, mode)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None:
|
2020-06-14 20:36:07 +01:00
|
|
|
with self.opennodefile(filename, "w") as f:
|
|
|
|
f.write(contents)
|
|
|
|
os.chmod(f.name, mode)
|
|
|
|
logging.info("created nodefile: '%s'; mode: 0%o", f.name, mode)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
2019-10-19 07:28:09 +01:00
|
|
|
return self.host_cmd(args, wait=wait)
|
2019-10-17 01:11:21 +01:00
|
|
|
|
2020-06-14 20:36:07 +01:00
|
|
|
def addfile(self, srcname: str, filename: str) -> None:
|
2020-06-14 21:35:06 +01:00
|
|
|
raise CoreError("physical node does not support addfile")
|
2020-06-14 20:36:07 +01:00
|
|
|
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-05-26 07:18:20 +01:00
|
|
|
class Rj45Node(CoreNodeBase):
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
RJ45Node is a physical interface on the host linked to the emulated
|
|
|
|
network.
|
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
|
2020-05-25 19:33:59 +01:00
|
|
|
apitype: NodeTypes = NodeTypes.RJ45
|
|
|
|
type: str = "rj45"
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
session: "Session",
|
|
|
|
_id: int = None,
|
|
|
|
name: str = None,
|
|
|
|
mtu: int = 1500,
|
|
|
|
server: DistributedServer = None,
|
|
|
|
) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Create an RJ45Node instance.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param session: core session instance
|
|
|
|
:param _id: node id
|
|
|
|
:param name: node name
|
2019-04-30 07:31:47 +01:00
|
|
|
:param mtu: rj45 mtu
|
2020-01-16 19:00:57 +00:00
|
|
|
:param server: remote server node
|
2019-10-14 23:43:57 +01:00
|
|
|
will run on, default is None for localhost
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-14 17:37:58 +01:00
|
|
|
super().__init__(session, _id, name, server)
|
2020-06-16 17:30:16 +01:00
|
|
|
self.iface = CoreInterface(session, self, name, name, mtu, server)
|
|
|
|
self.iface.transport_type = TransportType.RAW
|
2020-05-25 19:33:59 +01:00
|
|
|
self.lock: threading.RLock = threading.RLock()
|
2020-06-16 17:30:16 +01:00
|
|
|
self.iface_id: Optional[int] = None
|
2020-05-25 19:33:59 +01:00
|
|
|
self.old_up: bool = False
|
2020-05-26 07:18:20 +01:00
|
|
|
self.old_addrs: List[Tuple[str, Optional[str]]] = []
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def startup(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Set the interface in the up state.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
:raises CoreCommandError: when there is a command exception
|
|
|
|
"""
|
|
|
|
# interface will also be marked up during net.attach()
|
|
|
|
self.savestate()
|
2020-06-16 17:30:16 +01:00
|
|
|
self.net_client.device_up(self.iface.localname)
|
2019-04-30 07:31:47 +01:00
|
|
|
self.up = True
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def shutdown(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Bring the interface down. Remove any addresses and queuing
|
|
|
|
disciplines.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
if not self.up:
|
|
|
|
return
|
2020-06-16 17:30:16 +01:00
|
|
|
localname = self.iface.localname
|
2020-05-26 07:18:20 +01:00
|
|
|
self.net_client.device_down(localname)
|
|
|
|
self.net_client.device_flush(localname)
|
2019-04-30 07:31:47 +01:00
|
|
|
try:
|
2020-05-26 07:18:20 +01:00
|
|
|
self.net_client.delete_tc(localname)
|
2019-04-30 07:31:47 +01:00
|
|
|
except CoreCommandError:
|
2020-05-26 07:18:20 +01:00
|
|
|
pass
|
2019-04-30 07:31:47 +01:00
|
|
|
self.up = False
|
|
|
|
self.restorestate()
|
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
def new_iface(
|
|
|
|
self, net: CoreNetworkBase, iface_data: InterfaceData
|
2020-06-13 01:32:55 +01:00
|
|
|
) -> CoreInterface:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
This is called when linking with another node. Since this node
|
|
|
|
represents an interface, we do not create another object here,
|
|
|
|
but attach ourselves to the given network.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param net: new network instance
|
2020-06-16 17:30:16 +01:00
|
|
|
:param iface_data: interface data for new interface
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: interface index
|
2020-01-18 05:12:14 +00:00
|
|
|
:raises ValueError: when an interface has already been created, one max
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
with self.lock:
|
2020-06-16 17:30:16 +01:00
|
|
|
iface_id = iface_data.id
|
|
|
|
if iface_id is None:
|
|
|
|
iface_id = 0
|
|
|
|
if self.iface.net is not None:
|
|
|
|
raise CoreError("RJ45 nodes support at most 1 network interface")
|
|
|
|
self.ifaces[iface_id] = self.iface
|
|
|
|
self.iface_id = iface_id
|
2019-04-30 07:31:47 +01:00
|
|
|
if net is not None:
|
2020-06-16 17:30:16 +01:00
|
|
|
self.iface.attachnet(net)
|
2020-06-19 19:11:45 +01:00
|
|
|
for ip in iface_data.get_ips():
|
|
|
|
self.add_ip(ip)
|
2020-06-16 17:30:16 +01:00
|
|
|
return self.iface
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
def delete_iface(self, iface_id: int) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Delete a network interface.
|
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
:param iface_id: interface index to delete
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2020-06-16 17:30:16 +01:00
|
|
|
self.get_iface(iface_id)
|
|
|
|
self.ifaces.pop(iface_id)
|
|
|
|
self.shutdown()
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
def get_iface(self, iface_id: int) -> CoreInterface:
|
|
|
|
if iface_id != self.iface_id or iface_id not in self.ifaces:
|
|
|
|
raise CoreError(f"node({self.name}) interface({iface_id}) does not exist")
|
|
|
|
return self.iface
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
def get_iface_id(self, iface: CoreInterface) -> Optional[int]:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Retrieve network interface index.
|
|
|
|
|
2020-06-16 17:30:16 +01:00
|
|
|
:param iface: network interface to retrieve
|
2020-01-13 22:08:49 +00:00
|
|
|
index for
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: interface index, None otherwise
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2020-06-16 17:30:16 +01:00
|
|
|
if iface is not self.iface:
|
|
|
|
raise CoreError(f"node({self.name}) does not have interface({iface.name})")
|
|
|
|
return self.iface_id
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
def add_ip(self, ip: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
Add an ip address to an interface in the format "10.0.0.1/24".
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
:param ip: address to add to interface
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: nothing
|
2020-06-19 18:54:58 +01:00
|
|
|
:raises CoreError: when ip address provided is invalid
|
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
self.iface.add_ip(ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
2020-06-19 18:54:58 +01:00
|
|
|
self.net_client.create_address(self.name, ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
def remove_ip(self, ip: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
Remove an ip address from an interface in the format "10.0.0.1/24".
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-19 18:54:58 +01:00
|
|
|
:param ip: ip address to remove from interface
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: nothing
|
2020-06-19 18:54:58 +01:00
|
|
|
:raises CoreError: when ip address provided is invalid
|
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2020-06-19 18:54:58 +01:00
|
|
|
self.iface.remove_ip(ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.up:
|
2020-06-19 18:54:58 +01:00
|
|
|
self.net_client.delete_address(self.name, ip)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def savestate(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Save the addresses and other interface state before using the
|
|
|
|
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
:raises CoreCommandError: when there is a command exception
|
|
|
|
"""
|
|
|
|
self.old_up = False
|
2020-05-26 07:18:20 +01:00
|
|
|
self.old_addrs: List[Tuple[str, Optional[str]]] = []
|
2020-06-16 17:30:16 +01:00
|
|
|
localname = self.iface.localname
|
2020-05-26 07:18:20 +01:00
|
|
|
output = self.net_client.address_show(localname)
|
2019-04-30 07:31:47 +01:00
|
|
|
for line in output.split("\n"):
|
|
|
|
items = line.split()
|
|
|
|
if len(items) < 2:
|
|
|
|
continue
|
2020-05-26 07:18:20 +01:00
|
|
|
if items[1] == f"{localname}:":
|
2019-04-30 07:31:47 +01:00
|
|
|
flags = items[2][1:-1].split(",")
|
|
|
|
if "UP" in flags:
|
|
|
|
self.old_up = True
|
|
|
|
elif items[0] == "inet":
|
|
|
|
self.old_addrs.append((items[1], items[3]))
|
|
|
|
elif items[0] == "inet6":
|
|
|
|
if items[1][:4] == "fe80":
|
|
|
|
continue
|
|
|
|
self.old_addrs.append((items[1], None))
|
2020-05-26 07:18:20 +01:00
|
|
|
logging.info("saved rj45 state: addrs(%s) up(%s)", self.old_addrs, self.old_up)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def restorestate(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Restore the addresses and other interface state after using it.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
:raises CoreCommandError: when there is a command exception
|
|
|
|
"""
|
2020-06-16 17:30:16 +01:00
|
|
|
localname = self.iface.localname
|
2020-05-26 07:18:20 +01:00
|
|
|
logging.info("restoring rj45 state: %s", localname)
|
2019-04-30 07:31:47 +01:00
|
|
|
for addr in self.old_addrs:
|
2020-05-26 07:18:20 +01:00
|
|
|
self.net_client.create_address(localname, addr[0], addr[1])
|
2019-04-30 07:31:47 +01:00
|
|
|
if self.old_up:
|
2020-05-26 07:18:20 +01:00
|
|
|
self.net_client.device_up(localname)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-03-19 23:40:43 +00:00
|
|
|
def setposition(self, x: float = None, y: float = None, z: float = None) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Uses setposition from both parent classes.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
|
|
|
:param z: z position
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: True if position changed, False otherwise
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2020-05-26 07:18:20 +01:00
|
|
|
super().setposition(x, y, z)
|
2020-06-16 17:30:16 +01:00
|
|
|
self.iface.setposition()
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def termcmdstring(self, sh: str) -> str:
|
2020-06-14 21:35:06 +01:00
|
|
|
raise CoreError("rj45 does not support terminal commands")
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-06-14 20:36:07 +01:00
|
|
|
def addfile(self, srcname: str, filename: str) -> None:
|
2020-06-14 21:35:06 +01:00
|
|
|
raise CoreError("rj45 does not support addfile")
|
2020-06-14 20:36:07 +01:00
|
|
|
|
|
|
|
def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None:
|
2020-06-14 21:35:06 +01:00
|
|
|
raise CoreError("rj45 does not support nodefile")
|
2020-06-14 20:36:07 +01:00
|
|
|
|
|
|
|
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
2020-06-14 21:35:06 +01:00
|
|
|
raise CoreError("rj45 does not support cmds")
|