def handlenodemsg(self, msg): """ Process a Node Message to add/delete or move a node on the SDT display. Node properties are found in session._objs or self.remotes for remote nodes (or those not yet instantiated). :param msg: node message to handle :return: nothing """ # for distributed sessions to work properly, the SDT option should be # enabled prior to starting the session if not self.is_enabled(): return False # node.(objid, type, icon, name) are used. nodenum = msg.get_tlv(NodeTlvs.NUMBER.value) if not nodenum: return x = msg.get_tlv(NodeTlvs.X_POSITION.value) y = msg.get_tlv(NodeTlvs.Y_POSITION.value) z = None name = msg.get_tlv(NodeTlvs.NAME.value) nodetype = msg.get_tlv(NodeTlvs.TYPE.value) model = msg.get_tlv(NodeTlvs.MODEL.value) icon = msg.get_tlv(NodeTlvs.ICON.value) net = False if nodetype == NodeTypes.DEFAULT.value or \ nodetype == NodeTypes.PHYSICAL.value or \ nodetype == NodeTypes.XEN.value: if model is None: model = "router" type = model elif nodetype is not None: type = nodeutils.get_node_class(NodeTypes(nodetype)).type net = True else: type = None try: node = self.session.get_object(nodenum) except KeyError: node = None if node: self.updatenode(node.objid, msg.flags, x, y, z, node.name, node.type, node.icon) else: if nodenum in self.remotes: remote = self.remotes[nodenum] if name is None: name = remote.name if type is None: type = remote.type if icon is None: icon = remote.icon else: remote = Bunch(objid=nodenum, type=type, icon=icon, name=name, net=net, links=set()) self.remotes[nodenum] = remote remote.pos = (x, y, z) self.updatenode(nodenum, msg.flags, x, y, z, name, type, icon)
def handlenodemsg(self, msg): """ Process a Node Message to add/delete or move a node on the SDT display. Node properties are found in session._objs or self.remotes for remote nodes (or those not yet instantiated). :param msg: node message to handle :return: nothing """ # for distributed sessions to work properly, the SDT option should be # enabled prior to starting the session if not self.is_enabled(): return False # node.(objid, type, icon, name) are used. nodenum = msg.get_tlv(NodeTlvs.NUMBER.value) if not nodenum: return x = msg.get_tlv(NodeTlvs.X_POSITION.value) y = msg.get_tlv(NodeTlvs.Y_POSITION.value) z = None name = msg.get_tlv(NodeTlvs.NAME.value) nodetype = msg.get_tlv(NodeTlvs.TYPE.value) model = msg.get_tlv(NodeTlvs.MODEL.value) icon = msg.get_tlv(NodeTlvs.ICON.value) net = False if nodetype == NodeTypes.DEFAULT.value or \ nodetype == NodeTypes.PHYSICAL.value: if model is None: model = "router" type = model elif nodetype is not None: type = nodeutils.get_node_class(NodeTypes(nodetype)).type net = True else: type = None try: node = self.session.get_object(nodenum) except KeyError: node = None if node: self.updatenode(node.objid, msg.flags, x, y, z, node.name, node.type, node.icon) else: if nodenum in self.remotes: remote = self.remotes[nodenum] if name is None: name = remote.name if type is None: type = remote.type if icon is None: icon = remote.icon else: remote = Bunch(objid=nodenum, type=type, icon=icon, name=name, net=net, links=set()) self.remotes[nodenum] = remote remote.pos = (x, y, z) self.updatenode(nodenum, msg.flags, x, y, z, name, type, icon)
def parse_layer2_device(self, device): objid, device_name = self.get_common_attributes(device) logger.info('parsing layer-2 device: name=%s id=%s' % (device_name, objid)) try: return self.session.get_object(objid) except KeyError: logger.exception("error geting object: %s", objid) device_type = self.device_type(device) if device_type == 'hub': device_class = nodeutils.get_node_class(NodeTypes.HUB) elif device_type == 'switch': device_class = nodeutils.get_node_class(NodeTypes.SWITCH) else: logger.warn('unknown layer-2 device type: \'%s\'' % device_type) assert False # XXX for testing n = self.create_core_object(device_class, objid, device_name, device, None) return n
def network_class(self, network, network_type): """ Return the corresponding CORE network class for the given network/network_type. """ if network_type in ['ethernet', 'satcom']: return nodeutils.get_node_class(NodeTypes.PEER_TO_PEER) elif network_type == 'wireless': channel = xmlutils.get_first_child_by_tag_name(network, 'channel') if channel: # use an explicit CORE type if it exists coretype = xmlutils.get_first_child_text_trim_with_attribute(channel, 'type', 'domain', 'CORE') if coretype: if coretype == 'basic_range': return nodeutils.get_node_class(NodeTypes.WIRELESS_LAN) elif coretype.startswith('emane'): return nodeutils.get_node_class(NodeTypes.EMANE) else: logger.warn('unknown network type: \'%s\'', coretype) return xmlutils.xml_type_to_node_class(coretype) return nodeutils.get_node_class(NodeTypes.WIRELESS_LAN) logger.warn('unknown network type: \'%s\'', network_type) return None
def open_session_xml(session, filename, start=False, nodecls=None): """ Import a session from the EmulationScript XML format. """ # set default node class when one is not provided if not nodecls: nodecls = nodeutils.get_node_class(NodeTypes.DEFAULT) options = {'start': start, 'nodecls': nodecls} doc = core_document_parser(session, filename, options) if start: session.name = os.path.basename(filename) session.filename = filename session.instantiate()
def parsenodes(self): for node in self.np.getElementsByTagName("Node"): id, name, type = self.getcommonattributes(node) if type == "rj45": nodecls = nodeutils.get_node_class(NodeTypes.RJ45) else: nodecls = self.nodecls n = self.session.add_object(cls=nodecls, objid=id, name=name, start=self.start) if name in self.coords: x, y, z = self.coords[name] n.setposition(x, y, z) n.type = type xmlutils.get_params_set_attrs(node, ("icon", "canvas", "opaque"), n) if hasattr(n, "canvas") and n.canvas is not None: n.canvas = int(n.canvas) for ifc in node.getElementsByTagName("interface"): self.parseinterface(n, ifc)
def open_xml(self, file_name, start=False): """ Import a session from the EmulationScript XML format. :param str file_name: xml file to load session from :param bool start: instantiate session if true, false otherwise :return: nothing """ # clear out existing session self.clear() # set default node class when one is not provided node_class = nodeutils.get_node_class(NodeTypes.DEFAULT) options = {"start": start, "nodecls": node_class} core_document_parser(self, file_name, options) if start: self.name = os.path.basename(file_name) self.file_name = file_name self.instantiate()
def add_node(self, _type=NodeTypes.DEFAULT, _id=None, node_options=NodeOptions()): """ Add a node to the session, based on the provided node data. :param core.enumerations.NodeTypes _type: type of node to create :param int _id: id for node, defaults to None for generated id :param core.emulator.emudata.NodeOptions node_options: data to create node with :return: created node """ # retrieve node class for given node type try: node_class = nodeutils.get_node_class(_type) except KeyError: logger.error("invalid node type to create: %s", _type) return None # set node start based on current session state, override and check when rj45 start = self.state > EventTypes.DEFINITION_STATE.value enable_rj45 = self.options.get_config("enablerj45") == "1" if _type == NodeTypes.RJ45 and not enable_rj45: start = False # determine node id if not _id: while True: _id = self.node_id_gen.next() if _id not in self.objects: break # generate name if not provided name = node_options.name if not name: name = "%s%s" % (node_class.__name__, _id) # create node logger.info("creating node(%s) id(%s) name(%s) start(%s)", node_class.__name__, _id, name, start) node = self.add_object(cls=node_class, objid=_id, name=name, start=start) # set node attributes node.icon = node_options.icon node.canvas = node_options.canvas node.opaque = node_options.opaque # set node position and broadcast it self.set_node_position(node, node_options) # add services to default and physical nodes only if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]: node.type = node_options.model logger.debug("set node type: %s", node.type) self.services.add_services(node, node.type, node_options.services) # boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes is_boot_node = isinstance( node, PyCoreNode) and not nodeutils.is_node(node, NodeTypes.RJ45) if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node: self.write_objects() self.add_remove_control_interface(node=node, remove=False) self.services.boot_services(node) return node
def add_link(self, node_one_id, node_two_id, interface_one=None, interface_two=None, link_options=LinkOptions()): """ Add a link between nodes. :param int node_one_id: node one id :param int node_two_id: node two id :param core.emulator.emudata.InterfaceData interface_one: node one interface data, defaults to none :param core.emulator.emudata.InterfaceData interface_two: node two interface data, defaults to none :param core.emulator.emudata.LinkOptions link_options: data for creating link, defaults to no options :return: """ # get node objects identified by link data node_one, node_two, net_one, net_two, tunnel = self._link_nodes( node_one_id, node_two_id) if node_one: node_one.lock.acquire() if node_two: node_two.lock.acquire() try: # wireless link if link_options.type == LinkTypes.WIRELESS: objects = [node_one, node_two, net_one, net_two] self._link_wireless(objects, connect=True) # wired link else: # 2 nodes being linked, ptp network if all([node_one, node_two]) and not net_one: logger.info("adding link for peer to peer nodes: %s - %s", node_one.name, node_two.name) ptp_class = nodeutils.get_node_class( NodeTypes.PEER_TO_PEER) start = self.state > EventTypes.DEFINITION_STATE.value net_one = self.add_object(cls=ptp_class, start=start) # node to network if node_one and net_one: logger.info("adding link from node to network: %s - %s", node_one.name, net_one.name) interface = create_interface(node_one, net_one, interface_one) link_config(net_one, interface, link_options) # network to node if node_two and net_one: logger.info("adding link from network to node: %s - %s", node_two.name, net_one.name) interface = create_interface(node_two, net_one, interface_two) if not link_options.unidirectional: link_config(net_one, interface, link_options) # network to network if net_one and net_two: logger.info("adding link from network to network: %s", net_one.name, net_two.name) if nodeutils.is_node(net_two, NodeTypes.RJ45): interface = net_two.linknet(net_one) else: interface = net_one.linknet(net_two) link_config(net_one, interface, link_options) if not link_options.unidirectional: interface.swapparams("_params_up") link_config(net_two, interface, link_options, devname=interface.name) interface.swapparams("_params_up") # a tunnel node was found for the nodes addresses = [] if not node_one and all([net_one, interface_one]): addresses.extend(interface_one.get_addresses()) if not node_two and all([net_two, interface_two]): addresses.extend(interface_two.get_addresses()) # tunnel node logic key = link_options.key if key and nodeutils.is_node(net_one, NodeTypes.TUNNEL): logger.info("setting tunnel key for: %s", net_one.name) net_one.setkey(key) if addresses: net_one.addrconfig(addresses) if key and nodeutils.is_node(net_two, NodeTypes.TUNNEL): logger.info("setting tunnel key for: %s", net_two.name) net_two.setkey(key) if addresses: net_two.addrconfig(addresses) # physical node connected with tunnel if not net_one and not net_two and (node_one or node_two): if node_one and nodeutils.is_node(node_one, NodeTypes.PHYSICAL): logger.info("adding link for physical node: %s", node_one.name) addresses = interface_one.get_addresses() node_one.adoptnetif(tunnel, interface_one.id, interface_one.mac, addresses) link_config(node_one, tunnel, link_options) elif node_two and nodeutils.is_node( node_two, NodeTypes.PHYSICAL): logger.info("adding link for physical node: %s", node_two.name) addresses = interface_two.get_addresses() node_two.adoptnetif(tunnel, interface_two.id, interface_two.mac, addresses) link_config(node_two, tunnel, link_options) finally: if node_one: node_one.lock.release() if node_two: node_two.lock.release()
def handlenodemsg(self, message): """ Determine and return the servers to which this node message should be forwarded. Also keep track of link-layer nodes and the mapping of nodes to servers. :param core.api.coreapi.CoreMessage message: message to handle :return: boolean for handling locally and set of servers :rtype: tuple """ servers = set() handle_locally = False serverfiletxt = None # snoop Node Message for emulation server TLV and record mapping n = message.tlv_data[NodeTlvs.NUMBER.value] # replicate link-layer nodes on all servers nodetype = message.get_tlv(NodeTlvs.TYPE.value) if nodetype is not None: try: nodecls = nodeutils.get_node_class(NodeTypes(nodetype)) except KeyError: logger.warn("broker invalid node type %s", nodetype) return handle_locally, servers if nodecls is None: logger.warn("broker unimplemented node type %s", nodetype) return handle_locally, servers if issubclass(nodecls, PyCoreNet) and nodetype != NodeTypes.WIRELESS_LAN.value: # network node replicated on all servers; could be optimized # don"t replicate WLANs, because ebtables rules won"t work servers = self.getservers() handle_locally = True self.addnet(n) for server in servers: self.addnodemap(server, n) # do not record server name for networks since network # nodes are replicated across all server return handle_locally, servers elif issubclass(nodecls, PyCoreNode): name = message.get_tlv(NodeTlvs.NAME.value) if name: serverfiletxt = "%s %s %s" % (n, name, nodecls) if issubclass(nodecls, PhysicalNode): # remember physical nodes self.addphys(n) # emulation server TLV specifies server servername = message.get_tlv(NodeTlvs.EMULATION_SERVER.value) server = self.getserverbyname(servername) if server is not None: self.addnodemap(server, n) if server not in servers: servers.add(server) if serverfiletxt and self.session.master: self.writenodeserver(serverfiletxt, server) # hook to update coordinates of physical nodes if n in self.physical_nodes: self.session.mobility.physnodeupdateposition(message) return handle_locally, servers
def add_remove_control_net(self, net_index, remove=False, conf_required=True): """ Create a control network bridge as necessary. When the remove flag is True, remove the bridge that connects control interfaces. The conf_reqd flag, when False, causes a control network bridge to be added even if one has not been configured. :param int net_index: network index :param bool remove: flag to check if it should be removed :param bool conf_required: flag to check if conf is required :return: control net object :rtype: core.netns.nodes.CtrlNet """ logger.debug( "add/remove control net: index(%s) remove(%s) conf_required(%s)", net_index, remove, conf_required) prefix_spec_list = self.get_control_net_prefixes() prefix_spec = prefix_spec_list[net_index] if not prefix_spec: if conf_required: # no controlnet needed return None else: control_net_class = nodeutils.get_node_class( NodeTypes.CONTROL_NET) prefix_spec = control_net_class.DEFAULT_PREFIX_LIST[net_index] logger.debug("prefix spec: %s", prefix_spec) server_interface = self.get_control_net_server_interfaces()[net_index] # return any existing controlnet bridge try: control_net = self.get_control_net_object(net_index) if remove: self.delete_object(control_net.objid) return None return control_net except KeyError: if remove: return None # build a new controlnet bridge object_id = "ctrl%dnet" % net_index # use the updown script for control net 0 only. updown_script = None if net_index == 0: updown_script = self.options.get_config("controlnet_updown_script") if not updown_script: logger.warning("controlnet updown script not configured") prefixes = prefix_spec.split() if len(prefixes) > 1: # a list of per-host prefixes is provided assign_address = True if self.master: try: # split first (master) entry into server and prefix prefix = prefixes[0].split(":", 1)[1] except IndexError: # no server name. possibly only one server prefix = prefixes[0] else: # slave servers have their name and localhost in the serverlist servers = self.broker.getservernames() servers.remove("localhost") prefix = None for server_prefix in prefixes: try: # split each entry into server and prefix server, p = server_prefix.split(":") except ValueError: server = "" p = None if server == servers[0]: # the server name in the list matches this server prefix = p break if not prefix: logger.error( "Control network prefix not found for server '%s'" % servers[0]) assign_address = False try: prefix = prefixes[0].split(':', 1)[1] except IndexError: prefix = prefixes[0] # len(prefixes) == 1 else: # TODO: can we get the server name from the servers.conf or from the node assignments? # with one prefix, only master gets a ctrlnet address assign_address = self.master prefix = prefixes[0] control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET) control_net = self.add_object(cls=control_net_class, objid=object_id, prefix=prefix, assign_address=assign_address, updown_script=updown_script, serverintf=server_interface) # tunnels between controlnets will be built with Broker.addnettunnels() # TODO: potentially remove documentation saying object ids are ints # TODO: need to move broker code out of the session object self.broker.addnet(object_id) for server in self.broker.getservers(): self.broker.addnodemap(server, object_id) return control_net
def add_remove_control_net(self, net_index, remove=False, conf_required=True): """ Create a control network bridge as necessary. When the remove flag is True, remove the bridge that connects control interfaces. The conf_reqd flag, when False, causes a control network bridge to be added even if one has not been configured. :param int net_index: network index :param bool remove: flag to check if it should be removed :param bool conf_required: flag to check if conf is required :return: control net object :rtype: core.netns.nodes.CtrlNet """ logger.debug("add/remove control net: index(%s) remove(%s) conf_required(%s)", net_index, remove, conf_required) prefix_spec_list = self.get_control_net_prefixes() prefix_spec = prefix_spec_list[net_index] if not prefix_spec: if conf_required: # no controlnet needed return None else: control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET) prefix_spec = control_net_class.DEFAULT_PREFIX_LIST[net_index] logger.debug("prefix spec: %s", prefix_spec) server_interface = self.get_control_net_server_interfaces()[net_index] # return any existing controlnet bridge try: control_net = self.get_control_net_object(net_index) if remove: self.delete_object(control_net.objid) return None return control_net except KeyError: if remove: return None # build a new controlnet bridge object_id = "ctrl%dnet" % net_index # use the updown script for control net 0 only. updown_script = None if net_index == 0: updown_script = self.options.get_config("controlnet_updown_script") if not updown_script: logger.warning("controlnet updown script not configured") prefixes = prefix_spec.split() if len(prefixes) > 1: # a list of per-host prefixes is provided assign_address = True if self.master: try: # split first (master) entry into server and prefix prefix = prefixes[0].split(":", 1)[1] except IndexError: # no server name. possibly only one server prefix = prefixes[0] else: # slave servers have their name and localhost in the serverlist servers = self.broker.getservernames() servers.remove("localhost") prefix = None for server_prefix in prefixes: try: # split each entry into server and prefix server, p = server_prefix.split(":") except ValueError: server = "" p = None if server == servers[0]: # the server name in the list matches this server prefix = p break if not prefix: logger.error("Control network prefix not found for server '%s'" % servers[0]) assign_address = False try: prefix = prefixes[0].split(':', 1)[1] except IndexError: prefix = prefixes[0] # len(prefixes) == 1 else: # TODO: can we get the server name from the servers.conf or from the node assignments? # with one prefix, only master gets a ctrlnet address assign_address = self.master prefix = prefixes[0] control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET) control_net = self.add_object(cls=control_net_class, objid=object_id, prefix=prefix, assign_address=assign_address, updown_script=updown_script, serverintf=server_interface) # tunnels between controlnets will be built with Broker.addnettunnels() # TODO: potentially remove documentation saying object ids are ints # TODO: need to move broker code out of the session object self.broker.addnet(object_id) for server in self.broker.getservers(): self.broker.addnodemap(server, object_id) return control_net