def do_core_clean_all():
    
    netmanager = NetManager()
    
    flowrules_separator = ", "
    deleted_flowrules = []
    notfound_flowrules = []
    
    logging.info("Starting cleaning database and switches...")
    external_flowrules = GraphSession().getAllExternalFlowrules()
    
    for ef in external_flowrules:
        try:            
            netmanager.deleteFlow(ef.switch_id, ef.internal_id)
            deleted_flowrules.append(ef.internal_id)
        
        except Exception as ex:
            if type(ex) is HTTPError and ex.response.status_code==404:
                #logging.debug("External flow "+ef.internal_id+" does not exist in the switch "+ef.switch_id+".")
                notfound_flowrules.append(ef.internal_id)
            else:
                logging.debug("Exception while deleting external flow "+ef.internal_id+" in the switch "+ef.switch_id+". "+ex.args[0])
    
    if len(deleted_flowrules)>0:
        logging.debug("Deleted flowrules: "+flowrules_separator.join(deleted_flowrules)+".")
    
    if len(notfound_flowrules)>0:
        logging.debug("Not found flowrules: "+flowrules_separator.join(notfound_flowrules)+".")  
    
    GraphSession().cleanAll()
    logging.info("Cleaning database and switches completed!")
    
    #ResourceDescription().updateAll()
    #ResourceDescription().saveFile()
    Messaging().PublishDomainConfig()
    logging.info("Resource description file updated!")
    
    print("Cleaning database and switches completed!")
Example #2
0
    def __init__(self, user_data):

        self.__session_id = None
        self.__print_enabled = Configuration().OO_CONSOLE_PRINT

        self.nffg = None
        self.user_data = user_data
        '''
        Fields:
         - user_data.username
         - user_data.password
         - user_data.tenant
        '''

        # NetManager
        self.NetManager = NetManager()
 def get(self):
     """
     Get the network topology
     ---
     tags:
       - network topology
     produces:
       - application/json             
     parameters:
       - name: X-Auth-Token
         in: header
         description: Authentication token
         required: true
         type: string
                 
     responses:
       200:
         description: Topology correctly retrieved
       400:
         description: Bad request                
       401:
         description: Unauthorized
       404:
         description: Graph not found
       500:
         description: Internal Error
     """            
     try :
         UserAuthentication().authenticateUserFromRESTRequest(request)
         
         ng = NetManager()
         
         return jsonify(ng.getNetworkTopology()) #self._json_response(falcon.HTTP_200, "Network topology", topology=json.dumps(ng.getNetworkTopology()))
     
     # User auth request - raised by UserAuthentication().authenticateUserFromRESTRequest
     except wrongRequest as err:
         logging.exception(err)
         return ("Bad Request", 400)
             
     # User auth credentials - raised by UserAuthentication().authenticateUserFromRESTRequest
     except unauthorizedRequest as err:
         if request.headers.get("X-Auth-User") is not None:
             logging.debug("Unauthorized access attempt from user "+request.headers.get("X-Auth-User"))
         logging.debug(err.message)
         return ("Unauthorized", 401)
             
     # User auth credentials - raised by UserAuthentication().authenticateUserFromRESTRequest
     except UserTokenExpired as err:
         logging.exception(err)
         return (err.message, 401)
     
     # No Results
     except UserNotFound as err:
         logging.exception(err)
         return ("UserNotFound", 404)
     except TenantNotFound as err:
         logging.exception(err)
         return ("TenantNotFound", 404)
     except NoResultFound as err:
         logging.exception(err)
         return ("NoResultFound", 404)
     except sessionNotFound as err:
         logging.exception(err)
         return ("sessionNotFound", 404)
     
     # Other errors
     except requests.HTTPError as err:
         logging.exception(err)
         return (str(err), 500)            
     except Exception as err:
         logging.exception(err)
         return (str(err), 500)
Example #4
0
class DO(object):
    def __init__(self, user_data):

        self.__session_id = None
        self.__print_enabled = Configuration().OO_CONSOLE_PRINT

        self.nffg = None
        self.user_data = user_data
        '''
        Fields:
         - user_data.username
         - user_data.password
         - user_data.tenant
        '''

        # NetManager
        self.NetManager = NetManager()

    def __print(self, msg):
        if self.__print_enabled:
            print(msg)

    def NFFG_Put(self, nffg):
        """
        Manage the request of NF-FG instantiation.
        """
        logging.debug("Put NF-FG: put from user " + self.user_data.username + " on tenant " + self.user_data.tenant)

        # Check if the NF-FG is already instantiated, update it and exit
        if self.NFFG_Update(nffg) is not None:
            return self.__session_id

        # Instantiate a new NF-FG
        try:
            logging.debug("Put NF-FG: instantiating a new nffg: " + nffg.getJSON(True))
            self.__session_id = GraphSession().addNFFG(nffg, self.user_data.user_id)

            # Build the Profile Graph
            self.NetManager.ProfileGraph_BuildFromNFFG(nffg)

            # Send flow rules to Network Controller
            self.__NC_FlowsInstantiation(nffg)
            logging.debug("Flow rules instantiated!")

            # TODO activate applications implementing the requested VNFs

            # TODO configure applications to match vnf involving flow rules

            logging.debug("Put NF-FG: session " + self.__session_id + " correctly instantiated!")

            GraphSession().updateStatus(self.__session_id, 'complete')

            # Update the resource description .json
            ResourceDescription().updateAll()
            ResourceDescription().saveFile()

            Messaging().PublishDomainConfig()

            return self.__session_id

        except Exception as ex:
            logging.error(ex)
            self.__NFFG_NC_deleteGraph()
            GraphSession().updateError(self.__session_id)
            raise ex

    def NFFG_Update(self, new_nffg):

        # Check and get the session id for this user-graph couple
        logging.debug(
            "Update NF-FG: check if the user " + self.user_data.user_id + " has already instantiated the graph " + new_nffg.id + ".")
        session = GraphSession().getActiveUserGraphSession(self.user_data.user_id, new_nffg.id, error_aware=True)
        if session is None:
            return None
        self.__session_id = session.session_id
        logging.debug("Update NF-FG: already instantiated, trying to update it")

        try:
            logging.debug(
                "Update NF-FG: updating session " + self.__session_id + " from user " + self.user_data.username + " on tenant " + self.user_data.tenant)
            GraphSession().updateStatus(self.__session_id, 'updating')

            # Get the old NFFG
            old_nffg = GraphSession().getNFFG(self.__session_id)
            logging.debug("Update NF-FG: the old session: " + old_nffg.getJSON())

            # Get the updated NFFG
            updated_nffg = old_nffg.diff(new_nffg)
            logging.debug("Update NF-FG: coming updates: " + updated_nffg.getJSON(True))

            # Delete useless endpoints and flowrules, from DB and Network Controller 
            self.__NFFG_NC_DeleteAndUpdate(updated_nffg)

            # Update database
            GraphSession().updateNFFG(updated_nffg, self.__session_id)

            # Send flowrules to Network Controller
            self.__NC_FlowsInstantiation(updated_nffg)
            logging.debug("Update NF-FG: session " + self.__session_id + " correctly updated!")

            GraphSession().updateStatus(self.__session_id, 'complete')

            # Update the resource description .json
            ResourceDescription().updateAll()
            ResourceDescription().saveFile()

            Messaging().PublishDomainConfig()

        except Exception as ex:
            logging.error("Update NF-FG: ", ex)
            self.__NFFG_NC_deleteGraph()
            GraphSession().updateError(self.__session_id)
            raise ex
        return self.__session_id

    def NFFG_Delete(self, nffg_id):

        session = GraphSession().getActiveUserGraphSession(self.user_data.user_id, nffg_id, error_aware=False)
        if session is None:
            raise sessionNotFound("Delete NF-FG: session not found for graph " + str(nffg_id))
        self.__session_id = session.session_id

        try:
            instantiated_nffg = GraphSession().getNFFG(self.__session_id)
            logging.debug("Delete NF-FG: [session=" + str(
                self.__session_id) + "] we are going to delete: " + instantiated_nffg.getJSON())
            self.__NFFG_NC_deleteGraph()
            logging.debug("Delete NF-FG: session " + self.__session_id + " correctly deleted!")

            # Update the resource description .json
            ResourceDescription().updateAll()
            ResourceDescription().saveFile()

            Messaging().PublishDomainConfig()

        except Exception as ex:
            logging.error("Delete NF-FG: ", ex)
            raise ex

    def NFFG_Get(self, nffg_id):
        session = GraphSession().getActiveUserGraphSession(self.user_data.user_id, nffg_id, error_aware=True)
        if session is None:
            raise sessionNotFound("Get NF-FG: session not found, for graph " + str(nffg_id))

        self.__session_id = session.session_id
        logging.debug("Getting session: " + str(self.__session_id))
        return GraphSession().getNFFG(self.__session_id).getJSON()

    def NFFG_Status(self, nffg_id):
        session = GraphSession().getActiveUserGraphSession(self.user_data.user_id, nffg_id, error_aware=False)
        if session is None:
            raise sessionNotFound("Status NF-FG: session not found, for graph " + str(nffg_id))

        self.__session_id = session.session_id
        percentage = 0

        if session.status != 'error':
            percentage = GraphSession().getFlowruleProgressionPercentage(self.__session_id, nffg_id)

        logging.debug("Status NF-FG: graph status: " + str(session.status) + " " + str(percentage) + "%")
        return session.status, percentage

    def NFFG_Validate(self, nffg):
        '''
        A validator for this specific domain orchestrator.
        The original json, as specified in the extern NFFG library,
        could contain useless objects and fields for this DO.
        If this happens, we have to raise exceptions to stop the request processing.  
        '''

        def raise_useless_info(msg):
            logging.debug("NFFG Validation: " + msg + ". This DO does not process this kind of data.")
            raise NffgUselessInformations("NFFG Validation: " + msg + ". This DO does not process this kind of data.")

        def raise_invalid_actions(msg):
            logging.debug("NFFG Validation: " + msg + ". This DO does not process this kind of flowrules.")
            raise NffgUselessInformations("NFFG Validation: " + msg +
                                          ". This DO does not process this kind of flowrules.")

        # EP Array
        EPs = {}

        '''
        The below domain could offer some network function capabilities that could be used to implement
        the VNFs of this graph. Here we check if this is possible (all VNFs of the graph could be implemented on the
        domain), else raise an error.
        '''
        # VNFs inspections
        # TODO this check is implemented through the 'template' information. I don't know if is the best approach
        print(Configuration().MSG_RESDESC_FILE)
        domain_info = DomainInfo.get_from_file(Configuration().MSG_RESDESC_FILE)
        available_functions = []
        for functional_capability in domain_info.capabilities.functional_capabilities:
            available_functions.append(functional_capability.tamplate)
        for vnf in nffg.vnfs:
            if vnf.template not in available_functions:
                raise_useless_info("The VNF '" + vnf.name + "' cannot be implemented on this domain")

        '''
        Busy VLAN ID: the control on the required vlan id(s) must wait for
        the graph instantiation into the database in order to clarify the situation.
        Finally, the the control on the required vlan id(s) is always made before
        processing a flowrule (see the first rows of "__NC_ProcessFlowrule").
        '''

        # END POINTs inspections
        for ep in nffg.end_points:
            if ep.type is not None and ep.type != "interface" and ep.type != "vlan":
                raise_useless_info("'end-points.type' must be 'interface' or 'vlan' (not '" + ep.type + "')")
            if ep.remote_endpoint_id is not None:
                raise_useless_info("presence of 'end-points.remote_endpoint_id'")
            if ep.remote_ip is not None:
                raise_useless_info("presence of 'end-points.remote-ip'")
            if ep.local_ip is not None:
                raise_useless_info("presence of 'end-points.local-ip'")
            if ep.gre_key is not None:
                raise_useless_info("presence of 'gre-key'")
            if ep.ttl is not None:
                raise_useless_info("presence of 'ttl'")
            if ep.prepare_connection_to_remote_endpoint_id is not None:
                raise_useless_info("presence of connection to remote endpoint")
            if ep.prepare_connection_to_remote_endpoint_ids is not None and len(
                    ep.prepare_connection_to_remote_endpoint_ids) > 0:
                raise_useless_info("presence of connection to remote endpoints")
            """
            # Check endpoints in ResourceDescription.json (switch/port)
            if ResourceDescription().checkEndpoint(ep.node_id, ep.interface)==False:
                raise GraphError("Endpoint "+str(ep.id)+" not found")
            """
            # Check vlan availability
            # if ep.type == "vlan" and ep.vlan_id is not None:
            #    if ResourceDescription().VlanID_isAvailable(int(ep.vlan_id), ep.node_id, ep.interface)==False:
            #        vids_list = ResourceDescription().VlanID_getAvailables_asString(ep.node_id, ep.interface)
            #        raise GraphError("Vlan ID "+str(ep.vlan_id)+" not allowed on the endpoint "+str(ep.id)+"! Valid vlan ids: "+vids_list)

            # Add the endpoint
            EPs['endpoint:' + ep.id] = {"sid": ep.node_id, "pid": ep.interface}

        # FLOW RULEs inspection
        for flowrule in nffg.flow_rules:

            if flowrule.match is None:
                GraphError("Flowrule " + flowrule.id + " has not match section")
            if flowrule.match.port_in is None:
                GraphError("Flowrule " + flowrule.id + " has not an ingress endpoint ('port_in')")
            if self.__getEndpointIdFromString(flowrule.match.port_in) is None:
                GraphError("Flowrule " + flowrule.id + " has not an ingress endpoint ('port_in')")

            # Check vlan availability
            # if flowrule.match.vlan_id is not None and ResourceDescription().VlanID_isAvailable(int(flowrule.match.vlan_id), EPs[flowrule.match.port_in]['sid'], EPs[flowrule.match.port_in]['pid'])==False:
            #    vids_list = ResourceDescription().VlanID_getAvailables_asString(ep.node_id, ep.interface)
            #    raise GraphError("Vlan ID "+str(ep.vlan_id)+" not allowed! Valid vlan ids: "+vids_list)

            # Detect multiple output actions (they are not allowed).
            # If multiple output are needed, multiple flow rules should be written
            # in the nffg.json, with a different priorities!
            output_action_counter = 0
            output_ep = None
            for a in flowrule.actions:
                if a.controller is not None and a.controller == True:
                    raise_useless_info("presence of 'output_to_controller'")
                if a.output_to_queue is not None:
                    raise_useless_info("presence of 'output_to_queue'")
                if a.output is not None:
                    if output_action_counter > 0:
                        raise_invalid_actions(
                            "Multiple 'output_to_port' not allowed (flow rule " + str(flowrule.id) + ")")
                    output_action_counter = output_action_counter + 1
                    if self.__getEndpointIdFromString(a.output) is None:
                        GraphError("Flowrule " + str(
                            flowrule.id) + " has not an egress endpoint ('output_to_port' in 'action')")
                    output_ep = a.output

            # Check vlan availability
            for a in flowrule.actions:
                if a.push_vlan is not None and ResourceDescription().VlanID_isAvailable(int(a.push_vlan),
                                                                                        EPs[output_ep]['sid'],
                                                                                        EPs[output_ep]['pid']) == False:
                    vids_list = str(Configuration().VLAN_AVAILABLE_IDS)
                    raise GraphError("Vlan ID " + str(a.push_vlan) + " not allowed! Valid vlan ids: " + vids_list)
                if a.set_vlan_id is not None and ResourceDescription().VlanID_isAvailable(int(a.set_vlan_id),
                                                                                          EPs[output_ep]['sid'],
                                                                                          EPs[output_ep][
                                                                                              'pid']) == False:
                    vids_list = str(Configuration().VLAN_AVAILABLE_IDS)
                    raise GraphError("Vlan ID " + str(a.set_vlan_id) + " not allowed! Valid vlan ids: " + vids_list)

    '''
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        NETWORK CONTROLLER INTERACTIONS
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
    '''

    def __NFFG_NC_deleteGraph(self):
        '''
        Delete a whole graph, and set it as "ended".
        Delete all endpoints, and releated resources.
        Delete all flowrules from database and from the network controller.
        '''

        # Endpoints
        endpoints = GraphSession().getEndpointsBySessionID(self.__session_id)
        if endpoints is not None:
            for ep in endpoints:
                self.__deleteEndpointByID(ep.id)

        # Flowrules (maybe will never enter)
        flowrules = GraphSession().getFlowrules(self.__session_id)
        if flowrules is not None:
            for fr in flowrules:
                self.__deleteFlowRule(fr)

        # End field
        GraphSession().updateEnded(self.__session_id)

    def __NFFG_NC_DeleteAndUpdate(self, updated_nffg):
        '''
        Remove all endpoints and all flowrules which is marked as 'to_be_deleted'.
        For each flowrule marked as 'already_deployed' this function checks if the
        releated endpoints are been updated: in this case the flowrule is deleted 
        and it is set as 'new' in order that be installed again.   
        '''
        # List of updated endpoints
        updated_endpoints = []

        # Delete the endpoints 'to_be_deleted'
        for endpoint in updated_nffg.end_points[:]:  # "[:]" keep in memory deleted items during the loop.
            if endpoint.status == 'to_be_deleted':
                self.__deleteEndpointByID(endpoint.db_id)
                updated_nffg.end_points.remove(endpoint)
            elif endpoint.status == 'new' or endpoint.status is None:
                updated_endpoints.append(endpoint.id)

        # Delete the flowrules 'to_be_deleted'
        for flowrule in updated_nffg.flow_rules[:]:  # "[:]" keep in memory deleted items during the loop.

            # Delete flowrule
            if flowrule.status == 'to_be_deleted':  # and flowrule.type != 'external':
                self.__deleteFlowRuleByGraphID(flowrule.id)
                updated_nffg.flow_rules.remove(flowrule)

            # Set flowrule as "new" when associated endpoint has been updated
            elif flowrule.status == 'already_deployed':
                ep_in = self.__getEndpointIdFromString(flowrule.match.port_in)
                if ep_in is not None and ep_in in updated_endpoints:
                    flowrule.status = 'new'
                else:
                    for a in flowrule.actions:
                        ep_out = self.__getEndpointIdFromString(a.output)
                        if ep_out is not None and ep_out in updated_endpoints:
                            flowrule.status = 'new'

    def __getEndpointIdFromString(self, endpoint_string):
        if endpoint_string is None:
            return None
        endpoint_string = str(endpoint_string)
        tmp2 = endpoint_string.split(':')
        port2_type = tmp2[0]
        port2_id = tmp2[1]
        if port2_type == 'endpoint':
            return port2_id
        return None

    def __NC_FlowsInstantiation(self, nffg):

        # [ FLOW RULEs ]
        for flowrule in self.NetManager.ProfileGraph.getFlowrules():

            # Check if this flowrule has to be installed
            if flowrule.status != 'new':
                continue

            # Get ingress endpoint
            logging.debug("port_in: " + flowrule.match.port_in)
            port_in_id = self.__getEndpointIdFromString(flowrule.match.port_in)
            logging.debug("port_in_id: " + port_in_id)
            # check if it is not an endpoint flow rule
            if port_in_id.split(':')[0] != 'endpoint':
                logging.debug("skipping '" + port_in_id.split(':')[0] + "' flow rule.")
                continue
            in_endpoint = self.NetManager.ProfileGraph.getEndpoint(port_in_id)

            # Process flow rule with VLAN
            self.__NC_ProcessFlowrule(in_endpoint, flowrule)

    def __NC_ApplicationsInstantiation(self, nffg):
        """
        Instantiate applications on the controller implementing VNFs of the NF_FG.
        The vnf are categorized in three groups:
        SWITCH: vnfs that implement a L2 bridge connecting endpoints
        DETACHED: vnfs that have flows just to/from endpoints
        ATTACHED: vnfs that have flows to/from an other vnf
        :param nffg:
        :return:
        """

        def raise_useless_info(msg):
            logging.debug("NFFG vnf emulation: " + msg + ". This DO does not process this kind of data.")
            raise NffgUselessInformations("NFFG vnf emulation: " + msg + ". This DO does not process this kind of data.")

        domain_info = DomainInfo.get_from_file(Configuration().MSG_RESDESC_FILE)

        # [ SWITCH VNFs ]
        if len(self.NetManager.ProfileGraph.getSwitchesVnfs()) != 0:
            # TODO add support to emulate a L2 switch connecting the end points
            raise_useless_info("Switch vnf not supported yet")

        # [ DETACHED VNFs ]
        for vnf in self.NetManager.ProfileGraph.getDetachedVnfs():
            # get the name of the application
            application_name = ""
            for capability in domain_info.capabilities.functional_capabilities:
                if capability.template == vnf.template:
                    application_name = capability.name
            # we just need to activate the application and to pass as configuration the interfaces
            self.__NC_ProcessDetachedVnf(application_name, vnf)

        # [ ATTACHED VNFs ]
        if len(self.NetManager.ProfileGraph.getSwitchesVnfs()) != 0:
            # TODO add support to emulate a L2 switch connecting the end points
            raise_useless_info("Attached vnf not supported yet")

    def __NC_ProcessDetachedVnf(self, application_name, vnf):
        """

        :param application_name: application implementing the vnf
        :param vnf: vnf to emulate
        :type application_name: str
        :type vnf: VNF
        """
        flows = self.NetManager.ProfileGraph.get_flows_from_vnf()
        vnf_port_map = {}

        # get endpoints attached to vnf ports
        for flow in flows:
            for action in flow.actions:
                if action.output is not None:
                    vnf_port_map[flow.match.port_in.split(':')[2] + flow.match.port_in.split(':')[3]] = action.output

        # get interface names for endpoints
        for vnf_port in vnf_port_map:
            endpoint = self.NetManager.ProfileGraph.getEndpoint(vnf_port_map[vnf_port])
            vnf_port_map[vnf_port] = endpoint.interface

        self.NetManager.activate_app(application_name)
        # TODO push configuration to set ports

    def __NC_ProcessFlowrule(self, in_endpoint, flowrule):
        '''
        in_endpoint = nffg.EndPoint
        flowrule = nffg.FlowRule
        
        Process a flow rule written in the section "big switch" of a nffg json.
        Add a vlan match/mod/strip to every flowrule in order to distinguish it.
        After the verification that output is an endpoint, this function manages
        three main cases:
            1) endpoints are on the same switch;
            2) endpoints are on different switches, so search for a path.
        '''

        # Endpoint.type = VLAN ...overwrites the match on vlan_id
        if in_endpoint.type == "vlan":
            flowrule.match.vlan_id = in_endpoint.vlan_id

        self.__NC_CheckFlowruleOnEndpoint(in_endpoint, flowrule)

        out_endpoint = None

        # Search for a "drop" action.
        # Install immediately the flow rule, and return.
        # If a flow rule has a drop action, we don't care of other actions!
        for a in flowrule.actions:
            if a.drop is True:
                single_efr = self.NetManager.externalFlowrule(nffg_match=flowrule.match, priority=flowrule.priority,
                                                              flow_id=flowrule.id, nffg_flowrule=flowrule)
                single_efr.setInOut(in_endpoint.node_id, a, in_endpoint.interface, None, "1")
                self.__Push_externalFlowrule(single_efr)
                return

        # Search for the output endpoint
        for a in flowrule.actions:
            if a.output is not None:
                port2_id = self.__getEndpointIdFromString(a.output)  # Is the 'output' destination an endpoint?
                if port2_id is not None:
                    out_endpoint = self.NetManager.ProfileGraph.getEndpoint(
                        port2_id)  # Endpoint object (declared in resources.py)
                    break

        # Out Endpoint not valid
        if out_endpoint is None:
            raise GraphError("Flowrule " + flowrule.id + " has an invalid egress endpoint")

        # [ 1 ] Endpoints are on the same switch
        if in_endpoint.node_id == out_endpoint.node_id:
            logging.debug("Endpoint are on the same switch.")
            # Error: endpoints are equal!
            if in_endpoint.interface == out_endpoint.interface:
                raise GraphError("Flowrule " + flowrule.id + " is wrong: endpoints are overlapping")

            # 'Single-switch' path
            self.__NC_LinkEndpointsByVlanID([in_endpoint.node_id], in_endpoint, out_endpoint, flowrule)
            return

        # [ 2 ] Endpoints are on different switches...search for a path!
        logging.debug("Endpoint are on different switches, finding a path...")
        nodes_path = self.NetManager.getShortestPath(in_endpoint.node_id, out_endpoint.node_id)
        if (nodes_path is not None):

            logging.info(
                "Found a path between " + in_endpoint.node_id + " and " + out_endpoint.node_id + ". " + "Path Length = " + str(
                    len(nodes_path)))
            if self.__NC_checkEndpointsOnPath(nodes_path, in_endpoint, out_endpoint) == False:
                logging.debug("Invalid link between the endpoints")
                return
            self.__NC_LinkEndpointsByVlanID(nodes_path, in_endpoint, out_endpoint, flowrule)
            return

        # [ 3 ] No paths between the endpoints 
        logging.debug("Cannot find a link between " + in_endpoint.node_id + " and " + out_endpoint.node_id)
        return

    def __NC_CheckFlowruleOnEndpoint(self, in_endpoint, flowrule):
        '''
        Check if the flowrule can be installed on the ingress endpoint.
        '''

        # Is the endpoint enabled?
        if GraphSession().isDirectEndpoint(in_endpoint.interface, in_endpoint.node_id):
            raise GraphError("The ingress endpoint " + in_endpoint.id + " is a busy direct endpoint")

        # Flowrule collision
        qref = GraphSession().getFlowruleOnTheSwitch(in_endpoint.node_id, in_endpoint.interface, flowrule)
        if qref is not None:
            raise GraphError(
                "Flowrule " + flowrule.id + " collides with an another flowrule on the ingress port (ingress endpoint " + in_endpoint.id + ").")

    def __NC_checkEndpointsOnPath(self, path, ep1, ep2):
        if len(path) < 2:
            return None
        # check if ep1 stays on the link
        if ep1.interface == self.NetManager.switchPortIn(path[0], path[1]):
            logging.debug(
                "...path not valid: endpoint " + ep1.node_id + " port:" + ep1.interface + " stay on the link!")
            return False
        # check if ep2 stays on the link
        path_last = len(path) - 1
        if ep2.interface == self.NetManager.switchPortIn(path[path_last], path[path_last - 1]):
            logging.debug(
                "...path not valid: endpoint " + ep2.node_id + " port:" + ep2.interface + " stay on the link!")
            return False
        return True

    def __NC_LinkEndpointsByVlanID(self, path, epIN, epOUT, flowrule):
        ''' 
        This function links two endpoints with a set of flow rules pushed in
        all the intermediate switches (and in first and last switches, of course).
        
        The link between this endpoints is based on vlan id.
        If no ingress (or egress) vlan id is specified, a suitable vlan id will be chosen.
        In any case, all the vlan ids will be checked in order to avoid possible 
        conflicts in the traversed switches.
        '''

        efr = self.NetManager.externalFlowrule(flow_id=flowrule.id, priority=flowrule.priority, nffg_flowrule=flowrule)

        base_actions = []
        vlan_out = None
        original_vlan_out = None
        vlan_in = None
        pop_vlan_flag = False

        # Initialize vlan_id and save it
        if flowrule.match.vlan_id is not None:
            vlan_in = flowrule.match.vlan_id
            original_vlan_out = vlan_in

        # Clean actions, search for an egress vlan id and pop vlan action
        for a in flowrule.actions:

            # [PUSH VLAN (ID)] Store the VLAN ID and remove the action
            if a.push_vlan is not None:
                vlan_out = a.push_vlan
                original_vlan_out = a.push_vlan
                continue

            # [SET VLAN ID] Store the VLAN ID and remove the action
            if a.set_vlan_id is not None:
                vlan_out = a.set_vlan_id
                original_vlan_out = a.set_vlan_id
                continue

            # [POP VLAN] Set the flag and remove the action
            if a.pop_vlan is not None and a.pop_vlan == True:
                pop_vlan_flag = True
                continue

            # Filter non OUTPUT actions
            if a.output is None:
                base_actions.append(copy.copy(a))

        ''' Remember to pop vlan header by the last switch.
            If vlan out is not None, a pushvlan/setvlan action is present, and popvlan action is incompatible.
            Otherwise, if vlan in is None, a vlan header will be pushed by the first switch,
            so it will have to be removed by the last switch.
            This flag is also set to True when a "pop-vlan" action and a vlan match are present. 
        '''
        pop_vlan_flag = (vlan_out is None) and (pop_vlan_flag or vlan_in is None)

        # [PATH] Traverse the path and create the flow for each switch
        logging.debug("Creating the flow for each switch")
        logging.debug("Path: " + str(path))
        for i in range(0, len(path)):
            logging.debug("index in path: " + str(i))
            hop = path[i]
            efr.set_flow_name(i)
            efr.set_actions(None)  # efr.set_actions(list(base_actions))

            # Switch position
            pos = 0  # (-2: 'single-switch' path, -1:first, 0:middle, 1:last)

            # Next switch and next ingress port
            next_switch_ID = None
            next_switch_portIn = None
            if i < (len(path) - 1):
                next_switch_ID = path[i + 1]
                next_switch_portIn = self.NetManager.switchPortIn(next_switch_ID, hop)

            # First switch
            if i == 0:
                pos = -1
                logging.debug("setting switch_id: " + epIN.node_id)
                efr.set_switch_id(epIN.node_id)
                port_in = self.NetManager.getPortByInterface(epIN.node_id, epIN.interface)
                port_out = self.NetManager.switchPortOut(hop, next_switch_ID)
                if port_out is None and len(path) == 1:  # 'single-switch' path
                    pos = -2
                    port_out = self.NetManager.getPortByInterface(epOUT.node_id, epOUT.interface)

            # Last switch
            elif i == len(path) - 1:
                pos = 1
                logging.debug("setting switch_id: " + epOUT.node_id)
                efr.set_switch_id(epOUT.node_id)
                port_in = self.NetManager.switchPortIn(hop, path[i - 1])
                port_out = self.NetManager.getPortByInterface(epOUT.node_id, epOUT.interface)
                # Add actions
                efr.set_actions(list(base_actions))

                # Force the vlan out to be equal to the original
                if pop_vlan_flag == False and original_vlan_out is not None:
                    vlan_out = original_vlan_out

            # Middle way switch
            else:
                efr.set_switch_id(hop)
                port_in = self.NetManager.switchPortIn(hop, path[i - 1])
                port_out = self.NetManager.switchPortOut(hop, next_switch_ID)

            # Vlan egress endpoint ...set the vlan_id
            if epOUT.type == 'vlan' and (pos == 1 or pos == -2):
                pop_vlan_flag = False
                vlan_out = epOUT.vlan_id

            # Check, generate and set vlan ids
            vlan_out, set_vlan_out = self.__checkAndSetVlanIDs(next_switch_ID, next_switch_portIn, flowrule.match,
                                                               vlan_out)

            # MATCH           
            base_nffg_match = copy.copy(flowrule.match)

            # VLAN In
            if vlan_in is not None:
                base_nffg_match.vlan_id = vlan_in

            # ACTIONS

            # Remove VLAN header
            if pop_vlan_flag and (pos == 1 or pos == -2):  # 1=last switch; -2='single-switch' path
                efr.append_action(NffgAction(pop_vlan=True))

            # Add/mod VLAN header
            if set_vlan_out is not None:

                # If there is a match rule on vlan id, it means a vlan header 
                # it is already present and we do not need to push a vlan.
                if vlan_in is None:
                    efr.append_action(NffgAction(push_vlan=True))
                    efr.append_action(NffgAction(set_vlan_id=set_vlan_out))

            # Set next ingress vlan
            vlan_in = vlan_out

            # Push the flow rule
            base_nffg_match.port_in = port_in
            efr.set_match(base_nffg_match)
            efr.append_action(NffgAction(output=port_out))
            self.__Push_externalFlowrule(efr)
        # end-for
        '''
        Note#1 - Where are the original vlan id?
            vlan in = flowrule.match.vlan_id
            vlan out = original_vlan_out
        '''
        return

    def __checkAndSetVlanIDs(self, switch_id, port_in, nffg_match, vlan_in=None):
        '''
        Receives the main parameters for a "vlan based" flow rule.
        Check all vlan ids on the specified ports of current switch and the next switch.
        If a similar "vlan based" flow rule exists, new vlan in/out will be chosen.
        This function make other some checks to verify the correctness of all parameters.
        
        '''
        # New Egress VLAN ID
        free_vlan_id = None
        if switch_id is not None:
            free_vlan_id = self.__getFreeVlanOnSwitch(switch_id, port_in, nffg_match, vlan_in)
            if free_vlan_id is None:
                raise GraphError("No free vlan ids on the switch " + switch_id)
            if free_vlan_id == vlan_in:
                free_vlan_id = None
        else:
            free_vlan_id = vlan_in

        previous_vlan_out = vlan_in
        set_previous_vlan_out = free_vlan_id
        if set_previous_vlan_out is not None:
            previous_vlan_out = set_previous_vlan_out

        return previous_vlan_out, set_previous_vlan_out

    def __getFreeVlanOnSwitch(self, switch_id, port_in, nffg_match, vlan_in=None):
        busy_vlan_ids = GraphSession().getBusyVlanInOnTheSwitch(switch_id, port_in, nffg_match)

        if vlan_in is not None and vlan_in not in busy_vlan_ids:
            return vlan_in

        # Select first valid VLAN ID
        for vid_range in Configuration().ALLOWED_VLANS:
            vid = vid_range[0]
            while vid < vid_range[1]:
                if vid not in busy_vlan_ids:
                    return vid
                vid = vid + 1
        return None

    '''
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        DATABASE INTERACTIONS
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
    '''

    def __deleteFlowRuleByGraphID(self, graph_flow_rule_id):
        flowrules = GraphSession().getFlowrules(self.__session_id, graph_flow_rule_id)
        if flowrules is not None:
            for fr in flowrules:
                self.__deleteFlowRule(fr)

    def __deleteFlowRuleByID(self, flow_rule_id):
        fr = GraphSession().getFlowruleByID(flow_rule_id)
        if fr is None:
            return
        if fr.internal_id is not None and fr.type is not None:
            self.__deleteFlowRule(fr)
        self.__deleteFlowRuleByGraphID(fr.graph_flow_rule_id)

    # Database + Controller
    def __deleteFlowRule(self, flow_rule_ref):
        # flow_rule_ref is a FlowRuleModel object
        if flow_rule_ref.type == 'external':  # and flow.status == "complete"
            try:
                # PRINT
                self.__print(
                    "[Remove Flow] id:'" + flow_rule_ref.internal_id + "' device:'" + flow_rule_ref.switch_id + "'")

                # RESOURCE DESCRIPTION
                ResourceDescription().delete_flowrule(flow_rule_ref.id)

                # CONTROLLER
                self.NetManager.deleteFlow(flow_rule_ref.switch_id, flow_rule_ref.internal_id)
            except Exception as ex:
                if type(ex) is HTTPError and ex.response.status_code == 404:
                    logging.debug(
                        "External flow " + flow_rule_ref.internal_id + " does not exist in the switch " + flow_rule_ref.switch_id + ".")
                else:
                    logging.debug(
                        "Exception while deleting external flow " + flow_rule_ref.internal_id + " in the switch " + flow_rule_ref.switch_id + ". ")
                    raise ex
        GraphSession().deleteFlowruleByID(flow_rule_ref.id)

    def __deletePortByID(self, port_id):
        GraphSession().deletePort(port_id, self.__session_id)

    def __deleteEndpointByGraphID(self, graph_endpoint_id):
        ep = GraphSession().getEndpointByGraphID(graph_endpoint_id, self.__session_id)
        if ep is not None:
            self.__deleteEndpointByID(ep.id)

    def __deleteEndpointByID(self, endpoint_id):
        ep_resources = GraphSession().getEndpointResourcesByEndpointID(endpoint_id)
        if ep_resources is None:
            return
        for eprs in ep_resources:
            if eprs.resource_type == 'flow-rule':
                self.__deleteFlowRuleByID(eprs.resource_id)
            elif eprs.resource_type == 'port':
                self.__deletePortByID(eprs.resource_id)
        GraphSession().deleteEndpointByID(endpoint_id)

    '''
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        EXTERNAL FLOWRULE
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
    '''

    def __Push_externalFlowrule(self, efr):
        # efr = NetManager.externalFlowrule
        '''
        This is the only function that should be used to push an external flow
        (a "custom flow", in other words) in the database and in the network controller.
        GraphSession().addFlowrule(...) is also used in GraphSession().updateNFFG 
        and in GraphSession().addNFFG to store the flow rules written in nffg.json.
        '''

        nffg_match = efr.getNffgMatch()
        nffg_actions = efr.getNffgAction()
        nffg_flowrule = NffgFlowrule(match=nffg_match, actions=nffg_actions)

        '''
        Check if exists a flowrule with the same match criteria in the same switch (very rare event);
        If it exists, raise an exception!
        Similar flow rules are replaced by ovs switch, so one of them disappear!
        '''
        qref = GraphSession().getFlowruleOnTheSwitch(efr.get_switch_id(), nffg_match.port_in, nffg_flowrule)
        if qref is not None:
            raise GraphError(
                "Cannot install the flowrule " + efr.get_flow_name() + ". Collision on switch " + efr.get_switch_id() + " .")

        # If the flow name already exists, get new one
        self.__checkFlowname_externalFlowrule(efr)

        # NC/Switch: Add flow rule
        sw_flow_name = self.NetManager.createFlow(efr)  # efr.get_flow_name()
        sw_flow_name = self.NetManager.createFlow(efr)  # efr.get_flow_name()

        # DATABASE: Add flow rule
        flow_rule = NffgFlowrule(_id=efr.get_flow_id(), node_id=efr.get_switch_id(), _type='external',
                                 status='complete',
                                 priority=efr.get_priority(), internal_id=sw_flow_name)
        flow_rule_db_id = GraphSession().addFlowrule(self.__session_id, efr.get_switch_id(), flow_rule)
        GraphSession().dbStoreMatch(nffg_match, flow_rule_db_id, flow_rule_db_id)
        GraphSession().dbStoreAction(nffg_actions, flow_rule_db_id)

        # RESOURCE DESCRIPTION
        ResourceDescription().new_flowrule(flow_rule_db_id)

        # PRINT
        self.__print("[New Flow] id:'" + efr.get_flow_name() + "' device:'" + efr.get_switch_id() + "'")

    def __checkFlowname_externalFlowrule(self, efr):
        '''
        Check if the flow name already exists on the same switch,
        in order to avoid subscribing the existing flowrule in one switch.
        '''
        if GraphSession().externalFlowruleExists(efr.get_switch_id(), efr.get_flow_name()) == False:
            return

        efr.set_flow_name(0)
        this_efr = self.NetManager.externalFlowrule()

        flow_rules_ref = GraphSession().getExternalFlowrulesByGraphFlowruleID(efr.get_switch_id(), efr.get_flow_id())
        for fr in flow_rules_ref:
            if fr.type != 'external' or fr.internal_id is None:
                continue

            this_efr.set_complete_flow_name(fr.internal_id)

            if this_efr.compare_flow_name(
                    efr.get_flow_name()) < 2:  # [ ( this_efr.flow_name - prev_efr.flow_name ) < 2 ]
                efr.set_complete_flow_name(fr.internal_id)
                continue
            break
        efr.inc_flow_name()
from do_core.config import Configuration

# NetManager
from do_core.netmanager import NetManager

# Configuration
conf = Configuration()
conf.log_configuration()


# START NETWORK CONTROLLER DOMAIN ORCHESTRATOR
logging.debug("Printing the network topology watched by Network Controller Domain Orchestrator")
print("Network Controller Domain Orchestrator - Network Topology")


ng = NetManager()
nt = ng.getNetworkTopology()

print("\nNetwork Controller: "+ng.getControllerName()+"\n")

for node in nt:
    print("\n  Node:        "+node['node'])
    print("--------------------------------------------------")
    print("  neighbours:", end="")
    for nb in node['neighbours']:
        print("  "+nb)
        print("             ",end="")
    print("")