def attachnet(self, net): """ Attach a network. :param core.coreobj.PyCoreNet net: network to attach :return: nothing """ CoreInterface.attachnet(self, net)
def attachnet(self, net: CoreNetworkBase) -> None: """ Attach a network. :param net: network to attach :return: nothing """ CoreInterface.attachnet(self, net)
class Rj45Node(CoreNodeBase): """ RJ45Node is a physical interface on the host linked to the emulated network. """ apitype: NodeTypes = NodeTypes.RJ45 type: str = "rj45" def __init__( self, session: "Session", _id: int = None, name: str = None, mtu: int = 1500, server: DistributedServer = None, ) -> None: """ Create an RJ45Node instance. :param session: core session instance :param _id: node id :param name: node name :param mtu: rj45 mtu :param server: remote server node will run on, default is None for localhost """ super().__init__(session, _id, name, server) self.iface = CoreInterface(session, self, name, name, mtu, server) self.iface.transport_type = TransportType.RAW self.lock: threading.RLock = threading.RLock() self.iface_id: Optional[int] = None self.old_up: bool = False self.old_addrs: List[Tuple[str, Optional[str]]] = [] def startup(self) -> None: """ 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() self.net_client.device_up(self.iface.localname) self.up = True def shutdown(self) -> None: """ Bring the interface down. Remove any addresses and queuing disciplines. :return: nothing """ if not self.up: return localname = self.iface.localname self.net_client.device_down(localname) self.net_client.device_flush(localname) try: self.net_client.delete_tc(localname) except CoreCommandError: pass self.up = False self.restorestate() def path_exists(self, path: str) -> bool: """ Determines if a file or directory path exists. :param path: path to file or directory :return: True if path exists, False otherwise """ try: self.host_cmd(f"{TEST} -e {path}") return True except CoreCommandError: return False def new_iface(self, net: CoreNetworkBase, iface_data: InterfaceData) -> CoreInterface: """ 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. :param net: new network instance :param iface_data: interface data for new interface :return: interface index :raises ValueError: when an interface has already been created, one max """ with self.lock: 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 if net is not None: self.iface.attachnet(net) for ip in iface_data.get_ips(): self.add_ip(ip) return self.iface def delete_iface(self, iface_id: int) -> None: """ Delete a network interface. :param iface_id: interface index to delete :return: nothing """ self.get_iface(iface_id) self.ifaces.pop(iface_id) self.shutdown() 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 def get_iface_id(self, iface: CoreInterface) -> Optional[int]: """ Retrieve network interface index. :param iface: network interface to retrieve index for :return: interface index, None otherwise """ if iface is not self.iface: raise CoreError( f"node({self.name}) does not have interface({iface.name})") return self.iface_id def add_ip(self, ip: str) -> None: """ Add an ip address to an interface in the format "10.0.0.1/24". :param ip: address to add to interface :return: nothing :raises CoreError: when ip address provided is invalid :raises CoreCommandError: when a non-zero exit status occurs """ self.iface.add_ip(ip) if self.up: self.net_client.create_address(self.name, ip) def remove_ip(self, ip: str) -> None: """ Remove an ip address from an interface in the format "10.0.0.1/24". :param ip: ip address to remove from interface :return: nothing :raises CoreError: when ip address provided is invalid :raises CoreCommandError: when a non-zero exit status occurs """ self.iface.remove_ip(ip) if self.up: self.net_client.delete_address(self.name, ip) def savestate(self) -> None: """ 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 self.old_addrs: List[Tuple[str, Optional[str]]] = [] localname = self.iface.localname output = self.net_client.address_show(localname) for line in output.split("\n"): items = line.split() if len(items) < 2: continue if items[1] == f"{localname}:": 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)) logging.info("saved rj45 state: addrs(%s) up(%s)", self.old_addrs, self.old_up) def restorestate(self) -> None: """ Restore the addresses and other interface state after using it. :return: nothing :raises CoreCommandError: when there is a command exception """ localname = self.iface.localname logging.info("restoring rj45 state: %s", localname) for addr in self.old_addrs: self.net_client.create_address(localname, addr[0], addr[1]) if self.old_up: self.net_client.device_up(localname) def setposition(self, x: float = None, y: float = None, z: float = None) -> None: """ Uses setposition from both parent classes. :param x: x position :param y: y position :param z: z position :return: True if position changed, False otherwise """ super().setposition(x, y, z) self.iface.setposition() def termcmdstring(self, sh: str) -> str: raise CoreError("rj45 does not support terminal commands") def addfile(self, srcname: str, filename: str) -> None: raise CoreError("rj45 does not support addfile") def nodefile(self, filename: str, contents: str, mode: int = 0o644) -> None: raise CoreError("rj45 does not support nodefile") def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: raise CoreError("rj45 does not support cmds")
class Rj45Node(CoreNodeBase): """ RJ45Node is a physical interface on the host linked to the emulated network. """ apitype: NodeTypes = NodeTypes.RJ45 type: str = "rj45" 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. :param session: core session instance :param _id: node id :param name: node name :param mtu: rj45 mtu :param start: start flag :param server: remote server node will run on, default is None for localhost """ super().__init__(session, _id, name, start, server) self.interface = CoreInterface(session, self, name, name, mtu, server) self.interface.transport_type = TransportType.RAW self.lock: threading.RLock = threading.RLock() self.ifindex: Optional[int] = None self.old_up: bool = False self.old_addrs: List[Tuple[str, Optional[str]]] = [] if start: self.startup() def startup(self) -> None: """ 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() self.net_client.device_up(self.interface.localname) self.up = True def shutdown(self) -> None: """ Bring the interface down. Remove any addresses and queuing disciplines. :return: nothing """ if not self.up: return localname = self.interface.localname self.net_client.device_down(localname) self.net_client.device_flush(localname) try: self.net_client.delete_tc(localname) except CoreCommandError: pass self.up = False self.restorestate() def newnetif(self, net: CoreNetworkBase, interface: InterfaceData) -> int: """ 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. :param net: new network instance :param interface: interface data for new interface :return: interface index :raises ValueError: when an interface has already been created, one max """ with self.lock: ifindex = interface.id if ifindex is None: ifindex = 0 if self.interface.net is not None: raise ValueError( "RJ45 nodes support at most 1 network interface") self._netif[ifindex] = self.interface self.ifindex = ifindex if net is not None: self.interface.attachnet(net) for addr in interface.get_addresses(): self.addaddr(addr) return ifindex def delnetif(self, ifindex: int) -> None: """ Delete a network interface. :param ifindex: interface index to delete :return: nothing """ if ifindex is None: ifindex = 0 self._netif.pop(ifindex) if ifindex == self.ifindex: self.shutdown() else: raise ValueError(f"ifindex {ifindex} does not exist") def netif(self, ifindex: int, net: CoreNetworkBase = None) -> Optional[CoreInterface]: """ This object is considered the network interface, so we only return self here. This keeps the RJ45Node compatible with real nodes. :param ifindex: interface index to retrieve :param net: network to retrieve :return: a network interface """ if net is not None and net == self.interface.net: return self.interface if ifindex is None: ifindex = 0 if ifindex == self.ifindex: return self.interface return None def getifindex(self, netif: CoreInterface) -> Optional[int]: """ Retrieve network interface index. :param netif: network interface to retrieve index for :return: interface index, None otherwise """ if netif != self.interface: return None return self.ifindex def addaddr(self, addr: str) -> None: """ Add address to to network interface. :param addr: address to add :return: nothing :raises CoreCommandError: when there is a command exception """ addr = utils.validate_ip(addr) if self.up: self.net_client.create_address(self.name, addr) self.interface.addaddr(addr) def deladdr(self, addr: str) -> None: """ Delete address from network interface. :param addr: address to delete :return: nothing :raises CoreCommandError: when there is a command exception """ if self.up: self.net_client.delete_address(self.name, addr) self.interface.deladdr(addr) def savestate(self) -> None: """ 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 self.old_addrs: List[Tuple[str, Optional[str]]] = [] localname = self.interface.localname output = self.net_client.address_show(localname) for line in output.split("\n"): items = line.split() if len(items) < 2: continue if items[1] == f"{localname}:": 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)) logging.info("saved rj45 state: addrs(%s) up(%s)", self.old_addrs, self.old_up) def restorestate(self) -> None: """ Restore the addresses and other interface state after using it. :return: nothing :raises CoreCommandError: when there is a command exception """ localname = self.interface.localname logging.info("restoring rj45 state: %s", localname) for addr in self.old_addrs: self.net_client.create_address(localname, addr[0], addr[1]) if self.old_up: self.net_client.device_up(localname) def setposition(self, x: float = None, y: float = None, z: float = None) -> None: """ Uses setposition from both parent classes. :param x: x position :param y: y position :param z: z position :return: True if position changed, False otherwise """ super().setposition(x, y, z) self.interface.setposition() def termcmdstring(self, sh: str) -> str: """ Create a terminal command string. :param sh: shell to execute command in :return: str """ raise NotImplementedError