def __init__ (self, network=None, topo_desc=None): """ Initialize Mininet implementation with proper attributes. Use network as the hided Mininet topology if it's given. :param topo_desc: static topology description e.g. the related NFFG :type topo_desc: :any:`NFFG` :param network: use this specific Mininet object for init (default: None) :type network: :class:`mininet.net.MininetWithControlNet` :return: None """ log.debug("Init ESCAPENetworkBridge with topo description: %s" % topo_desc) if network is not None: self.__mininet = network else: log.warning( "Network implementation object is missing! Use Builder class instead " "of direct initialization. Creating bare Mininet object anyway...") self.__mininet = MininetWithControlNet() # Topology description which is emulated by the Mininet self.topo_desc = topo_desc # Duplicate static links for ensure undirected neighbour relationship if self.topo_desc is not None: back_links = [l.id for u, v, l in self.topo_desc.network.edges_iter(data=True) if l.backward is True] if len(back_links) == 0: log.debug("No backward link has been detected! Duplicate STATIC links " "to ensure undirected relationship for mapping...") self.topo_desc.duplicate_static_links() # Need to clean after shutdown self._need_clean = None # There is no such flag in the Mininet class so using this self.started = False self.xterms = []
def __init_from_file (self, path, format=DEFAULT_NFFG_FORMAT): """ Build a pre-defined topology from an NFFG stored in a file. The file path is searched in CONFIG with tha name ``TOPO``. :param path: file path :type path: str :param format: NF-FG storing format (default: internal NFFG representation) :type format: str :return: None """ if path is None: log.error("Missing file path of Topology description") return try: with open(path, 'r') as f: log.info("Load topology from file: %s" % path) if format == self.DEFAULT_NFFG_FORMAT: log.info("Using file format: %s" % format) self.__init_from_NFFG(nffg=NFFG.parse(f.read())) else: raise TopologyBuilderException("Unsupported file format: %s!" % format) except IOError: log.warning("Additional topology file not found: %s" % path) raise TopologyBuilderException("Missing topology file!") except ValueError as e: log.error("An error occurred when load topology from file: %s" % e.message) raise TopologyBuilderException("File parsing error!")
def cleanup (self): """ Clean up junk which might be left over from old runs. ..seealso:: :func:`mininet.clean.cleanup() <mininet.clean.cleanup>` """ if self.started: log.warning( "Mininet network is not stopped yet! Skipping cleanup task...") else: log.info("Schedule cleanup task after Mininet emulation...") # Kill remained xterms log.debug("Close SAP xterms...") import os import signal for term in self.xterms: os.killpg(term.pid, signal.SIGTERM) # Schedule a cleanup as a coop task to avoid threading issues from escape.util.misc import remove_junks # call_as_coop_task(remove_junks, log=log) # threading.Thread(target=remove_junks, name="cleanup", args=(log, # )).start() # multiprocessing.Process(target=remove_junks, name="cleanup", # args=(log,)).start() remove_junks(log=log)
def build (self, topo=None): """ Initialize network. 1. If the additional ``topology`` is given then using that for init. 2. If TOPO is not given, search topology description in CONFIG with the \ name 'TOPO'. 3. If TOPO not found or an Exception was raised, search for the fallback \ topo with the name ``FALLBACK-TOPO``. 4. If FALLBACK-TOPO not found raise an exception or run a bare Mininet \ object if the run_dry attribute is set :param topo: optional topology representation :type topo: :any:`NFFG` or :any:`AbstractTopology` or ``None`` :return: object representing the emulated network :rtype: :any:`ESCAPENetworkBridge` """ log.debug("Init emulated topology based on Mininet v%s" % MNVERSION) # Load topology try: if topo is None: log.info("Get Topology description from CONFIG...") self.__init_from_CONFIG() elif isinstance(topo, NFFG): log.info("Get Topology description from given NFFG...") self.__init_from_NFFG(nffg=topo) elif isinstance(topo, basestring) and topo.startswith('/'): log.info("Get Topology description from given file...") self.__init_from_file(path=topo) elif isinstance(topo, AbstractTopology): log.info("Get Topology description based on Topology class...") self.__init_from_AbstractTopology(topo_class=topo) else: raise TopologyBuilderException( "Unsupported topology format: %s - %s" % (type(topo), topo)) return self.get_network() except SystemExit as e: quit_with_error(msg="Mininet exited unexpectedly!", logger=log, exception=e) except TopologyBuilderException: if self.fallback: # Search for fallback topology fallback = CONFIG.get_fallback_topology() if fallback: log.info("Load topo from fallback topology description...") self.__init_from_AbstractTopology(fallback) return self.get_network() # fallback topo is not found or set if self.run_dry: # Return with the bare Mininet object log.warning("Topology description is not found! Running dry...") return self.get_network() else: # Re-raise the exception raise except KeyboardInterrupt: quit_with_error( msg="Assembly of Mininet network was interrupted by user!", logger=log)
def start_network(self): """ Start network. :return: None """ log.debug("Starting Mininet network...") if self.__mininet is not None: if not self.started: try: self.__mininet.start() except SystemExit: quit_with_error( msg="Mininet emulation requires root privileges!", logger=LAYER_NAME) except KeyboardInterrupt: quit_with_error( msg= "Initiation of Mininet network was interrupted by user!", logger=log) self.started = True log.debug("Mininet network has been started!") self.runXTerms() else: log.warning( "Mininet network has already started! Skipping start task..." ) else: log.error("Missing topology! Skipping emulation...")
def runXTerms (self): """ Start an xterm to every SAP if it's enabled in the global config. SAP are stored as hosts in the Mininet class. :return: None """ if CONFIG.get_SAP_xterms(): log.debug("Starting xterm on SAPS...") terms = makeTerms(nodes=self.__mininet.hosts, title='SAP', term="xterm") self.xterms.extend(terms) else: log.warning("Skip starting xterms on SAPS according to global config")
def shutdown(self, event): """ .. seealso:: :func:`AbstractAPI.shutdown() <escape.util.api.AbstractAPI.shutdown>` :param event: event object """ log.info("Infrastructure Layer is going down...") if self.topology: try: self.topology.stop_network() except KeyboardInterrupt: log.warning( "Shutdown of Mininet network was interrupted by user!")
def stop_network (self): """ Stop network. """ log.debug("Shutting down Mininet network...") if self.__mininet is not None: if self.started: self.__mininet.stop() self.started = False log.debug("Mininet network has been stopped!") else: log.warning("Mininet network is not started yet! Skipping stop task...") if self._need_clean: self.cleanup()
def bind_inter_domain_SAPs(self, nffg): """ Search for inter-domain SAPs in given :class:`NFFG`, create them as a switch port and bind them to a physical interface given in sap.domain attribute. :param nffg: topology description :type nffg: :class:`NFFG` :return: None """ log.debug("Search for inter-domain SAPs...") # Create the inter-domain SAP ports for sap in {s for s in nffg.saps if s.binding is not None}: # NFFG is the raw NFFG without link duplication --> iterate over every # edges in or out there should be only one link in this case # e = (u, v, data) sap_switch_links = [ e for e in nffg.network.edges_iter(data=True) if sap.id in e ] try: if sap_switch_links[0][0] == sap.id: border_node = sap_switch_links[0][1] else: border_node = sap_switch_links[0][0] except IndexError: log.error("Link for inter-domain SAP: %s is not found. " "Skip SAP creation..." % sap) continue log.debug( "Detected inter-domain SAP: %s connected to border Node: %s" % (sap, border_node)) # if sap.delay or sap.bandwidth: # log.debug("Detected resource values for inter-domain connection: " # "delay: %s, bandwidth: %s" % (sap.delay, sap.bandwidth)) sw_name = nffg.network.node[border_node].id for sw in self.mn.switches: # print sw.name if sw.name == sw_name: if sap.binding not in get_ifaces(): log.warning( "Physical interface: %s is not found! Skip binding..." % sap.binding) continue log.debug( "Add physical port as inter-domain SAP: %s -> %s" % (sap.binding, sap.id)) # Add interface to border switch in Mininet # os.system('ovs-vsctl add-port %s %s' % (sw_name, sap.domain)) sw.addIntf(intf=Intf(name=sap.binding, node=sw))
def __init_from_NFFG (self, nffg): """ Initialize topology from an :any:`NFFG` representation. :param nffg: topology object structure :type nffg: :any:`NFFG` :return: None """ # pprint(nffg.network.__dict__) log.info("Start topology creation from NFFG(name: %s)..." % nffg.name) created_mn_nodes = {} # created nodes as 'NFFG-id': <node> created_mn_links = {} # created links as 'NFFG-id': <link> # If not set then cache the given NFFG as the topology description self.topo_desc = nffg # Create a Controller which will be the default internal POX controller try: self.create_Controller("ESCAPE") except SystemExit: raise TopologyBuilderException("Controller creations was unsuccessful!") # Convert INFRAs for infra in nffg.infras: # Create EE if infra.infra_type == NodeInfra.TYPE_EE: if infra.domain == "INTERNAL": ee_type = self.TYPE_EE_LOCAL else: log.warning( "Detected domain of infra: %s is not INTERNAL! Remote EE creation " "for domains other than INTERNAL is not supported yet!" % infra) # ee_type = self.TYPE_EE_REMOTE ee_type = self.TYPE_EE_LOCAL # FIXME - set resource info in MN EE if can - cpu,mem,delay,bandwidth? agt, sw = self.create_NETCONF_EE(name=infra.id, type=ee_type) created_mn_nodes[infra.id] = sw # Create Switch elif infra.infra_type == NodeInfra.TYPE_SDN_SWITCH: switch = self.create_Switch(name=infra.id) created_mn_nodes[infra.id] = switch elif infra.infra_type == NodeInfra.TYPE_STATIC_EE: static_ee = self.create_static_EE(name=infra.id) created_mn_nodes[infra.id] = static_ee else: quit_with_error( msg="Type: %s in %s is not supported by the topology creation " "process in %s!" % ( infra.infra_type, infra, self.__class__.__name__), logger=log) # Create SAPs - skip the temporary, inter-domain SAPs for sap in {s for s in nffg.saps if not s.binding}: # Create SAP sap_host = self.create_SAP(name=sap.id) created_mn_nodes[sap.id] = sap_host # Convert VNFs # TODO - implement --> currently the default Mininet topology does not # TODO contain NFs but it could be possible # Convert connections - copy link ref in a list and iter over it for edge in [l for l in nffg.links]: # Skip initiation of links which connected to an inter-domain SAP if (edge.src.node.type == NFFG.TYPE_SAP and edge.src.node.binding is not None) or ( edge.dst.node.type == NFFG.TYPE_SAP and edge.dst.node.binding is not None): continue # Create Links mn_src_node = created_mn_nodes.get(edge.src.node.id) mn_dst_node = created_mn_nodes.get(edge.dst.node.id) if mn_src_node is None or mn_dst_node is None: raise TopologyBuilderException( "Created topology node is missing! Something really went wrong!") src_port = int(edge.src.id) if int(edge.src.id) < 65535 else None if src_port is None: log.warning( "Source port id of Link: %s is generated dynamically! Using " "automatic port assignment based on internal Mininet " "implementation!" % edge) dst_port = int(edge.dst.id) if int(edge.dst.id) < 65535 else None if dst_port is None: log.warning( "Destination port id of Link: %s is generated dynamically! Using " "automatic port assignment based on internal Mininet " "implementation!" % edge) link = self.create_Link(src=mn_src_node, src_port=src_port, dst=mn_dst_node, dst_port=dst_port, bw=edge.bandwidth, delay=str(edge.delay) + 'ms') created_mn_links[edge.id] = link # Set port properties of SAP nodes. # A possible excerpt from a escape-mn-topo.nffg file: # "ports": [{ "id": 1, # "property": ["ip:10.0.10.1/24"] }] # for n in {s for s in nffg.saps if not s.binding}: mn_node = self.mn.getNodeByName(n.id) for port in n.ports: # ip should be something like '10.0.123.1/24'. if len(port.l3): if len(port.l3) == 1: ip = port.l3.container[0].provided else: log.warning( "Multiple L3 address is detected! Skip explicit IP address " "definition...") ip = None else: # or None ip = port.get_property('ip') if port.l2: mac = port.l2 else: mac = port.get_property('mac') intf = mn_node.intfs.get(port.id) if intf is None: log.warn(("Port %s of node %s is not connected," "it will remain unconfigured!") % (port.id, n.name)) continue if intf == mn_node.defaultIntf(): # Workaround a bug in Mininet mn_node.params.update({'ip': ip}) mn_node.params.update({'mac': mac}) if ip is not None: mn_node.setIP(ip, intf=intf) log.debug("Use explicit IP: %s for node: %s" % (ip, n)) if mac is not None: mn_node.setMAC(mac, intf=intf) log.debug("Use explicit MAC: %s for node: %s" % (mac, n)) # For inter-domain SAPs no need to create host/xterm just add the SAP as # a port to the border Node # Iterate inter-domain SAPs self.bind_inter_domain_SAPs(nffg=nffg) log.info("Topology creation from NFFG has been finished!")