Example #1
0
 def _start_mininet(self, opts=None):
     if not opts:
         opts = {}
     self._info("***Starting mininet***")
     opts['controller'] = Controller
     opts['autoSetMacs'] = True
     self.net = MininetWithControlNet(**opts)
Example #2
0
  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 _start_mininet(self, opts=None):
     if not opts:
         opts = {}
     self._info("***Starting mininet***")
     opts['controller'] = Controller
     opts['autoSetMacs'] = True
     self.net = MininetWithControlNet(**opts)
Example #4
0
    def __init__(self, net=None, opts=None, fallback=True, run_dry=True):
        """
    Initialize NetworkBuilder.

    If the topology definition is not found, an exception will be raised or
    an empty :class:`mininet.net.Mininet` topology will be created if
    ``run_dry`` is set.

    :param net: update given Mininet object instead of creating a new one
    :type net: :class:`mininet.net.Mininet`
    :param opts: update default options with the given opts
    :type opts: dict
    :param fallback: search for fallback topology (default: True)
    :type fallback: bool
    :param run_dry: do not raise an Exception and return with bare Mininet obj.
    :type run_dry: bool
    :return: None
    """
        self.opts = dict(self.default_opts)
        if opts is not None:
            self.opts.update(opts)
        self.fallback = fallback
        self.run_dry = run_dry
        if net is not None:
            if isinstance(net, Mininet):
                # Initial settings - Create new Mininet object if necessary
                self.mn = net
            else:
                raise TopologyBuilderException(
                    "Network object's type must be a derived class of Mininet!"
                )
        else:
            # self.mn = Mininet(**self.opts)
            try:
                self.mn = MininetWithControlNet(**self.opts)
            except KeyboardInterrupt:
                quit_with_error(
                    msg="Assembly of Mininet network was interrupted by user!",
                    logger=log)
        # Basically a wrapper for mn to offer helping functions
        self.mn_bridge = None
        # Cache of the topology description as an NFFG which is parsed during
        # initialization
        self.topo_desc = None
        self.__dpid_cntr = self.dpidBase
class NetworkManagerMininet(NetworkManager,
                            GenericEventNotifyer,
                            LoggerHelper):
    __shared_state = {}

    def __init__(self):
        'lazy init'
        self.__dict__ = self.__shared_state
        if self.__dict__:
            # already initialized
            return

        GenericEventNotifyer.__init__(self)
        self.net = None
        # Initial topo to create Mininet topology
        self.initial_topo = None
        # Network state
        self.state = NetworkManager.DOWN
        self.port_map = {}
        self.dpid = {}
        # Running topo to manage/store running topo obtained from Mininet - dummy object
        self.network = Store()
        # Active link list
        self.network.links = {}
        # Active node list
        self.network.nodes = {}
        # Queue for parallel event processing 
        self.of_event_queue = []
        # Timer daemon process for periodic polling
        self.process = None
        self.vnf_manager = None
        self.last_status_poll = 0
        pox.core.core.listen_to_dependencies(self)
        # Start periodic scan
        self.periodic_scan()

    #########
    ### Mininet topology compilation
    #########
    
    def build_topo_network(self, network_topo, appPrefs):
        """
        Filling Mininet "topo" with app specific data
        network_topo - Topology object need to update
        appPrefs - global startup parameters
        
        No return
        """
        dpctl = None if not appPrefs['dpctl'] else int(appPrefs['dpctl'])
        #dpctl = int(appPrefs['dpctl']) if 'dpctl' in appPrefs else None
        network_topo['netopts'].update({'listenPort': dpctl,
                                        'topo': None,
                                        'build': False,
                                        'ipBase': appPrefs['ipBase'],
                                        'autoSetMacs': True,
                                        'autoStaticArp': True})
        self._debug('Add global parameters: %s' % network_topo['netopts'])

    def build_topo_switch(self, network_topo, name, opts, appPrefs):
        """
        Build switch object with given name and "opts" params
        network_topo - Topology object need to update
        name - switch name
        opts - switch instance params
        appPrefs - global startup parameters
        
        No return
        """
        required_keys = {'switchType'}
        if not opts.viewkeys() & required_keys:
            raise KeyError('Required argument is missing!\nCheck: ' + repr(required_keys))
        switch = dict()
        switchParms = {'name': name}
        if 'dpctl' in opts:
            switchParms['listenPort'] = int(opts['dpctl'])
        if 'dpid' in opts:
            switchParms['dpid'] = opts['dpid']
        # Get switch type or default
        if opts['switchType'] == 'default':
            sw_type = appPrefs['switchType']
        else:
            sw_type = opts['switchType']
        # Get the correct switch class
        if sw_type == 'ivs':
            switchParms['cls'] = IVSSwitch
        elif sw_type == 'user':
            switchParms['cls'] = CustomUserSwitch
        elif sw_type == 'userns':
            switchParms['inNamespace'] = True
            switchParms['cls'] = CustomUserSwitch
        else:
            switchParms['cls'] = customOvs
            switch['openflowver'] = appPrefs['openFlowVersions']
        switch['controllers'] = opts.get('controllers', None)
        switch['netflow'] = opts.get('netflow', None)
        switch['sflow'] = opts.get('sflow', None)
        # Are these ifs important or switch instances can contain empty ip, extintf attributes?
        # Attach external interfaces
        if 'externalInterfaces' in opts:
            switch['extintf'] = opts['externalInterfaces']
        if 'ip' in opts:
            switch['ip'] = opts['switchIP']
        # Add new switch param and switch
        switch['params'] = switchParms
        network_topo['switches'][opts['_id']] = switch
        self._debug('Add %s Switch to mininet topo with parameters %s' % (name, network_topo['switches'][opts['_id']]))
        
    def build_topo_ee(self, network_topo, name, opts):
        """
        Build VNF Container object with given name and "opts" params
        network_topo - Topology object need to update
        name - Container name
        opts - Container instance params
        
        No return
        """
        settings = {}
        ip = opts.get('ip', None)
        if ip: settings['ip'] = ip

        defaultRoute = opts.get('defaultRoute', None)
        if defaultRoute: settings['defaultRoute'] = 'via ' + defaultRoute

        # Create the correct host class
        hostCls = EE
        params = {'name': name,
                  'cls': hostCls,
                  'cpu': opts['res']['cpu'],
                  'mem': opts['res']['mem'],
                  'ee_type': opts.get('ee_type', 'static'),
                  }
        for o in ['remote_dpid', 'remote_port', 'remote_conf_ip',
                  'remote_netconf_port', 'netconf_username',
                  'netconf_passwd', 'local_intf_name']:
            params[o] = opts.get(o)

        params.update(settings)
        network_topo['ee'][opts['_id']]={'params': params}

        if False:
            # Set the CPULimitedHost specific options
            if 'cores' in opts:
                network_topo['ee'][opts['_id']]['cores'] = opts['cores']
            if 'cpu' in opts:
                network_topo['ee'][opts['_id']]['frac']={'f':opts['res']['cpu'],
                                                         'sched':opts['sched']
                                                         }

        # Attach external interfaces
        if 'externalInterfaces' in opts:
            network_topo['ee'][opts['_id']]['extintf'] = opts['externalInterfaces']

        vlanif = opts.get('vlanInterfaces', None)
        if vlanif:
            self._debug('Checking that OS is VLAN prepared')
            self.pathCheck('vconfig', moduleName='vlan package')
            moduleDeps( add='8021q' )
            network_topo['ee'][opts['_id']]['vlanif'] = vlanif
            
        self._debug("Add %s EE to mininet topo with parameters %s" % (name, network_topo['ee'][opts['_id']]))

    def pathCheck( self, *args, **kwargs ):
        """Make sure each program in *args can be found in $PATH."""
        moduleName = kwargs.get( 'moduleName', 'it' )
        for arg in args:
            if not quietRun( 'which ' + arg ):
                showerror(title="Error",
                      message= 'Cannot find required executable %s.\n' % arg +
                       'Please make sure that %s is installed ' % moduleName +
                       'and available in your $PATH.' )

    def build_topo_controller(self, network_topo, name, opts):
        """
        Build Controller object with given name and "opts" params
        network_topo - Topology object need to update
        name - Controller name
        opts - Controller instance params
        
        No return
        """
        # Get controller info from panel
        controllerType = opts['controllerType']

        # Make controller
        self._info('*** Getting controller selection: %s' % controllerType)

        if controllerType == 'remote':
            c = RemoteController
        elif controllerType == 'inband':
            c = InbandController
        elif controllerType == 'ovsc':
            c = OVSController
        else:
            c = Controller
        params = {'name': name,
                  'ip': opts['remoteIP'],
                  'port': opts['remotePort'],
                  'controller': c
                  }
        network_topo['controllers'][opts['_id']] = {'params': params}
        self._debug("Add %s Controller(s) to mininet topo with parameters %s" % (name, network_topo['controllers'][opts['_id']]))

    def build_topo_sap(self, network_topo, name, opts):
        settings = {}
        ip = opts.get('ip', None)
        if ip: settings['ip'] = ip
                    # else:
                        # nodeNum = canvas.startpointOpts[name]['nodeNum']
                        # settings['nodeNum']= nodeNum
                        # ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
                        # settings['ipBaseNum'] = ipBaseNum
                        # settings['prefixLen'] = prefixLen
                        # ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)

        defaultRoute = opts.get('defaultRoute', None)
        if defaultRoute: settings['defaultRoute'] = 'via ' + defaultRoute

        # Create the correct host class        
        hostCls = Host
        if 'cores' in opts or 'cpu' in opts:
            hostCls=CPULimitedHost

        params = {'name': name,
                  'cls': hostCls
                  }
        params.update(settings)
        network_topo['saps'][opts['_id']]={'params': params}

                    # Set the CPULimitedHost specific options
        if 'cores' in opts:
            network_topo['saps'][opts['_id']]['cores'] = opts['cores']
        if 'cpu' in opts:
            network_topo['saps'][opts['_id']].update({'f':opts['res']['cpu'],
                                                     'sched':opts['sched']
                                                     })

                    # Attach external interfaces
        if 'externalInterfaces' in opts:
            network_topo['saps'][opts['_id']]['extintf'] = \
            opts['externalInterfaces']

        vlanif = opts.get('vlanInterfaces', None)
        if vlanif:
            pass
                            # self._debug('Checking that OS is VLAN prepared')
                            # self.pathCheck('vconfig', moduleName='vlan package')
                            # moduleDeps( add='8021q' )
        self._debug("Add %s SAP to mininet topo with parameters %s" % (name, network_topo['saps'][opts['_id']]))

    def build_topo_links(self, network_topo, phy_g):     
        for node1, node2, params in list(phy_g.edges_iter(data=True)):
            if params.get('type', None) == 'data':
                network_topo['links'][(node1, node2)] = {'node1': node1,
                                                         'node2': node2,
                                                         'cls': TCLink}
                if 'delay' in params:
                    network_topo['links'][(node1, node2)]['delay'] = params.get('delay', 5)
                if 'bw' in params:
                    network_topo['links'][(node1, node2)]['bw'] = params['bw']
                self._debug("Create link between %s : %s with parameters %s" % (node1, node2, network_topo['links'][(node1, node2)]))
                
    # TODO: don't use appPrefs and canvas, references and bindings to GUI should be removed
    # check imports to eliminate unnecessary ones
    def build_topo(self, phy_g, appPrefs):
        """
        Generate and set mininet topo
        
        No return
        """
        from Utils import dump
        #dump(phy_g)
        self.initial_topo = self.generate_topo(phy_g, appPrefs)
        
    def generate_topo(self, phy_g, appPrefs):
        """
        Build the topology according to the GUI widget params
        appPrefs - global params: ipBase, switchType, openFlowVersions
        Return: topo object
        """
        self._info("*** Build network based on our topology.")
        # Empty topo
        network_topo = { 'netopts': dict(),
                         'ee': dict(),
                         'saps': dict(),
                         'switches': dict(),
                         'controllers': dict(),
                         'links': dict()
                        }
        # Set global params
        self.build_topo_network(network_topo, appPrefs)
        
        # Make nodes
        self._info("*** Getting Hosts and Switches.")
        for node in phy_g.nodes():
            if phy_g.node[node]['node_type'] == NODE_TYPE['SWITCH']:
                # Adding specific switch object to "topo"
                self.build_topo_switch(network_topo, node, phy_g.node[node], appPrefs)
                # TODO: Need to handle 'LegacySwitch' ???
                    # elif 'LegacySwitch' in tags:
                        # opts = canvas.switchOpts[name]
                        # params = {'params':{'name': name,
                                            # 'cls': LegacySwitch}
                                  # }
                        # # Adding specific switch object to "topo"
                        # network_topo['switches'][opts['_id']] = params
            elif phy_g.node[node]['node_type'] == NODE_TYPE['NODE']:
                # Adding specific EE object to "topo"
                self.build_topo_ee(network_topo, node, phy_g.node[node])
            elif phy_g.node[node]['node_type'] == NODE_TYPE['CONTROLLER']:
                # Adding specific controller object to "topo"
                self.build_topo_controller(network_topo, node, phy_g.node[node])
                # TODO: Need to handle 'LegacyRouter' ???
                    # elif 'LegacyRouter' in tags:
                        # opts = canvas.switchOpts[name]
                        # params = {'params':{'name': name,
                                            # 'cls': LegacyRouter}
                                  # }
                        # # Adding specific router object to "topo"
                        # network_topo['hosts'][opts['_id']] = params
            elif phy_g.node[node]['node_type'] == NODE_TYPE['SAP']:
                # Adding specific SAP object to "topo"
                self.build_topo_sap(network_topo, node, phy_g.node[node])
            else:
                raise TypeError('Cannot create mystery node: ' + node)
            
        # Adding the links
        self._info("*** Getting Links.")
        self.build_topo_links(network_topo, phy_g)
        
        return network_topo
    
    #########
    ### Mininet topology creation
    #########
    
    # Use this function instead of direct access to initial_topo
    def get_initial_topology(self):
        """
        General function to return the observed topology in the NetworkX format
        """
        return self._convert_to_NetworkX_format()

    def _convert_to_NetworkX_format(self):
        """
        Convert the "topo" dictionary to NetworkX format for Orchestration module
        Keep only the relevant node information
        Return: graph - networkx.classes.graph.Graph
        @author: czentye
        """
        # Create empty graph
        graph = nx.Graph()
        # Return if topo is not set
        if not self.initial_topo:
            return graph
        from Utils import dump
        #dump(self.initial_topo, 'NetMen initial_topo')
        # Convert "controllers" to node
        controllers = self.initial_topo['controllers']
        for c in controllers:
            node_name = controllers[c]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = c
            graph.node[node_name]['node_type'] = NODE_TYPE['CONTROLLER']
            graph.node[node_name]['hostname'] = node_name

            graph.node[node_name]['controllerType'] = None   #TODO cut from controllers[c]['params']['controller']
            graph.node[node_name]['canvas_id'] = None

            graph.node[node_name]['remoteIP'] = controllers[c]['params']['ip']
            graph.node[node_name]['remotePort'] = controllers[c]['params']['port']

        # Convert "ee" to node
        ee = self.initial_topo['ee']
        for container in ee:
            node_name = ee[container]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = container
            graph.node[node_name]['node_type'] = NODE_TYPE['NODE']
            graph.node[node_name]['hostname'] = node_name

            graph.node[node_name]['nodeNum'] = None
            graph.node[node_name]['canvas_id'] = None

            # graph.node[node_name]['ee_type'] = ee[container]['params']['ee_type']
            # graph.node[node_name]['cpu'] = ee[container]['params']['cpu']
            # graph.node[node_name]['mem'] = ee[container]['params']['mem']
            # Duplicated data, SHOULD be removed !!!
            graph.node[node_name]['res'] = {'cpu': ee[container]['params']['cpu'], 'mem': ee[container]['params']['mem']}

        # Convert "saps" to node
        saps = self.initial_topo['saps']
        for sap in saps:
            node_name = saps[sap]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = sap
            graph.node[node_name]['node_type'] = NODE_TYPE['SAP']
            graph.node[node_name]['name'] = node_name

            graph.node[node_name]['canvas_id'] = None
            graph.node[node_name]['nodeNum'] = None

        # Convert "switches" to node
        switches = self.initial_topo['switches']
        for switch in switches:
            node_name = switches[switch]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = switch
            graph.node[node_name]['node_type'] = NODE_TYPE['SWITCH']
            graph.node[node_name]['hostname'] = node_name

            graph.node[node_name]['canvas_id'] = None
            graph.node[node_name]['nodeNum'] = None

            graph.node[node_name]['controllers'] = switches[switch]['controllers']
            graph.node[node_name]['netflow'] = switches[switch]['netflow']
            graph.node[node_name]['sflow'] = switches[switch]['sflow']
            #graph.node[node_name]['switchIP'] = ''
            graph.node[node_name]['switchType'] = None   #TODO cut from switches[switch]['params']['cls']

        # Convert "links" to edges
        links = self.initial_topo['links']
        for node1, node2 in links:
            options = {'weight': 1}
            if graph.node[node1]['node_type'] == 'C' or graph.node[node2]['node_type'] == 'C':
                # Control links are missing from topo -> this branch is useless
                options['type'] = LINK_TYPE['CONTROL']
            else:
                if 'bw' in links[(node1, node2)]:
                    options['bw'] = links[(node1, node2)]['bw']
                options['type'] = LINK_TYPE['DATA']
                if 'delay' in links[(node1, node2)]:
                    options['delay'] = links[(node1, node2)].get('delay', 5)
            graph.add_edge(node1, node2, attr_dict=options)

        # Adding control channel links
        # TODO improve if there is multiple controller
        for node in self.initial_topo['switches']:
            graph.add_edge(self.initial_topo['switches'][node]['controllers'][0], node, attr_dict={'type': LINK_TYPE['CONTROL'], 'weight': 1})
        return graph

    def _start_mininet(self, opts=None):
        if not opts:
            opts = {}
        self._info("***Starting mininet***")
        opts['controller'] = Controller
        opts['autoSetMacs'] = True
        self.net = MininetWithControlNet(**opts)

    def _create_ee(self, ees):
        self._info('**** Create %d execution environment(s)' % len(ees))
        for id, ee in ees.iteritems():
            params = ee['params']
            name = params['name']
            self._debug('\tCreate %s EE with params %s' % (name, ee))
            if params['ee_type'] == 'netconf':
                sw = self.net.addSwitch(name)
                agt = self.net.addAgent('agt_' + name)
                agt.setSwitch(sw)
                continue
            elif params['ee_type'] == 'remote':
                p = copy.deepcopy(params)
                p['cls'] = None
                p['inNamespace'] = False
                p['dpid'] = p['remote_dpid']
                p['username'] = p['netconf_username']
                p['passwd'] = p['netconf_passwd']
                p['conf_ip'] = p['remote_conf_ip']
                p['agentPort'] = p['remote_netconf_port']
                del p['name']
                sw = self.net.addRemoteSwitch(name, **p)
                agt = self.net.addAgent('agt_' + name, **p)
                agt.setSwitch(sw)
                continue
            else:
                # params['ee_type'] == 'static':
                # normal case
                h = self.net.addEE(**params)
                if 'cores' in ee:
                    h.setCPUs(**ee['cores'])
                if 'frac' in ee:
                    h.setCPUFrac(**ee['frac'])
                if 'vlanif' in ee:
                    for vif in ee['vlaninf']:
                        # TODO: In miniedit it was after self.net.build()
                        h.cmdPrint('vconfig add '+name+'-eth0 '+vif[1])
                        h.cmdPrint('ifconfig '+name+'-eth0.'+vif[1]+' '+vif[0])

    def _create_switches(self, switches):
        self._info('**** Create %d switch(es)'%len(switches))
        for id, switch in switches.iteritems():
            self._debug('\tCreate %s switch with params %s' % (switch['params']['name'], switch))
            sw = self.net.addSwitch(**switch['params'])
            if 'openflowver' in switch:
                sw.setOpenFlowVersion(switch['openflowver'])
            if 'ip' in switch:
                sw.setSwitchIP(switch['ip'])

    def _create_controllers(self, controllers):
        self._info('**** Create %d controller(s)'%len(controllers))
        for id, controller in controllers.iteritems():
            self._debug('\tCreate %s controller with params %s' % (controller['params']['name'], controller))
            self.net.addController(**controller['params'])

    def _create_saps(self, saps):
        self._info('**** Create %d SAP(s)'%len(saps))
        for id, sap in saps.iteritems():
            self._debug('\tCreate %s SAP with params %s' % (sap['params']['name'], sap))
            self.net.addHost(**sap['params'])

    def _create_links(self, links):

        def is_remote(node):
            return isinstance(node, RemoteSwitch)

        def is_local(node):
            return not is_remote(node)

        self._info('**** Create %d link(s)' % len(links))
        for id, link in links.iteritems():
            self._debug('\tCreate link %s with params: %s' % (id, link))
            node1 = self.net.get(link['node1'])
            node2 = self.net.get(link['node2'])
            name_to_node = {'node1': node1, 'node2': node2}
            link.update(name_to_node)

            remote = filter(is_remote, [node1, node2])
            local = filter(is_local, [node1, node2])
            if not remote:
                self.net.addLink(**link)
            else:
                sw = local[0]
                r = remote[0]
                intfName = r.params['local_intf_name']
                r_mac = None # unknown, r.params['remote_mac']
                r_port = r.params['remote_port']
                self._debug('\tadd hw interface (%s) to node (%s)' % (intfName, sw.name))

                # This hack avoids calling __init__ which always makeIntfPair()
                link = Link.__new__(Link)
                i1 = Intf(intfName, node=sw, link=link)
                i2 = Intf(intfName, node=r, mac=r_mac, port=r_port, link=link)
                i2.mac = r_mac # mn runs 'ifconfig', which resets mac to None
                #
                link.intf1, link.intf2 = i1, i2

    def _start_controllers(self):
        self._info('**** Start controller(s)')
        for controller in self.net.controllers:
            controller.start()

    def _start_switches(self, switches):
        for id, switch in switches.iteritems():
            controllers = [] #with legacySwitch there is no controller in miniedit
            if switch['controllers']:
                controllers.append(self.net.get(*switch['controllers']))
            self.net.get(switch['params']['name']).start(controllers)

    def start_topo(self, **kwargs):
        """
        Start the physical topology (using Mininet)
        topo - Set and use this topology
        nflow - 
        sflow - 
        startcli - 
        
        No return
        """
        
        self.change_network_state(NetworkManager.STARTING)
        # Save given topo (created by MiniEdit)
        if 'topo' in kwargs:
            self.initial_topo = kwargs['topo']

        if self._is_initial_topo_empty():
            raise AttributeError("Initial topology is missing!!!")

        self._start_mininet(self.initial_topo['netopts'])
        self._create_ee(self.initial_topo['ee'])
        self._create_switches(self.initial_topo['switches'])
        self._create_controllers(self.initial_topo['controllers'])
        self._create_saps(self.initial_topo['saps'])
        self._create_links(self.initial_topo['links'])

        self.net.build()
        self.net.start()

        if 'nflow' in kwargs and kwargs['nflow'].get('nflowTarget'):
            self.start_nflow(kwargs['nflow']['nflowTarget'],
                             kwargs['nflow']['nflowTimeout'],
                             kwargs['nflow']['nflowAddId'])
        if 'sflow' in kwargs and kwargs['sflow'].get('sflowTarget'):
            self.start_sflow(kwargs['sflow']['sflowTarget'],
                             kwargs['sflow']['sflowHeader'],
                             kwargs['sflow']['sflowSampling'],
                             kwargs['sflow']['sflowPolling'])
        if 'startcli' in kwargs and kwargs['startcli'] == '1':
            self.start_cli()

        self.change_network_state(NetworkManager.UP)

    def _is_initial_topo_empty(self):
        return self.initial_topo is None or ('ee' in self.initial_topo and 'saps' in self.initial_topo and 'switches' in self.initial_topo and 'controller' in self.initial_topo)
        
    def stop_network(self):
        self.change_network_state(NetworkManager.STOPPING)
        if self.net is not None:
            self.net.stop()
        self.net = None
        self.initial_topo = None
        self.network.links = {}
        self.network.nodes = {}
        self.dpid = {}
        self.port_map = {}
        # self._debug('Cleaning up Mininet...')
        # mininet.clean.cleanup()
        # time.sleep(4)
        self.change_network_state(NetworkManager.DOWN)

    def network_alive(self):
        return self.state in [NetworkManager.UP]

    def start_sflow(self, target, header, sampling, polling):
        sflowEnabled = False
        sflowSwitches = ''
        for switch in self.initial_topo['switches'].itervalues():
            name = switch['params']['name']
            if switch.get('sflow', None) == '1':
                self._info('%s has sflow enabled' % name)
                sflowSwitches += ' -- set Bridge '+name+' sflow=@MiniEditSF'
                sflowEnabled=True

        if sflowEnabled:
            sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '\
                       'target=\\\"'+target+'\\\" '\
                       'header='+header+' '+ \
                       'sampling='+sampling+' '+\
                       'polling='+polling
            self._debug('sFlow command: cmd = %s%s' % (sflowCmd,sflowSwitches))
            call(sflowCmd+sflowSwitches, shell=True)

        else:
            self._info('No switches with sflow')

    def start_nflow(self, nflowTarget, nflowTimeout, nflowAddId):
        nflowSwitches = ''
        nflowEnabled = False
        for switch in self.initial_topo['switches'].itervalues():
            name = switch['params']['name']
            if switch.get('netflow', None) == '1':
                self._info('%s has Netflow enabled'% name)
                nflowSwitches += ' -- set Bridge '+name+' netflow=@MiniEditNF'
                nflowEnabled=True

        if nflowEnabled:
            nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '\
                       'target=\\\"'+nflowTarget+'\\\" '\
                       'active-timeout='+nflowTimeout

            if nflowAddId == 1:
                nflowCmd += ' add_id_to_interface=true'
            else:
                nflowCmd += ' add_id_to_interface=false'

            self._debug('nFlowcmd = %s'%(nflowCmd+nflowSwitches))
            call(nflowCmd+nflowSwitches, shell=True)

        else:
            self._info('No switches with Netflow')

    def start_cli(self):
        CLI(self.net)

    def start_clicky(self, vnf_name):
        instances = self.vnf_manager.start_clicky(vnf_name)
        self.net.clickys += instances
        # self._debug('CLICKY: %s' % instances)

    #########
    ### Scan the Mininet network and updating the running topology
    #########
    
    def periodic_scan(self, wait = 1):
        """Scan the Mininet topo and recall itself after a period of time (wait)"""
        self.scan_network()

        self.process = threading.Timer(wait, self.periodic_scan)
        self.process.daemon = True
        self.process.start()

    def scan_network(self, forced = False):
        self.process_event_queue()
        if not self.net or self.state == NetworkManager.STOPPING:
            return
        self.poll_netconf_agents(forced)

        checked = []
        # net -> Mininet network representation
        for name, node in self.net.items():
            checked.append(name)
            if not self.found_node(node):
                return

        deleted = []
        for name, opts in self.network.nodes.iteritems():
            if opts.get('parent'):
                # netconf-controlled vnf node, mininet doesn't know about it
                continue
            if name not in checked:
                deleted.append(name)
        if self.state != NetworkManager.STARTING:
            for node_name in deleted:
                self._debug('remove node (%s) from network table' % node_name)
                del self.network.nodes[node_name]
                # TODO: send event

        deleted = []
        for link_id, link in self.network.links.iteritems():
            node_names = [link['node1'], link['node2']]
            for node_name in node_names:
                if node_name not in self.network.nodes:
                    self._debug('delete link: %s' % node_names)
                    deleted.append(link_id)
        for link_id in deleted:
            del self.network.links[link_id]

        self._fire_dpid_update(self.dpid)
        self._fire_port_map_update(self.port_map)

    def poll_netconf_agents(self, forced = False):
        "Poll netconf agents for VNF status updates"
        poll_period = 10
        if (time.time() - self.last_status_poll < poll_period) and not forced:
            return
        for sw in self.net.switches:
            if not sw.getAgent():
                continue
            i = self.vnf_manager.get_vnf_info_on_node(sw.name)
            # self._debug('VNF_INFO: %s' % i)
            visited = []
            for vnf_name, new_opts in i.iteritems():
                if vnf_name is None:
                    continue
                new_opts['parent'] = sw.name
                visited.append(vnf_name)
                orig = self.network.nodes.get(vnf_name, {})
                old_status = orig.get('status')
                if orig == new_opts:
                    # nothing's changed
                    continue
                orig.update(new_opts)
                self.network.nodes[vnf_name] = orig
                links = new_opts.get('link', [])
                if type(links) != list:
                    links = [links]
                self.found_vnf_links(vnf_name, links)
                if old_status != new_opts['status']:
                    self._fire_vnf_update(vnf_name, new_opts['status'])
            deleted = []
            for node_name, opts in self.network.nodes.iteritems():
                # self._debug('node_name: %s  opts: %s' % (node_name, opts))
                if opts.get('parent') != sw.name:
                    continue
                if node_name not in visited:
                    deleted.append(node_name)
            for vnf_name in deleted:
                del self.network.nodes[vnf_name]
                # self._debug('vnf_name: %s' % vnf_name)
                sw =  self.net.nameToNode[vnf_name]
                self.net.switches.remove(sw)
                del self.net.nameToNode[vnf_name]
                neighbours = self.port_map[vnf_name].keys()
                for n in neighbours:
                    del self.port_map[n][vnf_name]
                    # Assuming there can be only one link between n and vnf.
                    link = {'node1': n, 'node2': vnf_name,
                            'intf1': None, 'intf2': None, 'delete': True}
                    pox.core.core.raiseLater(self, LinkChange, **link)
                del self.port_map[vnf_name]
                self._fire_vnf_update(vnf_name, 'STOPPED')

        self.last_status_poll = time.time()

    def found_vnf_links(self, vnf_id, links):
        def get_intf_by_name(node, intf_name):
            for i in node.intfList():
                if str(i) == intf_name:
                    return i
            return None

        def get_or_create_intf(dev_name, obj, port):
            if port not in obj.intfs:
                # does not exist, let's create it
                return Intf(dev_name, node=obj, port=port)
            if str(obj.intfs[port]) != dev_name:
                # intf exists, but port is invalid
                return Intf(dev_name, node=obj, port=port)
            return obj.intfs[port]

        for i, link in enumerate(links):
            if int(link['sw_port']) == -1:
                # disconnected links (with port==-1) are omitted
                continue
            sw_name = link.get('sw_id', 'sw_id'+str(i))
            sw_dev = link.get('sw_dev', 'sw_dev'+str(i))
            sw_port = int(link['sw_port'])

            nf_name = vnf_id
            nf_dev  = link.get('vnf_dev', 'vnf_dev'+str(i))
            nf_port = int(link['vnf_port'])
            nf_mac = link['vnf_dev_mac']

            sw_obj = self.net.getNodeByName(sw_name)
            try:
                nf_obj = self.net.getNodeByName(nf_name)
            except KeyError:
                # this is a VNF not managed by mininet, yet we have to
                # add to the mininet 'database'.  TODO: Ideally, it
                # would be a RemoteHost, but for now it is a
                # RemoteSwitch.
                nf_obj = self.net.addRemoteSwitch(nf_name, dpid="-1")
            sw_i = get_or_create_intf(sw_dev, sw_obj, sw_port)
            if nf_dev in nf_obj.intfNames():
                nf_i = get_intf_by_name(nf_obj, nf_dev)
            else:
                nf_i = Intf(nf_dev, node=nf_obj, port=nf_port, mac=nf_mac)
                nf_i.mac = nf_mac # mn runs 'ifconfig', which resets mac to None

            self.found_link(sw_obj, nf_obj, sw_i, nf_i)

    def found_link(self, node_a, node_b, intf_a, intf_b):
        # link "A -> B" is the same as link "B -> A"
        link = [(node_a, intf_a), (node_b, intf_b)]
        link = sorted(link, key=lambda x: x[1])
        [(node1, intf1), (node2, intf2)] = link

        link_id = ''.join([intf1.name, intf2.name])
        orig_link = self.network.links.get(link_id, {})
        link = { 'node1': node1.name,
                 'node2': node2.name,
                 'intf1': intf1,
                 'intf2': intf2
                }
        try:
            self.port_map[node1.name][node2.name] = node1.ports[intf1]
        except KeyError:
            self.port_map[node1.name] = {node2.name: node1.ports[intf1]}

        try:
            self.port_map[node2.name][node1.name] = node2.ports[intf2]
        except KeyError:
            self.port_map[node2.name] = {node1.name: node2.ports[intf2]}

        if not cmp(orig_link, link) == 0:
            self.network.links[link_id] = link
            pox.core.core.raiseLater(self, LinkChange, **link)

    def found_node(self, node):
        orig = self.network.nodes.get(node.name, {})
        new_opts = copy.deepcopy(orig)
        new_opts['name'] = node.name
        new_opts['intf'] = {}
        for intf in node.intfList():
            new_opts['intf'][intf.name] = {'ip': node.IP(intf),
                                           'mac': node.MAC(intf),
                                           'port': node.ports[intf]}
        try:
            # taken form Node.connectionsTo
            for intf in node.intfList():
                link = intf.link
                if not intf.link:
                    continue
                node1, node2 = link.intf1.node, link.intf2.node
                if node1 == node or node2 == node:
                    self.found_link(node1, node2, link.intf1, link.intf2)
        except AttributeError:
            # network is not running
            return False

        if getattr(node, 'dpid', None):
            new_opts['dpid'] = int(node.dpid, base=16)
            self.dpid[node.name] = int(node.dpid, base=16)

        if not cmp(orig, new_opts) == 0:
            self.network.nodes[node.name] = new_opts
            pox.core.core.raiseLater(self, NodeChange,
                                     NodeChange.TYPE_DUMMY, **new_opts)
        return True

    def dpid_to_name(self, dpid):
        for name, name_dpid in self.dpid.iteritems():
            if dpid == name_dpid:
                return name
        return None

    def process_event_queue(self):
        processed = []
        for e in self.of_event_queue:
            name = self.dpid_to_name(e.dpid)
            if not name:
                continue
            e.name = name
            processed.append(e)
            self.fire(e.type, e)
        for e in processed:
            self.of_event_queue.remove(e)

    def change_network_state(self, new_state):
        if self.state == new_state:
            return
        self.state = new_state
        self._fire_network_state_change()

    #########
    ### Change Event generation and OpenFlow event handling
    #########
    def _fire_network_state_change(self):
        """Signalling the node/link params are changed"""
        event = Store()
        event.state = self.state
        self.fire('network_state_change', event)

    def _fire_dpid_update(self, dpids):
        event = Store()
        event.dpids = dpids
        self.fire('dpid_update', event)

    def _fire_port_map_update(self, port_map):
        event = Store()
        event.port_map = port_map
        self.fire('port_map_update', event)

    def _fire_switch_connection_up(self):
        pass

    def _fire_switch_connection_down(self):
        pass

    def _fire_vnf_update(self, vnf_name, status):
        m = {'FAILED': 'failed',
             'UP_AND_RUNNING': 'running',
             'INITIALIZING': 'starting',
             'STOPPED': 'stopped',
             }
        event = Store()
        event.name = vnf_name
        try:
            event.on_node = self.network.nodes[vnf_name]['parent']
        except KeyError:
            event.on_node = None
        event.status = m.get(status, 'failed')
        self.fire('vnf_update', event)

    def _handle_openflow_ConnectionUp(self, event):
        event.type = 'switch_connection_up'
        self.of_event_queue.append(event)

    def _handle_openflow_ConnectionDown(self, event):
        event.type = 'switch_connection_down'
        self.of_event_queue.append(event)
Example #6
0
class ESCAPENetworkBuilder(object):
  """
  Builder class for topology.

  Update the network object based on the parameters if it's given or create
  an empty instance.

  Always return with an ESCAPENetworkBridge instance which offer a generic
  interface for created :class:`mininet.net.Mininet` object and hide
  implementation's nature.

  Follows Builder design pattern.
  """
  # Default initial options for Mininet
  default_opts = {
    "controller": InternalControllerProxy,
    # Use own Controller
    'build': False,  # Not build during init
    'inNamespace': False,  # Not start element in namespace
    'autoSetMacs': False,  # Set simple MACs
    'autoStaticArp': True,  # Set static ARP entries
    'listenPort': None,  # Add listen port to OVS switches
    'link': TCLink  # Add default link
  }
  # Default internal storing format for NFFG parsing/reading from file
  DEFAULT_NFFG_FORMAT = "NFFG"
  # Constants
  TYPE_EE_LOCAL = "LOCAL"
  TYPE_EE_REMOTE = "REMOTE"
  # Constants for DPID generation
  dpidBase = 1  # Switches start with port 1 in OpenFlow
  dpidLen = 16  # digits in dpid passed to switch

  def __init__ (self, net=None, opts=None, fallback=True, run_dry=True):
    """
    Initialize NetworkBuilder.

    If the topology definition is not found, an exception will be raised or
    an empty :class:`mininet.net.Mininet` topology will be created if
    ``run_dry`` is set.

    :param net: update given Mininet object instead of creating a new one
    :type net: :class:`mininet.net.Mininet`
    :param opts: update default options with the given opts
    :type opts: dict
    :param fallback: search for fallback topology (default: True)
    :type fallback: bool
    :param run_dry: do not raise an Exception and return with bare Mininet obj.
    :type run_dry: bool
    :return: None
    """
    self.opts = dict(self.default_opts)
    if opts is not None:
      self.opts.update(opts)
    self.fallback = fallback
    self.run_dry = run_dry
    if net is not None:
      if isinstance(net, Mininet):
        # Initial settings - Create new Mininet object if necessary
        self.mn = net
      else:
        raise TopologyBuilderException(
          "Network object's type must be a derived class of Mininet!")
    else:
      # self.mn = Mininet(**self.opts)
      try:
        self.mn = MininetWithControlNet(**self.opts)
      except KeyboardInterrupt:
        quit_with_error(
          msg="Assembly of Mininet network was interrupted by user!",
          logger=log)
    # Basically a wrapper for mn to offer helping functions
    self.mn_bridge = None
    # Cache of the topology description as an NFFG which is parsed during
    # initialization
    self.topo_desc = None
    self.__dpid_cntr = self.dpidBase

  def __get_new_dpid (self):
    """
    Generate a new DPID and return the valid format for Mininet/OVS.

    :return: new DPID
    :rtype: str
    """
    dpid = hex(int(self.__dpid_cntr))[2:]
    dpid = '0' * (self.dpidLen - len(dpid)) + dpid
    self.__dpid_cntr += 1
    return dpid

  ##############################################################################
  # Topology initializer functions
  ##############################################################################

  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!")

  def __init_from_AbstractTopology (self, topo_class):
    """
    Build topology from pre-defined Topology class.

    :param topo_class: topology
    :type topo_class: :any:`AbstractTopology`
    :return: None
    """
    log.info("Load topology from class: %s" % topo_class.__name__)
    if topo_class.TYPE == "STATIC":
      self.mn.topo = topo_class().construct()
      self.mn.build()
    elif topo_class.TYPE == "DYNAMIC":
      # self.mn = topo_class().construct()
      topo_class().construct(builder=self)
    else:
      raise TopologyBuilderException(
        "TYPE field of the Topology class need to be set!")
    self.topo_desc = topo_class.get_topo_desc()

  def __init_from_CONFIG (self, 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 format: NF-FG storing format (default: internal NFFG representation)
    :type format: str
    :return: None
    """
    path = CONFIG.get_mininet_topology()
    if path is None:
      raise TopologyBuilderException("Missing Topology!")
    self.__init_from_file(path=path, format=format)

  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!")
      # except SystemExit:
      #   raise TopologyBuilderException("Got exit exception from Mininet!")

  def get_network (self):
    """
    Return the bridge to the constructed network.

    :return: object representing the emulated network
    :rtype: :any:`ESCAPENetworkBridge`
    """
    if self.mn_bridge is None:
      # Create the Interface object and set the topology description as the
      # original NFFG
      self.mn_bridge = ESCAPENetworkBridge(network=self.mn,
                                           topo_desc=self.topo_desc)
      # Additional settings
      self.mn_bridge._need_clean = CONFIG.get_clean_after_shutdown()
    return self.mn_bridge

  ##############################################################################
  # Builder functions
  ##############################################################################

  def create_static_EE (self, name, cls=None, **params):
    """
    Create and add a new EE to Mininet in the static way.

    This function is for only backward compatibility.

    .. warning::
      Not tested yet!

    :param name: name of the Execution Environment
    :type name: str
    :param cls: custom EE class/constructor (optional)
    :type cls: :class:`mininet.node.EE`
    :param cores: Specify (real) cores that our cgroup can run on (optional)
    :type cores: list
    :param frac: Set overall CPU fraction for this EE (optional)
    :type frac: list
    :param vlanif: set vlan interfaces (optional)
    :type vlanif: list
    :return: newly created EE object
    :rtype: :class:`mininet.node.EE`
    """
    # create static EE
    cfg = CONFIG.get_EE_params()
    cfg.update(params)
    cfg['dpid'] = self.__get_new_dpid()
    log.debug("Create static EE with name: %s" % name)
    ee = self.mn.addEE(name=name, cls=cls, **cfg)
    if 'cores' in cfg:
      ee.setCPUs(**cfg['cores'])
    if 'frac' in cfg:
      ee.setCPUFrac(**cfg['frac'])
    if 'vlanif' in cfg:
      for vif in cfg['vlaninf']:
        ee.cmdPrint('vconfig add ' + name + '-eth0 ' + vif[1])
        ee.cmdPrint('ifconfig ' + name + '-eth0.' + vif[1] + ' ' + vif[0])
    return ee

  def create_NETCONF_EE (self, name, type=TYPE_EE_LOCAL, **params):
    """
    Create and add a new EE to Mininet network.

    The type of EE can be {local|remote} NETCONF-based.

    :param name: name of the EE: switch: name, agent: agt_+'name'
    :type name: str
    :param type: type of EE {local|remote}
    :type type: str
    :param opts: additional options for the switch in EE
    :type opts: str
    :param dpid: remote switch DPID (remote only)
    :param username: NETCONF username (remote only)
    :param passwd: NETCONF password (remote only)
    :param ip: control Interface for the agent (optional)
    :param agentPort: port to listen on for NETCONF connections, (else set \
    automatically)
    :param minPort: first VNF control port which can be used (else set \
    automatically)
    :param cPort: number of VNF control ports (and VNFs) which can be used ( \
    default: 10)
    :return: tuple of newly created :class:`mininet.node.Agent` and \
    :class:`mininet.node.Switch` object
    :rtype: tuple
    """
    type = type.upper()
    cfg = CONFIG.get_EE_params()
    cfg.update(params)
    cfg['dpid'] = self.__get_new_dpid()
    if type == self.TYPE_EE_LOCAL:
      # create local NETCONF-based
      log.debug("Create local NETCONF EE with name: %s" % name)
      sw = self.mn.addSwitch(name, **cfg)
    elif type == self.TYPE_EE_REMOTE:
      # create remote NETCONF-based
      log.debug("Create remote NETCONF EE with name: %s" % name)
      cfg["inNamespace"] = False
      sw = self.mn.addRemoteSwitch(name, cls=None, **cfg)
    else:
      raise TopologyBuilderException(
        "Unsupported NETCONF-based EE type: %s!" % type)
    agt = self.mn.addAgent('agt_' + name, cls=None, **cfg)
    agt.setSwitch(sw)
    return agt, sw

  def create_Switch (self, name, cls=None, **params):
    """
    Create and add a new OF switch instance to Mininet network.

    Additional parameters are keyword arguments depend on and forwarded to
    the initiated Switch class type.

    :param name: name of switch
    :type name: str
    :param cls: custom switch class/constructor (optional)
    :type cls: :class:`mininet.node.Switch`
    :param dpid: DPID for switch (default: derived from name)
    :type dpid: str
    :param opts: additional switch options
    :type opts: str
    :param listenPort: custom listening port (optional)
    :type listenPort: int
    :param inNamespace: override the switch spawn in namespace (optional)
    :type inNamespace: bool
    :param of_ver: override OpenFlow version (optional)
    :type of_ver: int
    :param ip: set IP address for the switch (optional)
    :type ip:
    :return: newly created Switch object
    :rtype: :class:`mininet.node.Switch`
    """
    log.debug("Create Switch with name: %s" % name)
    cfg = CONFIG.get_Switch_params()
    cfg.update(params)
    cfg['dpid'] = self.__get_new_dpid()
    sw = self.mn.addSwitch(name=name, cls=cls, **cfg)
    if 'of_ver' in cfg:
      sw.setOpenFlowVersion(cfg['of_ver'])
    if 'ip' in cfg:
      sw.setSwitchIP(cfg['ip'])
    return sw

  def create_Controller (self, name, controller=None, **params):
    """
    Create and add a new OF controller to Mininet network.

    Additional parameters are keyword arguments depend on and forwarded to
    the initiated Controller class type.

    .. warning::
      Should not call this function and use the default InternalControllerProxy!

    :param name: name of controller
    :type name: str
    :param controller: custom controller class/constructor (optional)
    :type controller: :class:`mininet.node.Controller`
    :param inNamespace: override the controller spawn in namespace (optional)
    :type inNamespace: bool
    :return: newly created Controller object
    :rtype: :class:`mininet.node.Controller`
    """
    log.debug("Create Controller with name: %s" % name)
    cfg = CONFIG.get_Controller_params()
    cfg.update(params)
    return self.mn.addController(name=name, controller=controller, **cfg)

  def create_SAP (self, name, cls=None, **params):
    """
    Create and add a new SAP to Mininet network.

    Additional parameters are keyword arguments depend on and forwarded to
    the initiated Host class type.

    :param name: name of SAP
    :type name: str
    :param cls: custom hosts class/constructor (optional)
    :type cls: :class:`mininet.node.Host`
    :return: newly created Host object as the SAP
    :rtype: :class:`mininet.node.Host`
    """
    log.debug("Create SAP with name: %s" % name)
    cfg = CONFIG.get_SAP_params()
    cfg.update(params)
    return self.mn.addHost(name=name, cls=cls, **cfg)

  def bind_inter_domain_SAPs (self, nffg):
    """
    Search for inter-domain SAPs in given :any:`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: :any:`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 create_Link (self, src, dst, src_port=None, dst_port=None, **params):
    """
    Create an undirected connection between src and dst.

    Source and destination ports can be given optionally:

    :param src: source Node
    :param dst: destination Node
    :param src_port: source Port (optional)
    :param dst_port: destination Port (optional)
    :param params: additional link parameters
    :return:
    """
    log.debug("Create Link %s%s <--> %s%s" % (
      src, ":%s" % src_port if src_port is not None else "", dst,
      ":%s" % dst_port if dst_port is not None else ""))
    remote = filter(lambda n: isinstance(n, RemoteSwitch), [src, dst])
    local = filter(lambda n: not isinstance(n, RemoteSwitch), [src, dst])
    cfg = CONFIG.get_Link_params()
    cfg.update(params)
    if not remote:
      self.mn.addLink(src, dst, src_port, dst_port, **cfg)
    else:
      # sw = local[0]  # one of the local Node
      # r = remote[0]  # other Node which is the remote
      # intfName = r.params['local_intf_name']
      # r_mac = None  # unknown, r.params['remote_mac']
      # r_port = r.params['remote_port']
      # # self._debug('\tadd hw interface (%s) to node (%s)' % (intfName,
      # # sw.name))
      # # This hack avoids calling __init__ which always makeIntfPair()
      # link = Link.__new__(Link)
      # i1 = Intf(intfName, node=sw, link=link)
      # i2 = Intf(intfName, node=r, mac=r_mac, port=r_port, link=link)
      # i2.mac = r_mac  # mn runs 'ifconfig', which resets mac to None
      # link.intf1, link.intf2 = i1, i2
      raise TopologyBuilderException(
        "Remote Link creation is not supported yet!")

  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)
Example #7
0
class ESCAPENetworkBridge(object):
  """
  Internal class for representing the emulated topology.

  Represents a container class for network elements such as switches, nodes,
  execution environments, links etc. Contains network management functions
  similar to Mininet's mid-level API extended with ESCAPEv2 related capabilities

  Separate the interface using internally from original Mininet object to
  implement loose coupling and avoid changes caused by Mininet API changes
  e.g. 2.1.0 -> 2.2.0.

  Follows Bridge design pattern.
  """

  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 = []

  @property
  def network (self):
    """
    Internal network representation.

    :return: network representation
    :rtype: :class:`mininet.net.MininetWithControlNet`
    """
    return self.__mininet

  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 start_network (self):
    """
    Start network.
    """
    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 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 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 get_agent_to_switch (self, switch_name):
    """
    Return the agent to which the given switch is tided..

    :param switch_name: name of the switch
    :type switch_name: str
    :return: the agent
    :rtype: :class:`mininet.node.NetconfAgent`
    """
    for switch in self.__mininet.switches:
      if switch.name == switch_name:
        return switch.agent
    return None
Example #8
0
def netWithVNFs(netconf=False):
    "Create an empty network and add nodes to it."

    #ctl = InbandController( 'ctl', ip='192.168.123.1' )
    #ctl = InbandController( 'ctl', ip='127.0.0.1' )
    #net = MininetWithControlNet( )
    net = MininetWithControlNet(controller=Controller, autoSetMacs=True)
    #net = Mininet( controller=Controller )

    info('*** Adding controller\n')
    ctl = net.addController('c0', controller=RemoteController)
    #ctl = net.addController( 'c0' )

    #import pdb; pdb.set_trace();
    info('*** Adding hosts \n')
    h1 = net.addHost('h1')
    h2 = net.addHost('h2')

    info('*** Adding VNFs \n')

    if netconf:
        ee1 = net.addEE('ee1')
        ee1.setVNF(vnf_name='netconf')
        ee2 = net.addEE('ee2')
        ee2.setVNF(vnf_name='netconf')
        #[ exe1_sw, exe1_container ] = net.addManagedExe( 'exe1', nintf=5)
        #exe1_container.cmd = netconf.makeNetConfCmd()
    else:
        ee1 = net.addEE('ee1', cpu=0.5)
        # ee1.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')
        # ee1.setVNF(vnf_name='simpleForwarder', name=ee1.name)
        ee1.setVNF(vnf_name='headerCompressor', name=ee1.name)
        ee2 = net.addEE('ee2', cpu=0.5)
        ee2.setVNF(vnf_name='headerDecompressor', name=ee2.name)
        # ee2.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')

    info('*** Adding switches\n')
    s3 = net.addSwitch('s3')
    s4 = net.addSwitch('s4')

    info('*** Creating links\n')
    net.addLink(h1, s3)
    net.addLink(h2, s4)
    net.addLink(s3, s4)

    if netconf:
        net.addLink(exe1_sw, s3)
    else:
        net.addLink(ee1, s3)
        net.addLink(ee2, s4)

    info('*** Starting network\n')
    net.start()

    info('*** Running CLI\n')
    CLI(net)

    info('*** Stopping network')
    net.stop()
Example #9
0
def netWithVNFs(netconf=False):
    "Create an empty network and add nodes to it."

    #ctl = InbandController( 'ctl', ip='192.168.123.1' )
    #ctl = InbandController( 'ctl', ip='127.0.0.1' )
    #net = MininetWithControlNet( )
    net = MininetWithControlNet(controller=Controller, autoSetMacs=True)
    #net = Mininet( controller=Controller )

    info('*** Adding controller\n')
    ctl = net.addController('c0', controller=RemoteController)

    #import pdb; pdb.set_trace();
    info('*** Adding hosts \n')
    h1 = net.addHost('h1')
    h2 = net.addHost('h2')

    info('*** Adding VNFs \n')

    if netconf:
        ee1 = net.addEE('ee1')
        ee1.setVNF(vnf_name='netconf')
        ee2 = net.addEE('ee2')
        ee2.setVNF(vnf_name='netconf')
        #[ exe1_sw, exe1_container ] = net.addManagedExe( 'exe1', nintf=5)
        #exe1_container.cmd = netconf.makeNetConfCmd()
    else:
        ee1 = net.addEE('ee1', cpu=0.1)
        #ee1.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')
        ee1.setVNF(vnf_name='simpleForwarder',
                   device=ee1.name + '_eth1',
                   name=ee1.name)
        ee2 = net.addEE('ee2', cpu=0.1)

        #example for NAT with two ports connected to internal hosts (private addresses) and one port connected to the Internet (public address)
        device = [{
            'index': 0,
            'name': 'eth1',
            'ip1': '1.0.0.1',
            'ip2': '1.0.0.10'
        }, {
            'index': 1,
            'name': 'eth2',
            'ip1': '1.0.0.20',
            'ip2': '1.0.0.30'
        }]
        public = {'index': 2, 'name': 'eth2'}
        ee2.setVNF(vnf_name='nat', device=device, public=public)


#        ee2.setVNF(vnf_name='simpleObservationPoint', name=ee2.name)
#ee2.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')
#ee2.setVNF(vnf_name='lookbusy',
#    mem_util='5MB', cpu_util='8-20', cpu_mode='curve',
#    cpu_curve_period='5m', cpu_curve_peak='2m' )

    info('*** Adding switches\n')
    s3 = net.addSwitch('s3')
    s4 = net.addSwitch('s4')

    info('*** Creating links\n')
    net.addLink(h1, s3)
    net.addLink(h2, s4)
    net.addLink(s3, s4)

    if netconf:
        net.addLink(exe1_sw, s3)
    else:
        net.addLink(ee1, s3)
        net.addLink(ee2, s4)

    info('*** Starting network\n')
    net.start()

    info('*** Running CLI\n')
    CLI(net)

    info('*** Stopping network')
    net.stop()
Example #10
0
def netWithVNFs(netconf=False):
    "Create an empty network and add nodes to it."

    #ctl = InbandController( 'ctl', ip='192.168.123.1' )
    #ctl = InbandController( 'ctl', ip='127.0.0.1' )
    #net = MininetWithControlNet( )
    net = MininetWithControlNet(controller=Controller, autoSetMacs=True)
    #net = Mininet( controller=Controller )

    info('*** Adding controller\n')
    ctl = net.addController('c0', controller=RemoteController)

    #import pdb; pdb.set_trace();
    info('*** Adding hosts \n')
    h1 = net.addHost('h1')
    h2 = net.addHost('h2')

    info('*** Adding VNFs \n')

    if netconf:
        ee1 = net.addEE('ee1')
        ee1.setVNF(vnf_name='netconf')
        ee2 = net.addEE('ee2')
        ee2.setVNF(vnf_name='netconf')
        #[ exe1_sw, exe1_container ] = net.addManagedExe( 'exe1', nintf=5)
        #exe1_container.cmd = netconf.makeNetConfCmd()
    else:
        ee1 = net.addEE('ee1', cpu=0.1)
        #ee1.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')
        ee1.setVNF(
            vnf_name='simpleForwarder',
            device=ee1.name + '_eth1',
            name=ee1.name)
        ee2 = net.addEE('ee2', cpu=0.1)

        #example for NAT with two ports connected to internal hosts (private addresses) and one port connected to the Internet (public address)
        device = [{
            'index': 0,
            'name': 'eth1',
            'ip1': '1.0.0.1',
            'ip2': '1.0.0.10'
        }, {
            'index': 1,
            'name': 'eth2',
            'ip1': '1.0.0.20',
            'ip2': '1.0.0.30'
        }]
        public = {'index': 2, 'name': 'eth2'}
        ee2.setVNF(vnf_name='nat', device=device, public=public)


#        ee2.setVNF(vnf_name='simpleObservationPoint', name=ee2.name)
#ee2.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')
#ee2.setVNF(vnf_name='lookbusy',
#    mem_util='5MB', cpu_util='8-20', cpu_mode='curve',
#    cpu_curve_period='5m', cpu_curve_peak='2m' )

    info('*** Adding switches\n')
    s3 = net.addSwitch('s3')
    s4 = net.addSwitch('s4')

    info('*** Creating links\n')
    net.addLink(h1, s3)
    net.addLink(h2, s4)
    net.addLink(s3, s4)

    if netconf:
        net.addLink(exe1_sw, s3)
    else:
        net.addLink(ee1, s3)
        net.addLink(ee2, s4)

    info('*** Starting network\n')
    net.start()

    info('*** Running CLI\n')
    CLI(net)

    info('*** Stopping network')
    net.stop()
Example #11
0
class NetworkManagerMininet(NetworkManager, GenericEventNotifyer,
                            LoggerHelper):
    __shared_state = {}

    def __init__(self):
        'lazy init'
        self.__dict__ = self.__shared_state
        if self.__dict__:
            # already initialized
            return

        GenericEventNotifyer.__init__(self)
        self.net = None
        # Initial topo to create Mininet topology
        self.initial_topo = None
        # Network state
        self.state = NetworkManager.DOWN
        self.port_map = {}
        self.dpid = {}
        # Running topo to manage/store running topo obtained from Mininet - dummy object
        self.network = Store()
        # Active link list
        self.network.links = {}
        # Active node list
        self.network.nodes = {}
        # Queue for parallel event processing
        self.of_event_queue = []
        # Timer daemon process for periodic polling
        self.process = None
        self.vnf_manager = None
        self.last_status_poll = 0
        pox.core.core.listen_to_dependencies(self)
        # Start periodic scan
        self.periodic_scan()

    #########
    ### Mininet topology compilation
    #########

    def build_topo_network(self, network_topo, appPrefs):
        """
        Filling Mininet "topo" with app specific data
        network_topo - Topology object need to update
        appPrefs - global startup parameters
        
        No return
        """
        dpctl = None if not appPrefs['dpctl'] else int(appPrefs['dpctl'])
        #dpctl = int(appPrefs['dpctl']) if 'dpctl' in appPrefs else None
        network_topo['netopts'].update({
            'listenPort': dpctl,
            'topo': None,
            'build': False,
            'ipBase': appPrefs['ipBase'],
            'autoSetMacs': True,
            'autoStaticArp': True
        })
        self._debug('Add global parameters: %s' % network_topo['netopts'])

    def build_topo_switch(self, network_topo, name, opts, appPrefs):
        """
        Build switch object with given name and "opts" params
        network_topo - Topology object need to update
        name - switch name
        opts - switch instance params
        appPrefs - global startup parameters
        
        No return
        """
        required_keys = {'switchType'}
        if not opts.viewkeys() & required_keys:
            raise KeyError('Required argument is missing!\nCheck: ' +
                           repr(required_keys))
        switch = dict()
        switchParms = {'name': name}
        if 'dpctl' in opts:
            switchParms['listenPort'] = int(opts['dpctl'])
        if 'dpid' in opts:
            switchParms['dpid'] = opts['dpid']
        # Get switch type or default
        if opts['switchType'] == 'default':
            sw_type = appPrefs['switchType']
        else:
            sw_type = opts['switchType']
        # Get the correct switch class
        if sw_type == 'ivs':
            switchParms['cls'] = IVSSwitch
        elif sw_type == 'user':
            switchParms['cls'] = CustomUserSwitch
        elif sw_type == 'userns':
            switchParms['inNamespace'] = True
            switchParms['cls'] = CustomUserSwitch
        else:
            switchParms['cls'] = customOvs
            switch['openflowver'] = appPrefs['openFlowVersions']
        switch['controllers'] = opts.get('controllers', None)
        switch['netflow'] = opts.get('netflow', None)
        switch['sflow'] = opts.get('sflow', None)
        # Are these ifs important or switch instances can contain empty ip, extintf attributes?
        # Attach external interfaces
        if 'externalInterfaces' in opts:
            switch['extintf'] = opts['externalInterfaces']
        if 'ip' in opts:
            switch['ip'] = opts['switchIP']
        # Add new switch param and switch
        switch['params'] = switchParms
        network_topo['switches'][opts['_id']] = switch
        self._debug('Add %s Switch to mininet topo with parameters %s' %
                    (name, network_topo['switches'][opts['_id']]))

    def build_topo_ee(self, network_topo, name, opts):
        """
        Build VNF Container object with given name and "opts" params
        network_topo - Topology object need to update
        name - Container name
        opts - Container instance params
        
        No return
        """
        settings = {}
        ip = opts.get('ip', None)
        if ip: settings['ip'] = ip

        defaultRoute = opts.get('defaultRoute', None)
        if defaultRoute: settings['defaultRoute'] = 'via ' + defaultRoute

        # Create the correct host class
        hostCls = EE
        params = {
            'name': name,
            'cls': hostCls,
            'cpu': opts['res']['cpu'],
            'mem': opts['res']['mem'],
            'ee_type': opts.get('ee_type', 'static'),
        }
        for o in [
                'remote_dpid', 'remote_port', 'remote_conf_ip',
                'remote_netconf_port', 'netconf_username', 'netconf_passwd',
                'local_intf_name'
        ]:
            params[o] = opts.get(o)

        params.update(settings)
        network_topo['ee'][opts['_id']] = {'params': params}

        if False:
            # Set the CPULimitedHost specific options
            if 'cores' in opts:
                network_topo['ee'][opts['_id']]['cores'] = opts['cores']
            if 'cpu' in opts:
                network_topo['ee'][opts['_id']]['frac'] = {
                    'f': opts['res']['cpu'],
                    'sched': opts['sched']
                }

        # Attach external interfaces
        if 'externalInterfaces' in opts:
            network_topo['ee'][
                opts['_id']]['extintf'] = opts['externalInterfaces']

        vlanif = opts.get('vlanInterfaces', None)
        if vlanif:
            self._debug('Checking that OS is VLAN prepared')
            self.pathCheck('vconfig', moduleName='vlan package')
            moduleDeps(add='8021q')
            network_topo['ee'][opts['_id']]['vlanif'] = vlanif

        self._debug("Add %s EE to mininet topo with parameters %s" %
                    (name, network_topo['ee'][opts['_id']]))

    def pathCheck(self, *args, **kwargs):
        """Make sure each program in *args can be found in $PATH."""
        moduleName = kwargs.get('moduleName', 'it')
        for arg in args:
            if not quietRun('which ' + arg):
                showerror(
                    title="Error",
                    message='Cannot find required executable %s.\n' % arg +
                    'Please make sure that %s is installed ' % moduleName +
                    'and available in your $PATH.')

    def build_topo_controller(self, network_topo, name, opts):
        """
        Build Controller object with given name and "opts" params
        network_topo - Topology object need to update
        name - Controller name
        opts - Controller instance params
        
        No return
        """
        # Get controller info from panel
        controllerType = opts['controllerType']

        # Make controller
        self._info('*** Getting controller selection: %s' % controllerType)

        if controllerType == 'remote':
            c = RemoteController
        elif controllerType == 'inband':
            c = InbandController
        elif controllerType == 'ovsc':
            c = OVSController
        else:
            c = Controller
        params = {
            'name': name,
            'ip': opts['remoteIP'],
            'port': opts['remotePort'],
            'controller': c
        }
        network_topo['controllers'][opts['_id']] = {'params': params}
        self._debug("Add %s Controller(s) to mininet topo with parameters %s" %
                    (name, network_topo['controllers'][opts['_id']]))

    def build_topo_sap(self, network_topo, name, opts):
        settings = {}
        ip = opts.get('ip', None)
        if ip: settings['ip'] = ip
        # else:
        # nodeNum = canvas.startpointOpts[name]['nodeNum']
        # settings['nodeNum']= nodeNum
        # ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
        # settings['ipBaseNum'] = ipBaseNum
        # settings['prefixLen'] = prefixLen
        # ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)

        defaultRoute = opts.get('defaultRoute', None)
        if defaultRoute: settings['defaultRoute'] = 'via ' + defaultRoute

        # Create the correct host class
        hostCls = Host
        if 'cores' in opts or 'cpu' in opts:
            hostCls = CPULimitedHost

        params = {'name': name, 'cls': hostCls}
        params.update(settings)
        network_topo['saps'][opts['_id']] = {'params': params}

        # Set the CPULimitedHost specific options
        if 'cores' in opts:
            network_topo['saps'][opts['_id']]['cores'] = opts['cores']
        if 'cpu' in opts:
            network_topo['saps'][opts['_id']].update({
                'f': opts['res']['cpu'],
                'sched': opts['sched']
            })

            # Attach external interfaces
        if 'externalInterfaces' in opts:
            network_topo['saps'][opts['_id']]['extintf'] = \
            opts['externalInterfaces']

        vlanif = opts.get('vlanInterfaces', None)
        if vlanif:
            pass
            # self._debug('Checking that OS is VLAN prepared')
            # self.pathCheck('vconfig', moduleName='vlan package')
            # moduleDeps( add='8021q' )
        self._debug("Add %s SAP to mininet topo with parameters %s" %
                    (name, network_topo['saps'][opts['_id']]))

    def build_topo_links(self, network_topo, phy_g):
        for node1, node2, params in list(phy_g.edges_iter(data=True)):
            if params.get('type', None) == 'data':
                network_topo['links'][(node1, node2)] = {
                    'node1': node1,
                    'node2': node2,
                    'cls': TCLink
                }
                if 'delay' in params:
                    network_topo['links'][(node1,
                                           node2)]['delay'] = params.get(
                                               'delay', 5)
                if 'bw' in params:
                    network_topo['links'][(node1, node2)]['bw'] = params['bw']
                self._debug(
                    "Create link between %s : %s with parameters %s" %
                    (node1, node2, network_topo['links'][(node1, node2)]))

    # TODO: don't use appPrefs and canvas, references and bindings to GUI should be removed
    # check imports to eliminate unnecessary ones
    def build_topo(self, phy_g, appPrefs):
        """
        Generate and set mininet topo
        
        No return
        """
        from Utils import dump
        #dump(phy_g)
        self.initial_topo = self.generate_topo(phy_g, appPrefs)

    def generate_topo(self, phy_g, appPrefs):
        """
        Build the topology according to the GUI widget params
        appPrefs - global params: ipBase, switchType, openFlowVersions
        Return: topo object
        """
        self._info("*** Build network based on our topology.")
        # Empty topo
        network_topo = {
            'netopts': dict(),
            'ee': dict(),
            'saps': dict(),
            'switches': dict(),
            'controllers': dict(),
            'links': dict()
        }
        # Set global params
        self.build_topo_network(network_topo, appPrefs)

        # Make nodes
        self._info("*** Getting Hosts and Switches.")
        for node in phy_g.nodes():
            if phy_g.node[node]['node_type'] == NODE_TYPE['SWITCH']:
                # Adding specific switch object to "topo"
                self.build_topo_switch(network_topo, node, phy_g.node[node],
                                       appPrefs)
                # TODO: Need to handle 'LegacySwitch' ???
                # elif 'LegacySwitch' in tags:
                # opts = canvas.switchOpts[name]
                # params = {'params':{'name': name,
                # 'cls': LegacySwitch}
                # }
                # # Adding specific switch object to "topo"
                # network_topo['switches'][opts['_id']] = params
            elif phy_g.node[node]['node_type'] == NODE_TYPE['NODE']:
                # Adding specific EE object to "topo"
                self.build_topo_ee(network_topo, node, phy_g.node[node])
            elif phy_g.node[node]['node_type'] == NODE_TYPE['CONTROLLER']:
                # Adding specific controller object to "topo"
                self.build_topo_controller(network_topo, node,
                                           phy_g.node[node])
                # TODO: Need to handle 'LegacyRouter' ???
                # elif 'LegacyRouter' in tags:
                # opts = canvas.switchOpts[name]
                # params = {'params':{'name': name,
                # 'cls': LegacyRouter}
                # }
                # # Adding specific router object to "topo"
                # network_topo['hosts'][opts['_id']] = params
            elif phy_g.node[node]['node_type'] == NODE_TYPE['SAP']:
                # Adding specific SAP object to "topo"
                self.build_topo_sap(network_topo, node, phy_g.node[node])
            else:
                raise TypeError('Cannot create mystery node: ' + node)

        # Adding the links
        self._info("*** Getting Links.")
        self.build_topo_links(network_topo, phy_g)

        return network_topo

    #########
    ### Mininet topology creation
    #########

    # Use this function instead of direct access to initial_topo
    def get_initial_topology(self):
        """
        General function to return the observed topology in the NetworkX format
        """
        return self._convert_to_NetworkX_format()

    def _convert_to_NetworkX_format(self):
        """
        Convert the "topo" dictionary to NetworkX format for Orchestration module
        Keep only the relevant node information
        Return: graph - networkx.classes.graph.Graph
        @author: czentye
        """
        # Create empty graph
        graph = nx.Graph()
        # Return if topo is not set
        if not self.initial_topo:
            return graph
        from Utils import dump
        #dump(self.initial_topo, 'NetMen initial_topo')
        # Convert "controllers" to node
        controllers = self.initial_topo['controllers']
        for c in controllers:
            node_name = controllers[c]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = c
            graph.node[node_name]['node_type'] = NODE_TYPE['CONTROLLER']
            graph.node[node_name]['hostname'] = node_name

            graph.node[node_name][
                'controllerType'] = None  #TODO cut from controllers[c]['params']['controller']
            graph.node[node_name]['canvas_id'] = None

            graph.node[node_name]['remoteIP'] = controllers[c]['params']['ip']
            graph.node[node_name]['remotePort'] = controllers[c]['params'][
                'port']

        # Convert "ee" to node
        ee = self.initial_topo['ee']
        for container in ee:
            node_name = ee[container]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = container
            graph.node[node_name]['node_type'] = NODE_TYPE['NODE']
            graph.node[node_name]['hostname'] = node_name

            graph.node[node_name]['nodeNum'] = None
            graph.node[node_name]['canvas_id'] = None

            # graph.node[node_name]['ee_type'] = ee[container]['params']['ee_type']
            # graph.node[node_name]['cpu'] = ee[container]['params']['cpu']
            # graph.node[node_name]['mem'] = ee[container]['params']['mem']
            # Duplicated data, SHOULD be removed !!!
            graph.node[node_name]['res'] = {
                'cpu': ee[container]['params']['cpu'],
                'mem': ee[container]['params']['mem']
            }

        # Convert "saps" to node
        saps = self.initial_topo['saps']
        for sap in saps:
            node_name = saps[sap]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = sap
            graph.node[node_name]['node_type'] = NODE_TYPE['SAP']
            graph.node[node_name]['name'] = node_name

            graph.node[node_name]['canvas_id'] = None
            graph.node[node_name]['nodeNum'] = None

        # Convert "switches" to node
        switches = self.initial_topo['switches']
        for switch in switches:
            node_name = switches[switch]['params']['name']
            graph.add_node(node_name)
            graph.node[node_name]['_id'] = switch
            graph.node[node_name]['node_type'] = NODE_TYPE['SWITCH']
            graph.node[node_name]['hostname'] = node_name

            graph.node[node_name]['canvas_id'] = None
            graph.node[node_name]['nodeNum'] = None

            graph.node[node_name]['controllers'] = switches[switch][
                'controllers']
            graph.node[node_name]['netflow'] = switches[switch]['netflow']
            graph.node[node_name]['sflow'] = switches[switch]['sflow']
            #graph.node[node_name]['switchIP'] = ''
            graph.node[node_name][
                'switchType'] = None  #TODO cut from switches[switch]['params']['cls']

        # Convert "links" to edges
        links = self.initial_topo['links']
        for node1, node2 in links:
            options = {'weight': 1}
            if graph.node[node1]['node_type'] == 'C' or graph.node[node2][
                    'node_type'] == 'C':
                # Control links are missing from topo -> this branch is useless
                options['type'] = LINK_TYPE['CONTROL']
            else:
                if 'bw' in links[(node1, node2)]:
                    options['bw'] = links[(node1, node2)]['bw']
                options['type'] = LINK_TYPE['DATA']
                if 'delay' in links[(node1, node2)]:
                    options['delay'] = links[(node1, node2)].get('delay', 5)
            graph.add_edge(node1, node2, attr_dict=options)

        # Adding control channel links
        # TODO improve if there is multiple controller
        for node in self.initial_topo['switches']:
            graph.add_edge(
                self.initial_topo['switches'][node]['controllers'][0],
                node,
                attr_dict={
                    'type': LINK_TYPE['CONTROL'],
                    'weight': 1
                })
        return graph

    def _start_mininet(self, opts=None):
        if not opts:
            opts = {}
        self._info("***Starting mininet***")
        opts['controller'] = Controller
        opts['autoSetMacs'] = True
        self.net = MininetWithControlNet(**opts)

    def _create_ee(self, ees):
        self._info('**** Create %d execution environment(s)' % len(ees))
        for id, ee in ees.iteritems():
            params = ee['params']
            name = params['name']
            self._debug('\tCreate %s EE with params %s' % (name, ee))
            if params['ee_type'] == 'netconf':
                sw = self.net.addSwitch(name)
                agt = self.net.addAgent('agt_' + name)
                agt.setSwitch(sw)
                continue
            elif params['ee_type'] == 'remote':
                p = copy.deepcopy(params)
                p['cls'] = None
                p['inNamespace'] = False
                p['dpid'] = p['remote_dpid']
                p['username'] = p['netconf_username']
                p['passwd'] = p['netconf_passwd']
                p['conf_ip'] = p['remote_conf_ip']
                p['agentPort'] = p['remote_netconf_port']
                del p['name']
                sw = self.net.addRemoteSwitch(name, **p)
                agt = self.net.addAgent('agt_' + name, **p)
                agt.setSwitch(sw)
                continue
            else:
                # params['ee_type'] == 'static':
                # normal case
                h = self.net.addEE(**params)
                if 'cores' in ee:
                    h.setCPUs(**ee['cores'])
                if 'frac' in ee:
                    h.setCPUFrac(**ee['frac'])
                if 'vlanif' in ee:
                    for vif in ee['vlaninf']:
                        # TODO: In miniedit it was after self.net.build()
                        h.cmdPrint('vconfig add ' + name + '-eth0 ' + vif[1])
                        h.cmdPrint('ifconfig ' + name + '-eth0.' + vif[1] +
                                   ' ' + vif[0])

    def _create_switches(self, switches):
        self._info('**** Create %d switch(es)' % len(switches))
        for id, switch in switches.iteritems():
            self._debug('\tCreate %s switch with params %s' %
                        (switch['params']['name'], switch))
            sw = self.net.addSwitch(**switch['params'])
            if 'openflowver' in switch:
                sw.setOpenFlowVersion(switch['openflowver'])
            if 'ip' in switch:
                sw.setSwitchIP(switch['ip'])

    def _create_controllers(self, controllers):
        self._info('**** Create %d controller(s)' % len(controllers))
        for id, controller in controllers.iteritems():
            self._debug('\tCreate %s controller with params %s' %
                        (controller['params']['name'], controller))
            self.net.addController(**controller['params'])

    def _create_saps(self, saps):
        self._info('**** Create %d SAP(s)' % len(saps))
        for id, sap in saps.iteritems():
            self._debug('\tCreate %s SAP with params %s' %
                        (sap['params']['name'], sap))
            self.net.addHost(**sap['params'])

    def _create_links(self, links):
        def is_remote(node):
            return isinstance(node, RemoteSwitch)

        def is_local(node):
            return not is_remote(node)

        self._info('**** Create %d link(s)' % len(links))
        for id, link in links.iteritems():
            self._debug('\tCreate link %s with params: %s' % (id, link))
            node1 = self.net.get(link['node1'])
            node2 = self.net.get(link['node2'])
            name_to_node = {'node1': node1, 'node2': node2}
            link.update(name_to_node)

            remote = filter(is_remote, [node1, node2])
            local = filter(is_local, [node1, node2])
            if not remote:
                self.net.addLink(**link)
            else:
                sw = local[0]
                r = remote[0]
                intfName = r.params['local_intf_name']
                r_mac = None  # unknown, r.params['remote_mac']
                r_port = r.params['remote_port']
                self._debug('\tadd hw interface (%s) to node (%s)' %
                            (intfName, sw.name))

                # This hack avoids calling __init__ which always makeIntfPair()
                link = Link.__new__(Link)
                i1 = Intf(intfName, node=sw, link=link)
                i2 = Intf(intfName, node=r, mac=r_mac, port=r_port, link=link)
                i2.mac = r_mac  # mn runs 'ifconfig', which resets mac to None
                #
                link.intf1, link.intf2 = i1, i2

    def _start_controllers(self):
        self._info('**** Start controller(s)')
        for controller in self.net.controllers:
            controller.start()

    def _start_switches(self, switches):
        for id, switch in switches.iteritems():
            controllers = [
            ]  #with legacySwitch there is no controller in miniedit
            if switch['controllers']:
                controllers.append(self.net.get(*switch['controllers']))
            self.net.get(switch['params']['name']).start(controllers)

    def start_topo(self, **kwargs):
        """
        Start the physical topology (using Mininet)
        topo - Set and use this topology
        nflow - 
        sflow - 
        startcli - 
        
        No return
        """

        self.change_network_state(NetworkManager.STARTING)
        # Save given topo (created by MiniEdit)
        if 'topo' in kwargs:
            self.initial_topo = kwargs['topo']

        if self._is_initial_topo_empty():
            raise AttributeError("Initial topology is missing!!!")

        self._start_mininet(self.initial_topo['netopts'])
        self._create_ee(self.initial_topo['ee'])
        self._create_switches(self.initial_topo['switches'])
        self._create_controllers(self.initial_topo['controllers'])
        self._create_saps(self.initial_topo['saps'])
        self._create_links(self.initial_topo['links'])

        self.net.build()
        self.net.start()

        if 'nflow' in kwargs and kwargs['nflow'].get('nflowTarget'):
            self.start_nflow(kwargs['nflow']['nflowTarget'],
                             kwargs['nflow']['nflowTimeout'],
                             kwargs['nflow']['nflowAddId'])
        if 'sflow' in kwargs and kwargs['sflow'].get('sflowTarget'):
            self.start_sflow(kwargs['sflow']['sflowTarget'],
                             kwargs['sflow']['sflowHeader'],
                             kwargs['sflow']['sflowSampling'],
                             kwargs['sflow']['sflowPolling'])
        if 'startcli' in kwargs and kwargs['startcli'] == '1':
            self.start_cli()

        self.change_network_state(NetworkManager.UP)

    def _is_initial_topo_empty(self):
        return self.initial_topo is None or (
            'ee' in self.initial_topo and 'saps' in self.initial_topo
            and 'switches' in self.initial_topo
            and 'controller' in self.initial_topo)

    def stop_network(self):
        self.change_network_state(NetworkManager.STOPPING)
        if self.net is not None:
            self.net.stop()
        self.net = None
        self.initial_topo = None
        self.network.links = {}
        self.network.nodes = {}
        self.dpid = {}
        self.port_map = {}
        # self._debug('Cleaning up Mininet...')
        # mininet.clean.cleanup()
        # time.sleep(4)
        self.change_network_state(NetworkManager.DOWN)

    def network_alive(self):
        return self.state in [NetworkManager.UP]

    def start_sflow(self, target, header, sampling, polling):
        sflowEnabled = False
        sflowSwitches = ''
        for switch in self.initial_topo['switches'].itervalues():
            name = switch['params']['name']
            if switch.get('sflow', None) == '1':
                self._info('%s has sflow enabled' % name)
                sflowSwitches += ' -- set Bridge ' + name + ' sflow=@MiniEditSF'
                sflowEnabled = True

        if sflowEnabled:
            sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '\
                       'target=\\\"'+target+'\\\" '\
                       'header='+header+' '+ \
                       'sampling='+sampling+' '+\
                       'polling='+polling
            self._debug('sFlow command: cmd = %s%s' %
                        (sflowCmd, sflowSwitches))
            call(sflowCmd + sflowSwitches, shell=True)

        else:
            self._info('No switches with sflow')

    def start_nflow(self, nflowTarget, nflowTimeout, nflowAddId):
        nflowSwitches = ''
        nflowEnabled = False
        for switch in self.initial_topo['switches'].itervalues():
            name = switch['params']['name']
            if switch.get('netflow', None) == '1':
                self._info('%s has Netflow enabled' % name)
                nflowSwitches += ' -- set Bridge ' + name + ' netflow=@MiniEditNF'
                nflowEnabled = True

        if nflowEnabled:
            nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '\
                       'target=\\\"'+nflowTarget+'\\\" '\
                       'active-timeout='+nflowTimeout

            if nflowAddId == 1:
                nflowCmd += ' add_id_to_interface=true'
            else:
                nflowCmd += ' add_id_to_interface=false'

            self._debug('nFlowcmd = %s' % (nflowCmd + nflowSwitches))
            call(nflowCmd + nflowSwitches, shell=True)

        else:
            self._info('No switches with Netflow')

    def start_cli(self):
        CLI(self.net)

    def start_clicky(self, vnf_name):
        instances = self.vnf_manager.start_clicky(vnf_name)
        self.net.clickys += instances
        # self._debug('CLICKY: %s' % instances)

    #########
    ### Scan the Mininet network and updating the running topology
    #########

    def periodic_scan(self, wait=1):
        """Scan the Mininet topo and recall itself after a period of time (wait)"""
        self.scan_network()

        self.process = threading.Timer(wait, self.periodic_scan)
        self.process.daemon = True
        self.process.start()

    def scan_network(self, forced=False):
        self.process_event_queue()
        if not self.net or self.state == NetworkManager.STOPPING:
            return
        self.poll_netconf_agents(forced)

        checked = []
        # net -> Mininet network representation
        for name, node in self.net.items():
            checked.append(name)
            if not self.found_node(node):
                return

        deleted = []
        for name, opts in self.network.nodes.iteritems():
            if opts.get('parent'):
                # netconf-controlled vnf node, mininet doesn't know about it
                continue
            if name not in checked:
                deleted.append(name)
        if self.state != NetworkManager.STARTING:
            for node_name in deleted:
                self._debug('remove node (%s) from network table' % node_name)
                del self.network.nodes[node_name]
                # TODO: send event

        deleted = []
        for link_id, link in self.network.links.iteritems():
            node_names = [link['node1'], link['node2']]
            for node_name in node_names:
                if node_name not in self.network.nodes:
                    self._debug('delete link: %s' % node_names)
                    deleted.append(link_id)
        for link_id in deleted:
            del self.network.links[link_id]

        self._fire_dpid_update(self.dpid)
        self._fire_port_map_update(self.port_map)

    def poll_netconf_agents(self, forced=False):
        "Poll netconf agents for VNF status updates"
        poll_period = 10
        if (time.time() - self.last_status_poll < poll_period) and not forced:
            return
        for sw in self.net.switches:
            if not sw.getAgent():
                continue
            i = self.vnf_manager.get_vnf_info_on_node(sw.name)
            # self._debug('VNF_INFO: %s' % i)
            visited = []
            for vnf_name, new_opts in i.iteritems():
                if vnf_name is None:
                    continue
                new_opts['parent'] = sw.name
                visited.append(vnf_name)
                orig = self.network.nodes.get(vnf_name, {})
                old_status = orig.get('status')
                if orig == new_opts:
                    # nothing's changed
                    continue
                orig.update(new_opts)
                self.network.nodes[vnf_name] = orig
                links = new_opts.get('link', [])
                if type(links) != list:
                    links = [links]
                self.found_vnf_links(vnf_name, links)
                if old_status != new_opts['status']:
                    self._fire_vnf_update(vnf_name, new_opts['status'])
            deleted = []
            for node_name, opts in self.network.nodes.iteritems():
                # self._debug('node_name: %s  opts: %s' % (node_name, opts))
                if opts.get('parent') != sw.name:
                    continue
                if node_name not in visited:
                    deleted.append(node_name)
            for vnf_name in deleted:
                del self.network.nodes[vnf_name]
                # self._debug('vnf_name: %s' % vnf_name)
                sw = self.net.nameToNode[vnf_name]
                self.net.switches.remove(sw)
                del self.net.nameToNode[vnf_name]
                neighbours = self.port_map[vnf_name].keys()
                for n in neighbours:
                    del self.port_map[n][vnf_name]
                    # Assuming there can be only one link between n and vnf.
                    link = {
                        'node1': n,
                        'node2': vnf_name,
                        'intf1': None,
                        'intf2': None,
                        'delete': True
                    }
                    pox.core.core.raiseLater(self, LinkChange, **link)
                del self.port_map[vnf_name]
                self._fire_vnf_update(vnf_name, 'STOPPED')

        self.last_status_poll = time.time()

    def found_vnf_links(self, vnf_id, links):
        def get_intf_by_name(node, intf_name):
            for i in node.intfList():
                if str(i) == intf_name:
                    return i
            return None

        def get_or_create_intf(dev_name, obj, port):
            if port not in obj.intfs:
                # does not exist, let's create it
                return Intf(dev_name, node=obj, port=port)
            if str(obj.intfs[port]) != dev_name:
                # intf exists, but port is invalid
                return Intf(dev_name, node=obj, port=port)
            return obj.intfs[port]

        for i, link in enumerate(links):
            if int(link['sw_port']) == -1:
                # disconnected links (with port==-1) are omitted
                continue
            sw_name = link.get('sw_id', 'sw_id' + str(i))
            sw_dev = link.get('sw_dev', 'sw_dev' + str(i))
            sw_port = int(link['sw_port'])

            nf_name = vnf_id
            nf_dev = link.get('vnf_dev', 'vnf_dev' + str(i))
            nf_port = int(link['vnf_port'])
            nf_mac = link['vnf_dev_mac']

            sw_obj = self.net.getNodeByName(sw_name)
            try:
                nf_obj = self.net.getNodeByName(nf_name)
            except KeyError:
                # this is a VNF not managed by mininet, yet we have to
                # add to the mininet 'database'.  TODO: Ideally, it
                # would be a RemoteHost, but for now it is a
                # RemoteSwitch.
                nf_obj = self.net.addRemoteSwitch(nf_name, dpid="-1")
            sw_i = get_or_create_intf(sw_dev, sw_obj, sw_port)
            if nf_dev in nf_obj.intfNames():
                nf_i = get_intf_by_name(nf_obj, nf_dev)
            else:
                nf_i = Intf(nf_dev, node=nf_obj, port=nf_port, mac=nf_mac)
                nf_i.mac = nf_mac  # mn runs 'ifconfig', which resets mac to None

            self.found_link(sw_obj, nf_obj, sw_i, nf_i)

    def found_link(self, node_a, node_b, intf_a, intf_b):
        # link "A -> B" is the same as link "B -> A"
        link = [(node_a, intf_a), (node_b, intf_b)]
        link = sorted(link, key=lambda x: x[1])
        [(node1, intf1), (node2, intf2)] = link

        link_id = ''.join([intf1.name, intf2.name])
        orig_link = self.network.links.get(link_id, {})
        link = {
            'node1': node1.name,
            'node2': node2.name,
            'intf1': intf1,
            'intf2': intf2
        }
        try:
            self.port_map[node1.name][node2.name] = node1.ports[intf1]
        except KeyError:
            self.port_map[node1.name] = {node2.name: node1.ports[intf1]}

        try:
            self.port_map[node2.name][node1.name] = node2.ports[intf2]
        except KeyError:
            self.port_map[node2.name] = {node1.name: node2.ports[intf2]}

        if not cmp(orig_link, link) == 0:
            self.network.links[link_id] = link
            pox.core.core.raiseLater(self, LinkChange, **link)

    def found_node(self, node):
        orig = self.network.nodes.get(node.name, {})
        new_opts = copy.deepcopy(orig)
        new_opts['name'] = node.name
        new_opts['intf'] = {}
        for intf in node.intfList():
            new_opts['intf'][intf.name] = {
                'ip': node.IP(intf),
                'mac': node.MAC(intf),
                'port': node.ports[intf]
            }
        try:
            # taken form Node.connectionsTo
            for intf in node.intfList():
                link = intf.link
                if not intf.link:
                    continue
                node1, node2 = link.intf1.node, link.intf2.node
                if node1 == node or node2 == node:
                    self.found_link(node1, node2, link.intf1, link.intf2)
        except AttributeError:
            # network is not running
            return False

        if getattr(node, 'dpid', None):
            new_opts['dpid'] = int(node.dpid, base=16)
            self.dpid[node.name] = int(node.dpid, base=16)

        if not cmp(orig, new_opts) == 0:
            self.network.nodes[node.name] = new_opts
            pox.core.core.raiseLater(self, NodeChange, NodeChange.TYPE_DUMMY,
                                     **new_opts)
        return True

    def dpid_to_name(self, dpid):
        for name, name_dpid in self.dpid.iteritems():
            if dpid == name_dpid:
                return name
        return None

    def process_event_queue(self):
        processed = []
        for e in self.of_event_queue:
            name = self.dpid_to_name(e.dpid)
            if not name:
                continue
            e.name = name
            processed.append(e)
            self.fire(e.type, e)
        for e in processed:
            self.of_event_queue.remove(e)

    def change_network_state(self, new_state):
        if self.state == new_state:
            return
        self.state = new_state
        self._fire_network_state_change()

    #########
    ### Change Event generation and OpenFlow event handling
    #########
    def _fire_network_state_change(self):
        """Signalling the node/link params are changed"""
        event = Store()
        event.state = self.state
        self.fire('network_state_change', event)

    def _fire_dpid_update(self, dpids):
        event = Store()
        event.dpids = dpids
        self.fire('dpid_update', event)

    def _fire_port_map_update(self, port_map):
        event = Store()
        event.port_map = port_map
        self.fire('port_map_update', event)

    def _fire_switch_connection_up(self):
        pass

    def _fire_switch_connection_down(self):
        pass

    def _fire_vnf_update(self, vnf_name, status):
        m = {
            'FAILED': 'failed',
            'UP_AND_RUNNING': 'running',
            'INITIALIZING': 'starting',
            'STOPPED': 'stopped',
        }
        event = Store()
        event.name = vnf_name
        try:
            event.on_node = self.network.nodes[vnf_name]['parent']
        except KeyError:
            event.on_node = None
        event.status = m.get(status, 'failed')
        self.fire('vnf_update', event)

    def _handle_openflow_ConnectionUp(self, event):
        event.type = 'switch_connection_up'
        self.of_event_queue.append(event)

    def _handle_openflow_ConnectionDown(self, event):
        event.type = 'switch_connection_down'
        self.of_event_queue.append(event)
Example #12
0
def netWithVNFs(netconf=False):
    "Create an empty network and add nodes and VNFs to it."

    net = MininetWithControlNet(controller=Controller, autoSetMacs=True)

    info('*** Adding controller\n')
    ctl = net.addController('c0', controller=RemoteController)

    info('*** Adding hosts \n')
    h1 = net.addHost('h1')
    h2 = net.addHost('h2')

    info('*** Adding VNFs \n')

    if netconf:
        ee1 = net.addEE('ee1')
        ee1.setVNF(vnf_name='netconf')
        ee2 = net.addEE('ee2')
        ee2.setVNF(vnf_name='netconf')
    else:
        ee1 = net.addEE('ee1', cpu=0.1)
        ee1.setVNF(vnf_name='simpleForwarder', name=ee1.name)
        ee2 = net.addEE('ee2', cpu=0.1)
        #ee2.setVNF(vnf_name='fakeload', name=ee2.name, cpu='8', mem='5MB')
        ee2.setVNF(vnf_name='simpleForwarder')

    info('*** Adding switches\n')
    s3 = net.addSwitch('s3')
    s4 = net.addSwitch('s4')

    info('*** Creating links\n')
    net.addLink(h1, s3)
    net.addLink(h2, s4)
    net.addLink(s3, s4)

    if netconf:
        net.addLink(exe1_sw, s3)
    else:
        net.addLink(ee1, s3)
        net.addLink(ee2, s4)

    info('*** Starting network\n')
    net.start()

    info('*** Running CLI\n')
    CLI(net)

    info('*** Stopping network')
    net.stop()
Example #13
0
def netWithVNFs(netconf = False):
    "Create an empty network and add nodes and VNFs to it."

    net = MininetWithControlNet( controller=Controller, autoSetMacs=True )

    info( '*** Adding controller\n' )
    ctl = net.addController( 'c0' , controller=RemoteController )
 
    info( '*** Adding hosts \n' )
    h1 = net.addHost( 'h1')
    h2 = net.addHost( 'h2')

    info( '*** Adding VNFs \n' )

    if netconf:
        ee1 = net.addEE( 'ee1' )
        ee1.setVNF(vnf_name='netconf')
        ee2 = net.addEE( 'ee2' )
        ee2.setVNF(vnf_name='netconf')
    else:
        ee1 = net.addEE( 'ee1',cpu=0.1)
        ee1.setVNF(vnf_name='simpleForwarder', name=ee1.name)
        ee2 = net.addEE( 'ee2',cpu=0.1)
        #ee2.setVNF(vnf_name='fakeload', name=ee2.name, cpu='8', mem='5MB')
        ee2.setVNF(vnf_name='simpleForwarder')

    info( '*** Adding switches\n' )
    s3 = net.addSwitch( 's3' )
    s4 = net.addSwitch( 's4' )

    info( '*** Creating links\n' )
    net.addLink( h1, s3 )
    net.addLink( h2, s4 )
    net.addLink( s3, s4 )

    if netconf:
        net.addLink( exe1_sw, s3 )
    else:
        net.addLink( ee1, s3 )
        net.addLink( ee2, s4 )

    info( '*** Starting network\n' )
    net.start()

    info( '*** Running CLI\n' )
    CLI( net )

    info( '*** Stopping network' )
    net.stop()
Example #14
0
def netWithVNFs(netconf = False):
    "Create an empty network and add nodes to it."

    #ctl = InbandController( 'ctl', ip='192.168.123.1' )
    #ctl = InbandController( 'ctl', ip='127.0.0.1' )
    #net = MininetWithControlNet( )
    net = MininetWithControlNet( controller=Controller, autoSetMacs=True )
    #net = Mininet( controller=Controller )

    info( '*** Adding controller\n' )
    ctl = net.addController( 'c0' , controller=RemoteController )
    #ctl = net.addController( 'c0' )
 
    #import pdb; pdb.set_trace();
    info( '*** Adding hosts \n' )
    h1 = net.addHost( 'h1')
    h2 = net.addHost( 'h2')

    info( '*** Adding VNFs \n' )

    if netconf:
        ee1 = net.addEE( 'ee1' )
        ee1.setVNF(vnf_name='netconf')
        ee2 = net.addEE( 'ee2' )
        ee2.setVNF(vnf_name='netconf')
        #[ exe1_sw, exe1_container ] = net.addManagedExe( 'exe1', nintf=5)
        #exe1_container.cmd = netconf.makeNetConfCmd()
    else:
        ee1 = net.addEE( 'ee1', cpu=0.5)
        # ee1.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')
        # ee1.setVNF(vnf_name='simpleForwarder', name=ee1.name)
        ee1.setVNF(vnf_name='headerCompressor', name=ee1.name)
        ee2 = net.addEE( 'ee2', cpu=0.5)
        ee2.setVNF(vnf_name='headerDecompressor', name=ee2.name)
        # ee2.setVNF(vnf_name='fakeLoad', cpu='8', mem='5MB')

    info( '*** Adding switches\n' )
    s3 = net.addSwitch( 's3' )
    s4 = net.addSwitch( 's4' )

    info( '*** Creating links\n' )
    net.addLink( h1, s3 )
    net.addLink( h2, s4 )
    net.addLink( s3, s4 )

    if netconf:
        net.addLink( exe1_sw, s3 )
    else:
        net.addLink( ee1, s3 )
        net.addLink( ee2, s4 )

    info( '*** Starting network\n' )
    net.start()

    info( '*** Running CLI\n' )
    CLI( net )

    info( '*** Stopping network' )
    net.stop()