def __init__( self, session: "Session", name: str, localname: str, mtu: int = DEFAULT_MTU, server: "DistributedServer" = None, node: "CoreNode" = None, ) -> None: """ Creates a CoreInterface instance. :param session: core session instance :param name: interface name :param localname: interface local name :param mtu: mtu value :param server: remote server node will run on, default is None for localhost :param node: node for interface """ if len(name) >= 16: raise CoreError(f"interface name ({name}) too long, max 16") if len(localname) >= 16: raise CoreError( f"interface local name ({localname}) too long, max 16") self.session: "Session" = session self.node: Optional["CoreNode"] = node self.name: str = name self.localname: str = localname self.up: bool = False self.mtu: int = mtu self.net: Optional[CoreNetworkBase] = None self.othernet: Optional[CoreNetworkBase] = None self.ip4s: List[netaddr.IPNetwork] = [] self.ip6s: List[netaddr.IPNetwork] = [] self.mac: Optional[netaddr.EUI] = None # placeholder position hook self.poshook: Callable[[CoreInterface], None] = lambda x: None # used with EMANE self.transport_type: TransportType = TransportType.VIRTUAL # id of interface for node self.node_id: Optional[int] = None # id of interface for network self.net_id: Optional[int] = None # id used to find flow data self.flow_id: Optional[int] = None self.server: Optional["DistributedServer"] = server self.net_client: LinuxNetClient = get_net_client( self.session.use_ovs(), self.host_cmd) self.control: bool = False # configuration data self.has_local_netem: bool = False self.local_options: LinkOptions = LinkOptions() self.has_netem: bool = False self.options: LinkOptions = LinkOptions()
def get_link_options(self, unidirectional: int) -> LinkOptions: """ Get currently set params as link options. :param unidirectional: unidirectional setting :return: link options """ delay = self.getparam("delay") if delay is not None: delay = int(delay) bandwidth = self.getparam("bw") if bandwidth is not None: bandwidth = int(bandwidth) dup = self.getparam("duplicate") if dup is not None: dup = int(dup) jitter = self.getparam("jitter") if jitter is not None: jitter = int(jitter) return LinkOptions( delay=delay, bandwidth=bandwidth, dup=dup, jitter=jitter, loss=self.getparam("loss"), unidirectional=unidirectional, )
def test_add_node_to_node_uni(self, session: Session, ip_prefixes: IpPrefixes): # given node1 = session.add_node(CoreNode) node2 = session.add_node(CoreNode) iface1_data = ip_prefixes.create_iface(node1) iface2_data = ip_prefixes.create_iface(node2) link_options1 = LinkOptions( delay=50, bandwidth=5000000, loss=25, dup=25, jitter=10, buffer=100, unidirectional=True, ) link_options2 = LinkOptions( delay=51, bandwidth=5000001, loss=26, dup=26, jitter=11, buffer=101, unidirectional=True, ) # when iface1, iface2 = session.add_link(node1.id, node2.id, iface1_data, iface2_data, link_options1) session.update_link(node2.id, node1.id, iface2_data.id, iface1_data.id, link_options2) # then assert node1.get_iface(iface1_data.id) assert node2.get_iface(iface2_data.id) assert iface1 is not None assert iface2 is not None assert iface1.local_options == link_options1 assert iface1.has_local_netem assert iface2.local_options == link_options2 assert iface2.has_local_netem
def test_update_ptp(self, session: Session, ip_prefixes: IpPrefixes): # given delay = 50 bandwidth = 5000000 loss = 25 dup = 25 jitter = 10 buffer = 100 node1 = session.add_node(CoreNode) node2 = session.add_node(CoreNode) iface1_data = ip_prefixes.create_iface(node1) iface2_data = ip_prefixes.create_iface(node2) session.add_link(node1.id, node2.id, iface1_data, iface2_data) iface1 = node1.get_iface(iface1_data.id) iface2 = node2.get_iface(iface2_data.id) assert iface1.getparam("delay") != delay assert iface1.getparam("bw") != bandwidth assert iface1.getparam("loss") != loss assert iface1.getparam("duplicate") != dup assert iface1.getparam("jitter") != jitter assert iface1.getparam("buffer") != buffer assert iface2.getparam("delay") != delay assert iface2.getparam("bw") != bandwidth assert iface2.getparam("loss") != loss assert iface2.getparam("duplicate") != dup assert iface2.getparam("jitter") != jitter assert iface2.getparam("buffer") != buffer # when options = LinkOptions( delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter, buffer=buffer, ) session.update_link(node1.id, node2.id, iface1_data.id, iface2_data.id, options) # then assert iface1.getparam("delay") == delay assert iface1.getparam("bw") == bandwidth assert iface1.getparam("loss") == loss assert iface1.getparam("duplicate") == dup assert iface1.getparam("jitter") == jitter assert iface1.getparam("buffer") == buffer assert iface2.getparam("delay") == delay assert iface2.getparam("bw") == bandwidth assert iface2.getparam("loss") == loss assert iface2.getparam("duplicate") == dup assert iface2.getparam("jitter") == jitter assert iface2.getparam("buffer") == buffer
def setlinkparams(self) -> None: """ Apply link parameters to all interfaces. This is invoked from WlanNode.setmodel() after the position callback has been set. """ with self.iface_lock: for iface in self.iface_to_pos: options = LinkOptions( bandwidth=self.bw, delay=self.delay, loss=self.loss, jitter=self.jitter, ) iface.config(options)
def test_clear_net_to_net(self, session: Session, ip_prefixes: IpPrefixes): # given node1 = session.add_node(SwitchNode) node2 = session.add_node(SwitchNode) iface1, _ = session.add_link(node1.id, node2.id, options=LINK_OPTIONS) assert iface1.local_options == LINK_OPTIONS assert iface1.has_local_netem assert iface1.options == LINK_OPTIONS assert iface1.has_netem # when options = LinkOptions(delay=0, bandwidth=0, loss=0.0, dup=0, jitter=0, buffer=0) session.update_link(node1.id, node2.id, options=options) # then assert iface1.local_options.is_clear() assert not iface1.has_local_netem assert iface1.options.is_clear() assert not iface1.has_netem
def read_links(self) -> None: link_elements = self.scenario.find("links") if link_elements is None: return node_sets = set() for link_element in link_elements.iterchildren(): node1_id = get_int(link_element, "node1") if node1_id is None: node1_id = get_int(link_element, "node_one") node2_id = get_int(link_element, "node2") if node2_id is None: node2_id = get_int(link_element, "node_two") node_set = frozenset((node1_id, node2_id)) iface1_element = link_element.find("iface1") if iface1_element is None: iface1_element = link_element.find("interface_one") iface1_data = None if iface1_element is not None: iface1_data = create_iface_data(iface1_element) iface2_element = link_element.find("iface2") if iface2_element is None: iface2_element = link_element.find("interface_two") iface2_data = None if iface2_element is not None: iface2_data = create_iface_data(iface2_element) options_element = link_element.find("options") options = LinkOptions() if options_element is not None: options.bandwidth = get_int(options_element, "bandwidth") options.burst = get_int(options_element, "burst") options.delay = get_int(options_element, "delay") options.dup = get_int(options_element, "dup") options.mer = get_int(options_element, "mer") options.mburst = get_int(options_element, "mburst") options.jitter = get_int(options_element, "jitter") options.key = get_int(options_element, "key") options.loss = get_float(options_element, "loss") if options.loss is None: options.loss = get_float(options_element, "per") options.unidirectional = get_int(options_element, "unidirectional") options.buffer = get_int(options_element, "buffer") if options.unidirectional == 1 and node_set in node_sets: logging.info("updating link node1(%s) node2(%s)", node1_id, node2_id) self.session.update_link(node1_id, node2_id, iface1_data.id, iface2_data.id, options) else: logging.info("adding link node1(%s) node2(%s)", node1_id, node2_id) self.session.add_link(node1_id, node2_id, iface1_data, iface2_data, options) node_sets.add(node_set)
"range": "280", "bandwidth": "55000000", "delay": "100", "error": 0, "jitter": 0 }, ) # link nodes to wlan iface1 = ip_prefixes.create_iface(n1) session.add_link(n1.id, wlan.id, iface1) iface2 = ip_prefixes.create_iface(n2) session.add_link(n2.id, wlan.id, iface2) options = LinkOptions(delay=100, bandwidth=50_000_000, dup=5, loss=5.5, jitter=0) #iface1 = ip_prefixes.create_iface(n1) #iface2 = ip_prefixes.create_iface(n2) #print("Iface1", iface1.ip4) #print("Iface2", iface2.ip4) #session.add_link(n1.id, n2.id, iface1, iface2) def writeConfig(n): # n.cmd(shell=True, args="mkdir -p /etc/quagga/")
def test_link_options_bidirectional(self, session: Session, tmpdir: TemporaryFile, ip_prefixes: IpPrefixes): """ Test xml client methods for a ptp network. :param session: session for test :param tmpdir: tmpdir to create data in :param ip_prefixes: generates ip addresses for nodes """ # create nodes node1 = session.add_node(CoreNode) iface1_data = ip_prefixes.create_iface(node1) node2 = session.add_node(CoreNode) iface2_data = ip_prefixes.create_iface(node2) # create link options1 = LinkOptions() options1.unidirectional = 1 options1.bandwidth = 5000 options1.delay = 10 options1.loss = 10.5 options1.dup = 5 options1.jitter = 5 options1.buffer = 50 session.add_link(node1.id, node2.id, iface1_data, iface2_data, options1) options2 = LinkOptions() options2.unidirectional = 1 options2.bandwidth = 10000 options2.delay = 20 options2.loss = 10 options2.dup = 10 options2.jitter = 10 options2.buffer = 100 session.update_link(node2.id, node1.id, iface2_data.id, iface1_data.id, options2) # instantiate session session.instantiate() # get ids for nodes node1_id = node1.id node2_id = node2.id # save xml xml_file = tmpdir.join("session.xml") file_path = Path(xml_file.strpath) session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile() assert ElementTree.parse(file_path) # stop current session, clearing data session.shutdown() # verify nodes have been removed from session with pytest.raises(CoreError): assert not session.get_node(node1_id, CoreNode) with pytest.raises(CoreError): assert not session.get_node(node2_id, CoreNode) # load saved xml session.open_xml(file_path, start=True) # verify nodes have been recreated assert session.get_node(node1_id, CoreNode) assert session.get_node(node2_id, CoreNode) links = [] for node_id in session.nodes: node = session.nodes[node_id] links += node.links() assert len(links) == 2 link1 = links[0] link2 = links[1] assert options1.bandwidth == link1.options.bandwidth assert options1.delay == link1.options.delay assert options1.loss == link1.options.loss assert options1.dup == link1.options.dup assert options1.jitter == link1.options.jitter assert options1.buffer == link1.options.buffer assert options2.bandwidth == link2.options.bandwidth assert options2.delay == link2.options.delay assert options2.loss == link2.options.loss assert options2.dup == link2.options.dup assert options2.jitter == link2.options.jitter assert options2.buffer == link2.options.buffer
def add_link_data( link_proto: core_pb2.Link ) -> Tuple[InterfaceData, InterfaceData, LinkOptions, LinkTypes]: """ Convert link proto to link interfaces and options data. :param link_proto: link proto :return: link interfaces and options """ iface1_data = link_iface(link_proto.iface1) iface2_data = link_iface(link_proto.iface2) link_type = LinkTypes(link_proto.type) options = LinkOptions() options_proto = link_proto.options if options_proto: options.delay = options_proto.delay options.bandwidth = options_proto.bandwidth options.loss = options_proto.loss options.dup = options_proto.dup options.jitter = options_proto.jitter options.mer = options_proto.mer options.burst = options_proto.burst options.mburst = options_proto.mburst options.unidirectional = options_proto.unidirectional options.key = options_proto.key return iface1_data, iface2_data, options, link_type
def main(): parser = argparse.ArgumentParser() parser.add_argument('--is_virtual', dest='is_virtual', help='with Kronos', default="False") args = parser.parse_args() if args.is_virtual == "True": is_virtual = True else: is_virtual = False # Kronos specific parameters total_num_dilated_executables = 6 # (2 plcs + 2 communication modules + 2 hmis ) run_time_secs = 5 rel_cpu_speed = 1.0 num_insns_per_round = 1000000 plc_spec_directory = os.path.dirname(os.path.realpath(__file__)) plc1_spec_file = f"{plc_spec_directory}/plc1_system_specification.prototxt" plc2_spec_file = f"{plc_spec_directory}/plc2_system_specification.prototxt" NUM_PLCS = 2 NUM_HMIS = 2 # ip generator for example prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") # create emulator instance for creating sessions and utility methods coreemu = CoreEmu() session = coreemu.create_session() # must be in configuration state for nodes to start, when using "node_add" below session.set_state(EventTypes.CONFIGURATION_STATE) # create switch network node switch = session.add_node(SwitchNode, _id=100) # create nodes for _ in range(NUM_PLCS + NUM_HMIS): node = session.add_node(CoreNode) interface = prefixes.create_iface(node) session.add_link(node.id, switch.id, iface1_data=interface, options=LinkOptions(delay=1000)) # delay in us # instantiate session session.instantiate() node_ifaces = [] # get nodes to run example plc1_node = session.get_node(1, CoreNode) node_ifaces.extend( [x.localname for x in plc1_node.get_ifaces(control=False)]) plc2_node = session.get_node(2, CoreNode) node_ifaces.extend( [x.localname for x in plc2_node.get_ifaces(control=False)]) hmi1_node = session.get_node(3, CoreNode) node_ifaces.extend( [x.localname for x in hmi1_node.get_ifaces(control=False)]) hmi2_node = session.get_node(4, CoreNode) node_ifaces.extend( [x.localname for x in hmi2_node.get_ifaces(control=False)]) print("node-ifaces ", node_ifaces) plc1_ip_address = prefixes.ip4_address(plc1_node.id) plc2_ip_address = prefixes.ip4_address(plc2_node.id) print(f"PLC-1 IP: {plc1_ip_address}, PLC-2 IP: {plc2_ip_address}") # Clear any existing log files if os.path.exists("/tmp/pc_grpc_server_log.txt"): os.remove("/tmp/pc_grpc_server_log.txt") if os.path.exists("/tmp/plc1_log.txt"): os.remove("/tmp/plc1_log.txt") if os.path.exists("/tmp/plc2_log.txt"): os.remove("/tmp/plc2_log.txt") if os.path.exists("/tmp/comm_module1_log.txt"): os.remove("/tmp/comm_module1_log.txt") if os.path.exists("/tmp/comm_module2_log.txt"): os.remove("/tmp/comm_module2_log.txt") if os.path.exists("/tmp/hmi1.txt"): os.remove("/tmp/hmi1.txt") if os.path.exists("/tmp/hmi2.txt"): os.remove("/tmp/hmi2.txt") fd1 = os.open("/tmp/pc_grpc_server_log.txt", os.O_RDWR | os.O_CREAT) pendulum_sim = PendulumSystemSimulator() if args.is_virtual == "True": is_virtual = True else: is_virtual = False # Create an emulation driver. Register pendulum system simulator with it. emulation = EmulationDriver( number_dilated_nodes=total_num_dilated_executables, is_virtual=is_virtual, n_insns_per_round=num_insns_per_round, rel_cpu_speed=rel_cpu_speed, physical_system_sim_driver=pendulum_sim) # Start pc_grpc_server, all PLCs and all communication modules here emulation.start_grpc_server(plc_spec_directory, fd1) if is_virtual: emulation.add_interfaces_to_vt_control(node_ifaces) # Retrieve command strings to run PLCs/HMIs/Communication Modules plc1_cmd = emulation.get_plc_exec_command( path_to_plc_specification_file=plc1_spec_file, log_file_path="/tmp/plc1_log.txt") plc2_cmd = emulation.get_plc_exec_command( path_to_plc_specification_file=plc2_spec_file, log_file_path="/tmp/plc2_log.txt") comm_module1_cmd = emulation.wrap_command( get_comm_module_start_command(plc1_spec_file, plc1_ip_address, "/tmp/comm_module1_log.txt")) comm_module2_cmd = emulation.wrap_command( get_comm_module_start_command(plc2_spec_file, plc2_ip_address, "/tmp/comm_module2_log.txt")) hmi1_cmd = emulation.wrap_command( get_example_hmi_start_command(plc1_ip_address, "/tmp/hmi1.txt")) hmi2_cmd = emulation.wrap_command( get_example_hmi_start_command(plc2_ip_address, "/tmp/hmi2.txt")) print("Starting PLCs ...") plc1_node.cmd(plc1_cmd, wait=False) plc2_node.cmd(plc2_cmd, wait=False) print("Starting PLC Modbus Comm modules ...") plc1_node.cmd(comm_module1_cmd, wait=False) plc2_node.cmd(comm_module2_cmd, wait=False) print("Starting HMI ...") hmi1_node.cmd(hmi1_cmd, wait=False) hmi2_node.cmd(hmi2_cmd, wait=False) # Wait until all processes have started and registered themselves emulation.wait_for_initialization() # Register an interrupt signal handler. signal.signal(signal.SIGINT, handler) total_time_elapsed = 0.0 while total_time_elapsed <= run_time_secs: emulation.run_for(0.01) total_time_elapsed += 0.01 if is_virtual: print("Time Elapsed: ", total_time_elapsed) if stop == True: break print("Stopping Emulation ...") sys.stdout.flush() emulation.stop_exp() os.close(fd1) # shutdown session coreemu.shutdown() print("Emulation finished ! ")
from typing import Tuple import pytest from core.emulator.data import IpPrefixes, LinkOptions from core.emulator.session import Session from core.errors import CoreError from core.nodes.base import CoreNode from core.nodes.network import SwitchNode INVALID_ID: int = 100 LINK_OPTIONS: LinkOptions = LinkOptions(delay=50, bandwidth=5000000, loss=25, dup=25, jitter=10, buffer=100) def create_ptp_network(session: Session, ip_prefixes: IpPrefixes) -> Tuple[CoreNode, CoreNode]: # create nodes node1 = session.add_node(CoreNode) node2 = session.add_node(CoreNode) # link nodes to net node iface1_data = ip_prefixes.create_iface(node1) iface2_data = ip_prefixes.create_iface(node2) session.add_link(node1.id, node2.id, iface1_data, iface2_data) # instantiate session
def test_link_options(self, session: Session, tmpdir: TemporaryFile, ip_prefixes: IpPrefixes): """ Test xml client methods for a ptp network. :param session: session for test :param tmpdir: tmpdir to create data in :param ip_prefixes: generates ip addresses for nodes """ # create nodes node1 = session.add_node(CoreNode) iface1_data = ip_prefixes.create_iface(node1) switch = session.add_node(SwitchNode) # create link options = LinkOptions() options.loss = 10.5 options.bandwidth = 50000 options.jitter = 10 options.delay = 30 options.dup = 5 session.add_link(node1.id, switch.id, iface1_data, options=options) # instantiate session session.instantiate() # get ids for nodes node1_id = node1.id node2_id = switch.id # save xml xml_file = tmpdir.join("session.xml") file_path = xml_file.strpath session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile() assert ElementTree.parse(file_path) # stop current session, clearing data session.shutdown() # verify nodes have been removed from session with pytest.raises(CoreError): assert not session.get_node(node1_id, CoreNode) with pytest.raises(CoreError): assert not session.get_node(node2_id, SwitchNode) # load saved xml session.open_xml(file_path, start=True) # verify nodes have been recreated assert session.get_node(node1_id, CoreNode) assert session.get_node(node2_id, SwitchNode) links = [] for node_id in session.nodes: node = session.nodes[node_id] links += node.links() link = links[0] assert options.loss == link.options.loss assert options.bandwidth == link.options.bandwidth assert options.jitter == link.options.jitter assert options.delay == link.options.delay assert options.dup == link.options.dup