Exemplo n.º 1
0
 def show_details_client_type(self, client_type):
     '''
     Display the details of a Tunnel given a client_type
     '''
     try:
         vnid_hash = self.DVG_Hash[client_type]
         vnid_hash_ip = self.VNID_Hash_IP[client_type]
         ip_hash_vnid = self.IP_Hash_VNID[client_type]
         client_type_string = DpsClientType.types[client_type]
         print 'Tunnel Type %s\r'%client_type_string
         print 'VNIDS %s\r'%vnid_hash.keys()
         for vnid, vnid_ips in vnid_hash_ip.items():
             ip_list = IPAddressList(socket.AF_INET)
             for vnid_ip in vnid_ips:
                 try:
                     ip_list.add(socket.AF_INET, vnid_ip)
                 except Exception:
                     pass
             print 'VNID %s, PIPv4s %s\r'%(vnid, ip_list.toString())
         for ip, ip_vnids in ip_hash_vnid.items():
             try:
                 ip_packed = struct.pack(IPAddressList.fmts[socket.AF_INET], ip)
                 ip_string = socket.inet_ntop(socket.AF_INET, ip_packed)
             except Exception:
                 pass
             print 'PIPv4 %s, VNIDs %s\r'%(ip_string, ip_vnids.keys())
     except Exception:
         pass
     return
Exemplo n.º 2
0
 def show_details_client_type(self, client_type):
     '''
     Display the details of a Tunnel given a client_type
     '''
     try:
         vnid_hash = self.DVG_Hash[client_type]
         vnid_hash_ip = self.VNID_Hash_IP[client_type]
         ip_hash_vnid = self.IP_Hash_VNID[client_type]
         client_type_string = DpsClientType.types[client_type]
         print 'Tunnel Type %s\r' % client_type_string
         print 'VNIDS %s\r' % vnid_hash.keys()
         for vnid, vnid_ips in vnid_hash_ip.items():
             ip_list = IPAddressList(socket.AF_INET)
             for vnid_ip in vnid_ips:
                 try:
                     ip_list.add(socket.AF_INET, vnid_ip)
                 except Exception:
                     pass
             print 'VNID %s, PIPv4s %s\r' % (vnid, ip_list.toString())
         for ip, ip_vnids in ip_hash_vnid.items():
             try:
                 ip_packed = struct.pack(IPAddressList.fmts[socket.AF_INET],
                                         ip)
                 ip_string = socket.inet_ntop(socket.AF_INET, ip_packed)
             except Exception:
                 pass
             print 'PIPv4 %s, VNIDs %s\r' % (ip_string, ip_vnids.keys())
     except Exception:
         pass
     return
Exemplo n.º 3
0
class TunnelEndpoint:
    '''
    This represents a DOVE Underlay Tunnel in the DOVE environment -
    i.e DOVE Switch or VLAN Gateway.
    NOTE: Each Tunnel Endpoint will have multiple instances every every Domain
          it hosts Endpoints on. In other words if a TunnelEndpoint hosts
          endpoints on Domain A and Domain B, then there will be 2
          independent instances of that Tunnel Endpoint, 1 in Domain A and 1
          in Domain B.
    '''

    def __init__(self, fregister, domain, dvg, client_type, transaction_type, dps_client, pip_tuple_list):
        '''
        Constructor:
        This routine assumes that the 1st IP address in the tuple uniquely
        identifies a DOVE Switch/VLAN Gateway within the Domain.
        @param fregister: Whether this is an explicit tunnel register
        @param fregister: Boolean
        @param domain: The Domain object
        @type domain: Domain
        @param dvg: The DVG/VNID object
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param transaction_type: The Type of Transaction: should be in Domain.transaction_types
        @type transaction_type: Integer
        @param dps_client: The DPS Client associated with this Tunnel
        @type dps_client: DPSClient
        @type dps_client_port: Integer
        @param pip_tuple_array: A list of tuples of Physical IP Addresses, each 
                                tuple is of the form (inet_type, ip_value).
                                The inet_type = socket type i.e. AF_INET or AF_INET6
                                The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_array: List of tuples
        '''
        if len(pip_tuple_list) == 0:
            raise Exception('No IP Address Provided: Invalid DOVE Switch')
        try:
            client_string = DpsClientType.types[client_type]
        except Exception:
            raise Exception('Not a supported Tunnel Type')
        self.domain = domain
        #DPS Client Location i.e. the UDP Port and Location
        self.dps_client = dps_client
        #############################################################
        #Based on Object Model Chapter 5.3: DOVE Switch
        #############################################################
        #TODO: SQL Table of Policies: Requirement 1.3
        #Hash List of all Endpoints hashed by MAC: Requirement 1.4
        self.Endpoint_Hash_MAC = {}
        #A dictionary indexed by VNID IDs, the value is the number
        #of Endpoints on that DOVE Switch in a particular VNID/DVG
        self.DVG_Hash = {}
        inet_type = pip_tuple_list[0][0]
        ip_value = pip_tuple_list[0][1]
        #ip_location = IPAddressLocation(inet_type, ip_value, 0)
        #print 'Tunnel %s created DVG_Hash\r'%ip_location.show_ip()
        for ctype in DpsClientType.types.keys():
            self.DVG_Hash[ctype] = {}
        #Key IP_value: Value VNID
        self.IP_Hash_VNID = {}
        #print 'Tunnel %s created IP_Hash_VNID\r'%ip_location.show_ip()
        for ctype in DpsClientType.types.keys():
            self.IP_Hash_VNID[ctype] = {}
        #Key VNID: Value Hash of IPs
        self.VNID_Hash_IP = {}
        for ctype in DpsClientType.types.keys():
            self.VNID_Hash_IP[ctype] = {}
        #print 'Tunnel %s created VNID_Hash_IP\r'%ip_location.show_ip()
        #List of Physical IP Addresses
        self.ip_listv4 = IPAddressList(socket.AF_INET)
        self.ip_listv6 = IPAddressList(socket.AF_INET6)
        for i in range(len(pip_tuple_list)):
            inet_type = pip_tuple_list[i][0]
            ip_value = pip_tuple_list[i][1]
            self.ip_add(dvg, fregister, client_type, inet_type, ip_value, transaction_type)
        #############################################################
        #To be finished
        #############################################################
        #dcs_object.__init__(self, self.primary_ip().show())
        self.version = 0
        self.valid = True
        DPSClientHost.Host_Add(domain, dps_client.location.inet_type, 
                               dps_client.location.ip_value, dps_client.location.port, 
                               client_type)
        #print 'Tunnel %s created\r'%ip_location.show_ip()

    def endpoint_add(self, endpoint, transaction_type):
        '''
        Adds an Endpoint to the DOVE Tunnel
        @param transaction_type: The Type of Transaction: should be in Domain.transaction_types
        @type transaction_type: Integer
        '''
        #log.info('Tunnel.endpoint_add')
        #self.show(None)
        self.Endpoint_Hash_MAC[endpoint.vMac] = endpoint
        #self.show()
        return

    def endpoint_del(self, endpoint):
        '''
        Deletes an Endpoint from the DOVE Tunnel Endpoint
        '''
        try:
            del self.Endpoint_Hash_MAC[endpoint.vMac]
        except Exception:
            pass
        #self.show()
        if self.domain.unique_id == 0:
            #Remove the Tunnel Endpoint if no other Endpoints exist for Domain 0
            self.delete_if_empty()
        return

    def ip_add(self, dvg, fregister, client_type, inet_type, ip_value, transaction_type):
        '''
        This adds a tunnel IP address
        @param dvg: The DVG on which this registration occurred
        @type dvg: DVG
        @param fregister: Whether this is an explicit tunnel register
        @param fregister: Boolean
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param inet_type: AF_INET or AF_INET6
        @type inet_type: Integer
        @param ip_value: The IP address value
        @type ip_value: Integer or String
        @param transaction_type: The Type of Transaction: should be in DpsTransactionType
        @type transaction_type: Integer
        '''
        while True:
            try:
                client_type_value = DpsClientType.types[client_type]
            except Exception:
                break
            self.DVG_Hash[client_type][dvg.unique_id] = dvg
            #Add to Tunnels List even though it may have come through other DVGs as well
            if inet_type == socket.AF_INET:
                if not self.ip_listv4.search(ip_value):
                    self.ip_listv4.add(inet_type, ip_value)
            elif inet_type == socket.AF_INET6:
                if not self.ip_listv4.search(ip_value):
                    self.ip_listv6.add(inet_type, ip_value)
            #log.info('ip_add: Adding to DPS Client %s', ip_value)
            #Add DVG to self.
            #Add IP to DPS Client
            DPSClient.tunnel_endpoint_add_IP(self.dps_client, self, inet_type, ip_value)
            #Add IP to this DVGs
            DVG.tunnel_endpoint_add_IP(dvg, fregister, self, client_type, inet_type, ip_value, transaction_type)
            try:
                vnid_hash_ip = self.VNID_Hash_IP[client_type][dvg.unique_id]
            except Exception:
                self.VNID_Hash_IP[client_type][dvg.unique_id] = {}
                vnid_hash_ip = self.VNID_Hash_IP[client_type][dvg.unique_id]
            vnid_hash_ip[ip_value] = True
            #Add DVGs to IP hash
            try:
                ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
            except Exception:
                self.IP_Hash_VNID[client_type][ip_value] = {}
                ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
            ip_hash_vnid[dvg.unique_id] = True 
            #Add IP to Domain
            #log.info('ip_add: Adding to domain %s', self.domain.unique_id)
            Domain.tunnel_endpoint_add_IP(self.domain, self, inet_type, ip_value)
            break
        return

    def ip_delete(self, dvg, client_type, inet_type, ip_value):
        '''
        This deletes a tunnel IP address from a DVG and client type
        @param dvg: The DVG on which this registration occurred
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param inet_type: AF_INET or AF_INET6
        @type inet_type: Integer
        @param ip_value: The IP address value
        @type ip_value: Integer or String
        '''
        fdeleteVNID = False
        fdeleteIP = False
        while True:
            try:
                client_type_value = DpsClientType.types[client_type]
            except Exception:
                break
            try:
                vnid_hash_ip = self.VNID_Hash_IP[client_type][dvg.unique_id]
                del vnid_hash_ip[ip_value]
                if len(vnid_hash_ip) == 0:
                    del self.VNID_Hash_IP[client_type][dvg.unique_id]
                    fdeleteVNID = True
            except Exception:
                pass
            try:
                ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
                del ip_hash_vnid[dvg.unique_id]
                if len(ip_hash_vnid) == 0:
                    del self.IP_Hash_VNID[client_type][ip_value]
                    fdeleteIP = True
            except Exception:
                pass
            if fdeleteVNID:
                #Remove Tunnel from DVG
                DVG.tunnel_endpoint_delete(dvg, self, client_type)
                try:
                    del self.DVG_Hash[client_type][dvg.unique_id]
                    if len(self.DVG_Hash[client_type]) == 0:
                        #print 'Delete all Endpoints with client_type %s\r'%client_type_value
                        self.delete_client_type(client_type)
                except Exception:
                    pass
            if not fdeleteIP:
                break
            if fdeleteIP:
                #Remove only the IP from the DVG
                DVG.tunnel_endpoint_delete_IP(dvg, client_type, inet_type, ip_value)
            #Check if IP is present in any client type
            ip_present = False
            for ctype in self.IP_Hash_VNID.keys():
                try:
                    ip_hash_vnid = self.IP_Hash_VNID[ctype][ip_value]
                    ip_present = True
                    break
                except Exception:
                    continue
            if ip_present:
                break
            #Actually delete the IP from Domain and DPS Clients since no DVGs have it
            if inet_type == socket.AF_INET:
                self.ip_listv4.remove(inet_type, ip_value)
            elif inet_type == socket.AF_INET6:
                self.ip_listv6.remove(inet_type, ip_value)
            #Remove IP to DPS Client
            DPSClient.tunnel_endpoint_delete_IP(self.dps_client, inet_type, ip_value)
            #Remove IP from Domain
            Domain.tunnel_endpoint_delete_IP(self.domain, inet_type, ip_value)
            #Delete Self if not IP Addresses are present
            if self.ip_listv4.count() == 0 and self.ip_listv6.count() == 0:
                self.delete()
            break
        return

    def ip_clear(self, inet_type, ip_value):
        '''
        This deletes a tunnel IP address from this Tunnel and all DVGS and types
        @param dvg: The DVG on which this registration occurred
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param inet_type: AF_INET or AF_INET6
        @type inet_type: Integer
        @param ip_value: The IP address value
        @type ip_value: Integer or String
        '''
        while True:
            for client_type in DpsClientType.types.keys():
                #Delete IP from all matching VNIDs and Client Type
                for vnid in self.DVG_Hash[client_type].keys():
                    fRemoveVNID = False
                    try:
                        vnid_hash_ip = self.VNID_Hash_IP[client_type][vnid]
                        del vnid_hash_ip[ip_value]
                        if len(vnid_hash_ip) == 0:
                            del self.VNID_Hash_IP[client_type][vnid]
                            fRemoveVNID = True
                    except Exception:
                        pass
                    #Remove the IP from the DVG
                    try:
                        dvg = self.DVG_Hash[client_type][vnid]
                        DVG.tunnel_endpoint_delete_IP(dvg, client_type, inet_type, ip_value)
                        if fRemoveVNID:
                            del self.DVG_Hash[client_type][vnid]
                            if len(self.DVG_Hash[client_type]) == 0:
                                #print 'Delete all Endpoints with client_type %s\r'%client_type
                                self.delete_client_type(client_type)
                    except Exception:
                        pass
                #Delete IP completly from Tunnel
                try:
                    ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
                    ip_hash_vnid.clear()
                    del self.IP_Hash_VNID[client_type][ip_value]
                except Exception:
                    pass
            #Delete the IP from Domain and DPS Clients since no DVGs have it
            if inet_type == socket.AF_INET:
                self.ip_listv4.remove(inet_type, ip_value)
            elif inet_type == socket.AF_INET6:
                self.ip_listv6.remove(inet_type, ip_value)
            #Remove IP to DPS Client
            DPSClient.tunnel_endpoint_delete_IP(self.dps_client, inet_type, ip_value)
            #Remove IP from Domain
            Domain.tunnel_endpoint_delete_IP(self.domain, inet_type, ip_value)
            #Delete Self if not IP Addresses are present
            if self.ip_listv4.count() == 0 and self.ip_listv6.count() == 0:
                self.delete()
            break
        return

    def delete_if_empty(self):
        '''
        This routine checks if a DPS Client no longer hosts any Endpoint.
        If it doesn't, it unlinks itself from all the domain
        '''
        if len(self.Endpoint_Hash_MAC) != 0:
            return
        if not self.valid:
            return
        self.valid = False
        #Remove self from all DVGs
        for client_type in DpsClientType.types.keys():
            for dvg in self.DVG_Hash[client_type].values():
                DVG.tunnel_endpoint_delete(dvg, self, client_type)
        #Clear the VNID/DVG Hash
        self.DVG_Hash.clear()
        #Remove from DPS Client Collection
        DPSClient.tunnel_endpoint_delete(self.dps_client, self)
        #Remove from Domain Collection
        Domain.tunnel_endpoint_delete(self.domain, self)
        #Clear IPs
        self.ip_listv4.clear()
        self.ip_listv6.clear()
        return

    def delete(self):
        '''
        Destructor:
        '''
        #Lose references to all endpoint objects
        for endpoint in self.Endpoint_Hash_MAC.values():
            endpoint.delete()
        self.Endpoint_Hash_MAC.clear()
        #Now call delete_if_empty
        self.delete_if_empty()
        return

    def delete_client_type(self, client_type):
        '''
        Remove association with a particular client type
        '''
        for endpoint_key in self.Endpoint_Hash_MAC.keys():
            try:
                endpoint = self.Endpoint_Hash_MAC[endpoint_key]
            except Exception:
                continue
            if endpoint.client_type == client_type:
                endpoint.delete()
        return

    def primary_ip(self):
        '''
        This routine shows any one IP Address to represent the Tunnel
        '''
        try:
            ip = self.ip_listv4.get_primary()
            return IPAddressLocation(socket.AF_INET, ip, 0)
        except Exception:
            pass
        try:
            ip = self.ip_listv6.get_primary()
            return IPAddressLocation(socket.AF_INET6, ip, 0)
        except Exception:
            pass
        return IPAddressLocation(socket.AF_INET, 0, 0)

    def tunnel_types_supported(self):
        '''
        This routine lists the tunnel type hosted by this tunnel
        '''
        tunnel_types = []
        for tunnel_type in DpsClientType.types.keys():
            try:
                dvg_hash = self.DVG_Hash[tunnel_type]
                if len(dvg_hash) == 0:
                    continue
                tunnel_types.append(tunnel_type)
            except Exception:
                pass
        return tunnel_types

    @staticmethod
    def merge_tunnels(tunnel_primary, tunnel_secondary):
        '''
        This routine merges the content of secondary tunnel into the primary tunnel
        and deletes the secondary tunnel
        '''
        #Copy Endpoints
        tunnel_primary.Endpoint_Hash_MAC = dict(tunnel_primary.Endpoint_Hash_MAC.items() + tunnel_secondary.Endpoint_Hash_MAC.items())
        tunnel_secondary.Endpoint_Hash_MAC.clear()
        #Copy DVGs
        for ctype in DpsClientType.types.keys():
            tunnel_primary.DVG_Hash[ctype] = dict(tunnel_primary.DVG_Hash[ctype].items() + tunnel_secondary.DVG_Hash[ctype].items())
            tunnel_secondary.DVG_Hash[ctype].clear()
        #Copy 
        for ctype in DpsClientType.types.keys():
            tunnel_primary.IP_Hash_VNID[ctype] = dict(tunnel_primary.IP_Hash_VNID[ctype].items() + tunnel_secondary.IP_Hash_VNID[ctype].items())
            tunnel_secondary.IP_Hash_VNID[ctype].clear()
        #Key VNID: Value Hash of IPs
        for ctype in DpsClientType.types.keys():
            tunnel_primary.VNID_Hash_IP[ctype] = dict(tunnel_primary.VNID_Hash_IP[ctype].items() + tunnel_secondary.VNID_Hash_IP[ctype].items())
            tunnel_secondary.VNID_Hash_IP[ctype].clear()
        #List of Physical IP Addresses
        ipv4_values = tunnel_secondary.ip_listv4.ip_hash.keys()
        tunnel_secondary.ip_listv4.clear()
        for ip_value in ipv4_values:
            for ctype in DpsClientType.types.keys():
                for dvg in tunnel_primary.DVG_Hash[ctype].values():
                    tunnel_primary.ip_add(dvg, False, ctype, socket.AF_INET, ip_value, DpsTransactionType.normal)
        ip6_values = tunnel_secondary.ip_listv6.ip_hash.keys()
        tunnel_secondary.ip_listv6.clear()
        for ip_value in ip6_values:
            for ctype in DpsClientType.types.keys():
                for dvg in tunnel_primary.DVG_Hash[ctype].values():
                    tunnel_primary.ip_add(dvg, False, ctype, socket.AF_INET, ip_value, DpsTransactionType.normal)
        #Delete the Secondary Tunnel
        tunnel_secondary.delete()
        return

    @staticmethod
    def register(domain, dvg, client_type, transaction_type, dps_client, pip_tuple_list):
        '''
        This routine should be called when a set of tunnel endpoint IP Addresses
        are registered by the DPS
        Client Protocol Handler.
        @param domain: The Domain object
        @type domain: Domain
        @param dvg: The DVG/VNID object
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param transaction_type: The Type of Transaction: should be in Domain.transaction_types
        @type transaction_type: Integer
        @param dps_client: The DPS Client associated with this Tunnel
        @type dps_client: DPSClient
        @type dps_client_port: Integer
        @param pip_tuple_list: A list of tuples of Physical IP Addresses, each 
                                tuple is of the form (inet_type, ip_value).
                                The inet_type = socket type i.e. AF_INET or AF_INET6
                                The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_list: List of tuples
        '''
        #Find if a tunnel exists with existing pIP
        if len(pip_tuple_list) == 0:
            return
        tunnel_set = {}
        if ((client_type == DpsClientType.dove_switch) or 
            (client_type == DpsClientType.vlan_gateway) or
            (client_type == DpsClientType.external_gateway)):
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                #ip_location = IPAddressLocation(inet_type, pip_value, 0)
                #print 'Tunnel Registration: VNID %s, IP %s\r'%(dvg.unique_id, ip_location.show_ip())
                if inet_type == socket.AF_INET:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv4
                else:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv6
                try:
                    tunnel = ip_hash[pip_value]
                    if tunnel.dps_client != dps_client:
                        message = 'Tunnel %s migrated from DPS Client %s to %s\r'%(tunnel.primary_ip().show_ip(),
                                                                                   tunnel.dps_client.location.show_ip(),
                                                                                   dps_client.location.show_ip())
                        dcslib.dps_data_write_log(DpsLogLevels.NOTICE, message)
                        #Delete the Tunnel IP since it no longer belongs with this
                        #Tunnel object
                        tunnel.ip_clear(inet_type, pip_value)
                        continue
                    tunnel_set[tunnel] = True
                except Exception:
                    continue
            if len(tunnel_set) > 1:
                #More than 1 tunnels found corresponding to the IP Addresses
                #Merge them into a single tunnel
                tunnels = tunnel_set.keys()
                tunnel_primary = tunnels[0]
                tunnels_secondary = tunnels[1:]
                for tunnel_secondary in tunnels_secondary:
                    print 'TunnelEndpoint.merge tunnel_primary %s, tunnel_secondary %s\r'%(tunnel_primary, tunnel_primary)
                    TunnelEndpoint.merge_tunnels(tunnel_primary, tunnel_secondary)
                tunnel = tunnel_primary
            elif len(tunnel_set) == 1:
                tunnel = tunnel_set.keys()[0]
            else:
                tunnel = None
            if tunnel is not None:
                tunnel.DVG_Hash[client_type][dvg.unique_id] = dvg
                for pip_tuple in pip_tuple_list:
                    inet_type = pip_tuple[0]
                    pip_value = pip_tuple[1]
                    tunnel.ip_add(dvg, False, client_type, inet_type, pip_value, transaction_type)
                DPSClientHost.Host_Add(domain, dps_client.location.inet_type, 
                                       dps_client.location.ip_value, dps_client.location.port, 
                                       client_type)
            else:
                #Allocate a new tunnel
                tunnel = TunnelEndpoint(True, domain, dvg, client_type, transaction_type, dps_client, pip_tuple_list)
            #if tunnel is not None:
            #    tunnel.show_details()
        if client_type == DpsClientType.external_gateway:
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                try:
                    if inet_type == socket.AF_INET:
                        IPList = dvg.ExternalGatewayIPListv4
                    else:
                        IPList = dvg.ExternalGatewayIPListv6
                    IPList.add(inet_type, pip_value)
                except Exception:
                    pass
        return

    @staticmethod
    def unregister(domain, dvg, client_type, pip_tuple_list):
        '''
        This routine should be called when a set of tunnel endpoint IP 
        Addresses are unregistered by the DPS
        Client Protocol Handler.
        @param domain: The Domain object
        @type domain: Domain
        @param dvg: The DVG/VNID object
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param pip_tuple_list: A list of tuples of Physical IP Addresses, each 
                                tuple is of the form (inet_type, ip_value).
                                The inet_type = socket type i.e. AF_INET or AF_INET6
                                The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_list: List of tuples
        '''
        #TODO: See if we need a DPS Client validation check
        if ((client_type == DpsClientType.dove_switch) or 
            (client_type == DpsClientType.vlan_gateway) or
            (client_type == DpsClientType.external_gateway)):
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                #Find the Tunnel from Domain Collection
                if inet_type == socket.AF_INET:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv4
                else:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv6
                try:
                    tunnel = ip_hash[pip_value]
                    try:
                        TunnelEndpoint.ip_delete(tunnel, dvg, client_type, inet_type, pip_value)
                        #tunnel.show_details()
                    except:
                        message = 'Tunnel.Unregister: Problem deleting IP %s'%pip_value
                        dcslib.dps_data_write_log(DpsLogLevels.WARNING, message)
                except Exception:
                    pass
        if client_type == DpsClientType.external_gateway:
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                try:
                    if inet_type == socket.AF_INET:
                        IPList = dvg.ExternalGatewayIPListv4
                    else:
                        IPList = dvg.ExternalGatewayIPListv6
                    IPList.remove(inet_type, pip_value)
                except Exception:
                    pass
        #TODO: Send update to all other Tunnels in Domain about this IP List unregister
        return

    def ip_tuple_list_get(self):
        '''
        Returns the List of IPs as IP Tuples
        @return - A list of tuples of Physical IP Addresses, each tuple is of 
                  the form (inet_type, ip_value).
                  The inet_type = socket type i.e. AF_INET or AF_INET6
                  The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_list: List of tuples
        '''
        ip_tuple_list = []
        for ip in self.ip_listv4.ip_list:
            ip_tuple_list.append((socket.AF_INET, ip))
        for ip in self.ip_listv6.ip_list:
            ip_tuple_list.append((socket.AF_INET6, ip))
        return ip_tuple_list

    def ip_list_get(self):
        '''
        Returns List of IPs as a string
        '''
        string_ipv4 = self.ip_listv4.toString()
        string_ipv4 = string_ipv4.rstrip()
        string_ipv4 = string_ipv4.rstrip(',')
        string_ipv6 = self.ip_listv6.toString()
        string_ipv6 = string_ipv6.rstrip()
        string_ipv6 = string_ipv6.rstrip(',')
        string_ip_list = string_ipv4 + ', ' + string_ipv6
        string_ip_list = string_ip_list.rstrip()
        string_ip_list = string_ip_list.rstrip(',')
        return string_ip_list

    def show(self):
        '''
        Display Contents of a Tunnel
        '''
        try:
            string_ip = 'Tunnel IPs [%s]'%(self.ip_list_get())
            string_info = 'DPS Client %s: Endpoints %s'%(self.dps_client.location.show(), len(self.Endpoint_Hash_MAC))
            print '%s [%s]\r'%(string_ip, string_info)
        except Exception:
            pass
        return

    def show_details_client_type(self, client_type):
        '''
        Display the details of a Tunnel given a client_type
        '''
        try:
            vnid_hash = self.DVG_Hash[client_type]
            vnid_hash_ip = self.VNID_Hash_IP[client_type]
            ip_hash_vnid = self.IP_Hash_VNID[client_type]
            client_type_string = DpsClientType.types[client_type]
            print 'Tunnel Type %s\r'%client_type_string
            print 'VNIDS %s\r'%vnid_hash.keys()
            for vnid, vnid_ips in vnid_hash_ip.items():
                ip_list = IPAddressList(socket.AF_INET)
                for vnid_ip in vnid_ips:
                    try:
                        ip_list.add(socket.AF_INET, vnid_ip)
                    except Exception:
                        pass
                print 'VNID %s, PIPv4s %s\r'%(vnid, ip_list.toString())
            for ip, ip_vnids in ip_hash_vnid.items():
                try:
                    ip_packed = struct.pack(IPAddressList.fmts[socket.AF_INET], ip)
                    ip_string = socket.inet_ntop(socket.AF_INET, ip_packed)
                except Exception:
                    pass
                print 'PIPv4 %s, VNIDs %s\r'%(ip_string, ip_vnids.keys())
        except Exception:
            pass
        return

    def show_details(self):
        '''
        Display the details of a Tunnel
        '''
        try:
            print '------------------------------------------------------------------\r'
            string_ip = 'Tunnel IPs [%s]'%(self.ip_list_get())
            string_info = '     DPS Client %s: Endpoint Count %s'%(self.dps_client.location.show(), len(self.Endpoint_Hash_MAC))
            print '%s\r'%string_ip
            print '%s\r'%string_info
            for client_type in DpsClientType.types.keys():
                client_type_string = DpsClientType.types[client_type]
                if len(self.DVG_Hash[client_type]) == 0:
                    if len(self.VNID_Hash_IP[client_type]) != 0:
                        print 'ALERT! self.VNID_Hash_IP for %s exists but no VNID in tunnel\r'%client_type_string
                    if len(self.IP_Hash_VNID[client_type]) != 0:
                        print 'ALERT! self.IP_Hash_VNID for %s exists but no VNID in tunnel\r'%client_type_string
                    continue
                self.show_details_client_type(client_type)
        except Exception, ex:
            pass
        print '------------------------------------------------------------------\r'
Exemplo n.º 4
0
class TunnelEndpoint:
    '''
    This represents a DOVE Underlay Tunnel in the DOVE environment -
    i.e DOVE Switch or VLAN Gateway.
    NOTE: Each Tunnel Endpoint will have multiple instances every every Domain
          it hosts Endpoints on. In other words if a TunnelEndpoint hosts
          endpoints on Domain A and Domain B, then there will be 2
          independent instances of that Tunnel Endpoint, 1 in Domain A and 1
          in Domain B.
    '''
    def __init__(self, fregister, domain, dvg, client_type, transaction_type,
                 dps_client, pip_tuple_list):
        '''
        Constructor:
        This routine assumes that the 1st IP address in the tuple uniquely
        identifies a DOVE Switch/VLAN Gateway within the Domain.
        @param fregister: Whether this is an explicit tunnel register
        @param fregister: Boolean
        @param domain: The Domain object
        @type domain: Domain
        @param dvg: The DVG/VNID object
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param transaction_type: The Type of Transaction: should be in Domain.transaction_types
        @type transaction_type: Integer
        @param dps_client: The DPS Client associated with this Tunnel
        @type dps_client: DPSClient
        @type dps_client_port: Integer
        @param pip_tuple_array: A list of tuples of Physical IP Addresses, each 
                                tuple is of the form (inet_type, ip_value).
                                The inet_type = socket type i.e. AF_INET or AF_INET6
                                The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_array: List of tuples
        '''
        if len(pip_tuple_list) == 0:
            raise Exception('No IP Address Provided: Invalid DOVE Switch')
        try:
            client_string = DpsClientType.types[client_type]
        except Exception:
            raise Exception('Not a supported Tunnel Type')
        self.domain = domain
        #DPS Client Location i.e. the UDP Port and Location
        self.dps_client = dps_client
        #############################################################
        #Based on Object Model Chapter 5.3: DOVE Switch
        #############################################################
        #TODO: SQL Table of Policies: Requirement 1.3
        #Hash List of all Endpoints hashed by MAC: Requirement 1.4
        self.Endpoint_Hash_MAC = {}
        #A dictionary indexed by VNID IDs, the value is the number
        #of Endpoints on that DOVE Switch in a particular VNID/DVG
        self.DVG_Hash = {}
        inet_type = pip_tuple_list[0][0]
        ip_value = pip_tuple_list[0][1]
        #ip_location = IPAddressLocation(inet_type, ip_value, 0)
        #print 'Tunnel %s created DVG_Hash\r'%ip_location.show_ip()
        for ctype in DpsClientType.types.keys():
            self.DVG_Hash[ctype] = {}
        #Key IP_value: Value VNID
        self.IP_Hash_VNID = {}
        #print 'Tunnel %s created IP_Hash_VNID\r'%ip_location.show_ip()
        for ctype in DpsClientType.types.keys():
            self.IP_Hash_VNID[ctype] = {}
        #Key VNID: Value Hash of IPs
        self.VNID_Hash_IP = {}
        for ctype in DpsClientType.types.keys():
            self.VNID_Hash_IP[ctype] = {}
        #print 'Tunnel %s created VNID_Hash_IP\r'%ip_location.show_ip()
        #List of Physical IP Addresses
        self.ip_listv4 = IPAddressList(socket.AF_INET)
        self.ip_listv6 = IPAddressList(socket.AF_INET6)
        for i in range(len(pip_tuple_list)):
            inet_type = pip_tuple_list[i][0]
            ip_value = pip_tuple_list[i][1]
            self.ip_add(dvg, fregister, client_type, inet_type, ip_value,
                        transaction_type)
        #############################################################
        #To be finished
        #############################################################
        #dcs_object.__init__(self, self.primary_ip().show())
        self.version = 0
        self.valid = True
        DPSClientHost.Host_Add(domain, dps_client.location.inet_type,
                               dps_client.location.ip_value,
                               dps_client.location.port, client_type)
        #print 'Tunnel %s created\r'%ip_location.show_ip()

    def endpoint_add(self, endpoint, transaction_type):
        '''
        Adds an Endpoint to the DOVE Tunnel
        @param transaction_type: The Type of Transaction: should be in Domain.transaction_types
        @type transaction_type: Integer
        '''
        #log.info('Tunnel.endpoint_add')
        #self.show(None)
        self.Endpoint_Hash_MAC[endpoint.vMac] = endpoint
        #self.show()
        return

    def endpoint_del(self, endpoint):
        '''
        Deletes an Endpoint from the DOVE Tunnel Endpoint
        '''
        try:
            del self.Endpoint_Hash_MAC[endpoint.vMac]
        except Exception:
            pass
        #self.show()
        if self.domain.unique_id == 0:
            #Remove the Tunnel Endpoint if no other Endpoints exist for Domain 0
            self.delete_if_empty()
        return

    def ip_add(self, dvg, fregister, client_type, inet_type, ip_value,
               transaction_type):
        '''
        This adds a tunnel IP address
        @param dvg: The DVG on which this registration occurred
        @type dvg: DVG
        @param fregister: Whether this is an explicit tunnel register
        @param fregister: Boolean
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param inet_type: AF_INET or AF_INET6
        @type inet_type: Integer
        @param ip_value: The IP address value
        @type ip_value: Integer or String
        @param transaction_type: The Type of Transaction: should be in DpsTransactionType
        @type transaction_type: Integer
        '''
        while True:
            try:
                client_type_value = DpsClientType.types[client_type]
            except Exception:
                break
            self.DVG_Hash[client_type][dvg.unique_id] = dvg
            #Add to Tunnels List even though it may have come through other DVGs as well
            if inet_type == socket.AF_INET:
                if not self.ip_listv4.search(ip_value):
                    self.ip_listv4.add(inet_type, ip_value)
            elif inet_type == socket.AF_INET6:
                if not self.ip_listv4.search(ip_value):
                    self.ip_listv6.add(inet_type, ip_value)
            #log.info('ip_add: Adding to DPS Client %s', ip_value)
            #Add DVG to self.
            #Add IP to DPS Client
            DPSClient.tunnel_endpoint_add_IP(self.dps_client, self, inet_type,
                                             ip_value)
            #Add IP to this DVGs
            DVG.tunnel_endpoint_add_IP(dvg, fregister, self, client_type,
                                       inet_type, ip_value, transaction_type)
            try:
                vnid_hash_ip = self.VNID_Hash_IP[client_type][dvg.unique_id]
            except Exception:
                self.VNID_Hash_IP[client_type][dvg.unique_id] = {}
                vnid_hash_ip = self.VNID_Hash_IP[client_type][dvg.unique_id]
            vnid_hash_ip[ip_value] = True
            #Add DVGs to IP hash
            try:
                ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
            except Exception:
                self.IP_Hash_VNID[client_type][ip_value] = {}
                ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
            ip_hash_vnid[dvg.unique_id] = True
            #Add IP to Domain
            #log.info('ip_add: Adding to domain %s', self.domain.unique_id)
            Domain.tunnel_endpoint_add_IP(self.domain, self, inet_type,
                                          ip_value)
            break
        return

    def ip_delete(self, dvg, client_type, inet_type, ip_value):
        '''
        This deletes a tunnel IP address from a DVG and client type
        @param dvg: The DVG on which this registration occurred
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param inet_type: AF_INET or AF_INET6
        @type inet_type: Integer
        @param ip_value: The IP address value
        @type ip_value: Integer or String
        '''
        fdeleteVNID = False
        fdeleteIP = False
        while True:
            try:
                client_type_value = DpsClientType.types[client_type]
            except Exception:
                break
            try:
                vnid_hash_ip = self.VNID_Hash_IP[client_type][dvg.unique_id]
                del vnid_hash_ip[ip_value]
                if len(vnid_hash_ip) == 0:
                    del self.VNID_Hash_IP[client_type][dvg.unique_id]
                    fdeleteVNID = True
            except Exception:
                pass
            try:
                ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
                del ip_hash_vnid[dvg.unique_id]
                if len(ip_hash_vnid) == 0:
                    del self.IP_Hash_VNID[client_type][ip_value]
                    fdeleteIP = True
            except Exception:
                pass
            if fdeleteVNID:
                #Remove Tunnel from DVG
                DVG.tunnel_endpoint_delete(dvg, self, client_type)
                try:
                    del self.DVG_Hash[client_type][dvg.unique_id]
                    if len(self.DVG_Hash[client_type]) == 0:
                        #print 'Delete all Endpoints with client_type %s\r'%client_type_value
                        self.delete_client_type(client_type)
                except Exception:
                    pass
            if not fdeleteIP:
                break
            if fdeleteIP:
                #Remove only the IP from the DVG
                DVG.tunnel_endpoint_delete_IP(dvg, client_type, inet_type,
                                              ip_value)
            #Check if IP is present in any client type
            ip_present = False
            for ctype in self.IP_Hash_VNID.keys():
                try:
                    ip_hash_vnid = self.IP_Hash_VNID[ctype][ip_value]
                    ip_present = True
                    break
                except Exception:
                    continue
            if ip_present:
                break
            #Actually delete the IP from Domain and DPS Clients since no DVGs have it
            if inet_type == socket.AF_INET:
                self.ip_listv4.remove(inet_type, ip_value)
            elif inet_type == socket.AF_INET6:
                self.ip_listv6.remove(inet_type, ip_value)
            #Remove IP to DPS Client
            DPSClient.tunnel_endpoint_delete_IP(self.dps_client, inet_type,
                                                ip_value)
            #Remove IP from Domain
            Domain.tunnel_endpoint_delete_IP(self.domain, inet_type, ip_value)
            #Delete Self if not IP Addresses are present
            if self.ip_listv4.count() == 0 and self.ip_listv6.count() == 0:
                self.delete()
            break
        return

    def ip_clear(self, inet_type, ip_value):
        '''
        This deletes a tunnel IP address from this Tunnel and all DVGS and types
        @param dvg: The DVG on which this registration occurred
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param inet_type: AF_INET or AF_INET6
        @type inet_type: Integer
        @param ip_value: The IP address value
        @type ip_value: Integer or String
        '''
        while True:
            for client_type in DpsClientType.types.keys():
                #Delete IP from all matching VNIDs and Client Type
                for vnid in self.DVG_Hash[client_type].keys():
                    fRemoveVNID = False
                    try:
                        vnid_hash_ip = self.VNID_Hash_IP[client_type][vnid]
                        del vnid_hash_ip[ip_value]
                        if len(vnid_hash_ip) == 0:
                            del self.VNID_Hash_IP[client_type][vnid]
                            fRemoveVNID = True
                    except Exception:
                        pass
                    #Remove the IP from the DVG
                    try:
                        dvg = self.DVG_Hash[client_type][vnid]
                        DVG.tunnel_endpoint_delete_IP(dvg, client_type,
                                                      inet_type, ip_value)
                        if fRemoveVNID:
                            del self.DVG_Hash[client_type][vnid]
                            if len(self.DVG_Hash[client_type]) == 0:
                                #print 'Delete all Endpoints with client_type %s\r'%client_type
                                self.delete_client_type(client_type)
                    except Exception:
                        pass
                #Delete IP completly from Tunnel
                try:
                    ip_hash_vnid = self.IP_Hash_VNID[client_type][ip_value]
                    ip_hash_vnid.clear()
                    del self.IP_Hash_VNID[client_type][ip_value]
                except Exception:
                    pass
            #Delete the IP from Domain and DPS Clients since no DVGs have it
            if inet_type == socket.AF_INET:
                self.ip_listv4.remove(inet_type, ip_value)
            elif inet_type == socket.AF_INET6:
                self.ip_listv6.remove(inet_type, ip_value)
            #Remove IP to DPS Client
            DPSClient.tunnel_endpoint_delete_IP(self.dps_client, inet_type,
                                                ip_value)
            #Remove IP from Domain
            Domain.tunnel_endpoint_delete_IP(self.domain, inet_type, ip_value)
            #Delete Self if not IP Addresses are present
            if self.ip_listv4.count() == 0 and self.ip_listv6.count() == 0:
                self.delete()
            break
        return

    def delete_if_empty(self):
        '''
        This routine checks if a DPS Client no longer hosts any Endpoint.
        If it doesn't, it unlinks itself from all the domain
        '''
        if len(self.Endpoint_Hash_MAC) != 0:
            return
        if not self.valid:
            return
        self.valid = False
        #Remove self from all DVGs
        for client_type in DpsClientType.types.keys():
            for dvg in self.DVG_Hash[client_type].values():
                DVG.tunnel_endpoint_delete(dvg, self, client_type)
        #Clear the VNID/DVG Hash
        self.DVG_Hash.clear()
        #Remove from DPS Client Collection
        DPSClient.tunnel_endpoint_delete(self.dps_client, self)
        #Remove from Domain Collection
        Domain.tunnel_endpoint_delete(self.domain, self)
        #Clear IPs
        self.ip_listv4.clear()
        self.ip_listv6.clear()
        return

    def delete(self):
        '''
        Destructor:
        '''
        #Lose references to all endpoint objects
        for endpoint in self.Endpoint_Hash_MAC.values():
            endpoint.delete()
        self.Endpoint_Hash_MAC.clear()
        #Now call delete_if_empty
        self.delete_if_empty()
        return

    def delete_client_type(self, client_type):
        '''
        Remove association with a particular client type
        '''
        for endpoint_key in self.Endpoint_Hash_MAC.keys():
            try:
                endpoint = self.Endpoint_Hash_MAC[endpoint_key]
            except Exception:
                continue
            if endpoint.client_type == client_type:
                endpoint.delete()
        return

    def primary_ip(self):
        '''
        This routine shows any one IP Address to represent the Tunnel
        '''
        try:
            ip = self.ip_listv4.get_primary()
            return IPAddressLocation(socket.AF_INET, ip, 0)
        except Exception:
            pass
        try:
            ip = self.ip_listv6.get_primary()
            return IPAddressLocation(socket.AF_INET6, ip, 0)
        except Exception:
            pass
        return IPAddressLocation(socket.AF_INET, 0, 0)

    def tunnel_types_supported(self):
        '''
        This routine lists the tunnel type hosted by this tunnel
        '''
        tunnel_types = []
        for tunnel_type in DpsClientType.types.keys():
            try:
                dvg_hash = self.DVG_Hash[tunnel_type]
                if len(dvg_hash) == 0:
                    continue
                tunnel_types.append(tunnel_type)
            except Exception:
                pass
        return tunnel_types

    @staticmethod
    def merge_tunnels(tunnel_primary, tunnel_secondary):
        '''
        This routine merges the content of secondary tunnel into the primary tunnel
        and deletes the secondary tunnel
        '''
        #Copy Endpoints
        tunnel_primary.Endpoint_Hash_MAC = dict(
            tunnel_primary.Endpoint_Hash_MAC.items() +
            tunnel_secondary.Endpoint_Hash_MAC.items())
        tunnel_secondary.Endpoint_Hash_MAC.clear()
        #Copy DVGs
        for ctype in DpsClientType.types.keys():
            tunnel_primary.DVG_Hash[ctype] = dict(
                tunnel_primary.DVG_Hash[ctype].items() +
                tunnel_secondary.DVG_Hash[ctype].items())
            tunnel_secondary.DVG_Hash[ctype].clear()
        #Copy
        for ctype in DpsClientType.types.keys():
            tunnel_primary.IP_Hash_VNID[ctype] = dict(
                tunnel_primary.IP_Hash_VNID[ctype].items() +
                tunnel_secondary.IP_Hash_VNID[ctype].items())
            tunnel_secondary.IP_Hash_VNID[ctype].clear()
        #Key VNID: Value Hash of IPs
        for ctype in DpsClientType.types.keys():
            tunnel_primary.VNID_Hash_IP[ctype] = dict(
                tunnel_primary.VNID_Hash_IP[ctype].items() +
                tunnel_secondary.VNID_Hash_IP[ctype].items())
            tunnel_secondary.VNID_Hash_IP[ctype].clear()
        #List of Physical IP Addresses
        ipv4_values = tunnel_secondary.ip_listv4.ip_hash.keys()
        tunnel_secondary.ip_listv4.clear()
        for ip_value in ipv4_values:
            for ctype in DpsClientType.types.keys():
                for dvg in tunnel_primary.DVG_Hash[ctype].values():
                    tunnel_primary.ip_add(dvg, False, ctype, socket.AF_INET,
                                          ip_value, DpsTransactionType.normal)
        ip6_values = tunnel_secondary.ip_listv6.ip_hash.keys()
        tunnel_secondary.ip_listv6.clear()
        for ip_value in ip6_values:
            for ctype in DpsClientType.types.keys():
                for dvg in tunnel_primary.DVG_Hash[ctype].values():
                    tunnel_primary.ip_add(dvg, False, ctype, socket.AF_INET,
                                          ip_value, DpsTransactionType.normal)
        #Delete the Secondary Tunnel
        tunnel_secondary.delete()
        return

    @staticmethod
    def register(domain, dvg, client_type, transaction_type, dps_client,
                 pip_tuple_list):
        '''
        This routine should be called when a set of tunnel endpoint IP Addresses
        are registered by the DPS
        Client Protocol Handler.
        @param domain: The Domain object
        @type domain: Domain
        @param dvg: The DVG/VNID object
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param transaction_type: The Type of Transaction: should be in Domain.transaction_types
        @type transaction_type: Integer
        @param dps_client: The DPS Client associated with this Tunnel
        @type dps_client: DPSClient
        @type dps_client_port: Integer
        @param pip_tuple_list: A list of tuples of Physical IP Addresses, each 
                                tuple is of the form (inet_type, ip_value).
                                The inet_type = socket type i.e. AF_INET or AF_INET6
                                The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_list: List of tuples
        '''
        #Find if a tunnel exists with existing pIP
        if len(pip_tuple_list) == 0:
            return
        tunnel_set = {}
        if ((client_type == DpsClientType.dove_switch)
                or (client_type == DpsClientType.vlan_gateway)
                or (client_type == DpsClientType.external_gateway)):
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                #ip_location = IPAddressLocation(inet_type, pip_value, 0)
                #print 'Tunnel Registration: VNID %s, IP %s\r'%(dvg.unique_id, ip_location.show_ip())
                if inet_type == socket.AF_INET:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv4
                else:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv6
                try:
                    tunnel = ip_hash[pip_value]
                    if tunnel.dps_client != dps_client:
                        message = 'Tunnel %s migrated from DPS Client %s to %s\r' % (
                            tunnel.primary_ip().show_ip(),
                            tunnel.dps_client.location.show_ip(),
                            dps_client.location.show_ip())
                        dcslib.dps_data_write_log(DpsLogLevels.NOTICE, message)
                        #Delete the Tunnel IP since it no longer belongs with this
                        #Tunnel object
                        tunnel.ip_clear(inet_type, pip_value)
                        continue
                    tunnel_set[tunnel] = True
                except Exception:
                    continue
            if len(tunnel_set) > 1:
                #More than 1 tunnels found corresponding to the IP Addresses
                #Merge them into a single tunnel
                tunnels = tunnel_set.keys()
                tunnel_primary = tunnels[0]
                tunnels_secondary = tunnels[1:]
                for tunnel_secondary in tunnels_secondary:
                    print 'TunnelEndpoint.merge tunnel_primary %s, tunnel_secondary %s\r' % (
                        tunnel_primary, tunnel_primary)
                    TunnelEndpoint.merge_tunnels(tunnel_primary,
                                                 tunnel_secondary)
                tunnel = tunnel_primary
            elif len(tunnel_set) == 1:
                tunnel = tunnel_set.keys()[0]
            else:
                tunnel = None
            if tunnel is not None:
                tunnel.DVG_Hash[client_type][dvg.unique_id] = dvg
                for pip_tuple in pip_tuple_list:
                    inet_type = pip_tuple[0]
                    pip_value = pip_tuple[1]
                    tunnel.ip_add(dvg, False, client_type, inet_type,
                                  pip_value, transaction_type)
                DPSClientHost.Host_Add(domain, dps_client.location.inet_type,
                                       dps_client.location.ip_value,
                                       dps_client.location.port, client_type)
            else:
                #Allocate a new tunnel
                tunnel = TunnelEndpoint(True, domain, dvg, client_type,
                                        transaction_type, dps_client,
                                        pip_tuple_list)
            #if tunnel is not None:
            #    tunnel.show_details()
        if client_type == DpsClientType.external_gateway:
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                try:
                    if inet_type == socket.AF_INET:
                        IPList = dvg.ExternalGatewayIPListv4
                    else:
                        IPList = dvg.ExternalGatewayIPListv6
                    IPList.add(inet_type, pip_value)
                except Exception:
                    pass
        return

    @staticmethod
    def unregister(domain, dvg, client_type, pip_tuple_list):
        '''
        This routine should be called when a set of tunnel endpoint IP 
        Addresses are unregistered by the DPS
        Client Protocol Handler.
        @param domain: The Domain object
        @type domain: Domain
        @param dvg: The DVG/VNID object
        @type dvg: DVG
        @param client_type: The Type of Client: should be in DpsClientType.types
        @type client_type: Integer 
        @param pip_tuple_list: A list of tuples of Physical IP Addresses, each 
                                tuple is of the form (inet_type, ip_value).
                                The inet_type = socket type i.e. AF_INET or AF_INET6
                                The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_list: List of tuples
        '''
        #TODO: See if we need a DPS Client validation check
        if ((client_type == DpsClientType.dove_switch)
                or (client_type == DpsClientType.vlan_gateway)
                or (client_type == DpsClientType.external_gateway)):
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                #Find the Tunnel from Domain Collection
                if inet_type == socket.AF_INET:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv4
                else:
                    ip_hash = domain.Tunnel_Endpoints_Hash_IPv6
                try:
                    tunnel = ip_hash[pip_value]
                    try:
                        TunnelEndpoint.ip_delete(tunnel, dvg, client_type,
                                                 inet_type, pip_value)
                        #tunnel.show_details()
                    except:
                        message = 'Tunnel.Unregister: Problem deleting IP %s' % pip_value
                        dcslib.dps_data_write_log(DpsLogLevels.WARNING,
                                                  message)
                except Exception:
                    pass
        if client_type == DpsClientType.external_gateway:
            for pip_tuple in pip_tuple_list:
                inet_type = pip_tuple[0]
                pip_value = pip_tuple[1]
                try:
                    if inet_type == socket.AF_INET:
                        IPList = dvg.ExternalGatewayIPListv4
                    else:
                        IPList = dvg.ExternalGatewayIPListv6
                    IPList.remove(inet_type, pip_value)
                except Exception:
                    pass
        #TODO: Send update to all other Tunnels in Domain about this IP List unregister
        return

    def ip_tuple_list_get(self):
        '''
        Returns the List of IPs as IP Tuples
        @return - A list of tuples of Physical IP Addresses, each tuple is of 
                  the form (inet_type, ip_value).
                  The inet_type = socket type i.e. AF_INET or AF_INET6
                  The ip_value = IPv4 in integer or IPv6 in string
        @type pip_tuple_list: List of tuples
        '''
        ip_tuple_list = []
        for ip in self.ip_listv4.ip_list:
            ip_tuple_list.append((socket.AF_INET, ip))
        for ip in self.ip_listv6.ip_list:
            ip_tuple_list.append((socket.AF_INET6, ip))
        return ip_tuple_list

    def ip_list_get(self):
        '''
        Returns List of IPs as a string
        '''
        string_ipv4 = self.ip_listv4.toString()
        string_ipv4 = string_ipv4.rstrip()
        string_ipv4 = string_ipv4.rstrip(',')
        string_ipv6 = self.ip_listv6.toString()
        string_ipv6 = string_ipv6.rstrip()
        string_ipv6 = string_ipv6.rstrip(',')
        string_ip_list = string_ipv4 + ', ' + string_ipv6
        string_ip_list = string_ip_list.rstrip()
        string_ip_list = string_ip_list.rstrip(',')
        return string_ip_list

    def show(self):
        '''
        Display Contents of a Tunnel
        '''
        try:
            string_ip = 'Tunnel IPs [%s]' % (self.ip_list_get())
            string_info = 'DPS Client %s: Endpoints %s' % (
                self.dps_client.location.show(), len(self.Endpoint_Hash_MAC))
            print '%s [%s]\r' % (string_ip, string_info)
        except Exception:
            pass
        return

    def show_details_client_type(self, client_type):
        '''
        Display the details of a Tunnel given a client_type
        '''
        try:
            vnid_hash = self.DVG_Hash[client_type]
            vnid_hash_ip = self.VNID_Hash_IP[client_type]
            ip_hash_vnid = self.IP_Hash_VNID[client_type]
            client_type_string = DpsClientType.types[client_type]
            print 'Tunnel Type %s\r' % client_type_string
            print 'VNIDS %s\r' % vnid_hash.keys()
            for vnid, vnid_ips in vnid_hash_ip.items():
                ip_list = IPAddressList(socket.AF_INET)
                for vnid_ip in vnid_ips:
                    try:
                        ip_list.add(socket.AF_INET, vnid_ip)
                    except Exception:
                        pass
                print 'VNID %s, PIPv4s %s\r' % (vnid, ip_list.toString())
            for ip, ip_vnids in ip_hash_vnid.items():
                try:
                    ip_packed = struct.pack(IPAddressList.fmts[socket.AF_INET],
                                            ip)
                    ip_string = socket.inet_ntop(socket.AF_INET, ip_packed)
                except Exception:
                    pass
                print 'PIPv4 %s, VNIDs %s\r' % (ip_string, ip_vnids.keys())
        except Exception:
            pass
        return

    def show_details(self):
        '''
        Display the details of a Tunnel
        '''
        try:
            print '------------------------------------------------------------------\r'
            string_ip = 'Tunnel IPs [%s]' % (self.ip_list_get())
            string_info = '     DPS Client %s: Endpoint Count %s' % (
                self.dps_client.location.show(), len(self.Endpoint_Hash_MAC))
            print '%s\r' % string_ip
            print '%s\r' % string_info
            for client_type in DpsClientType.types.keys():
                client_type_string = DpsClientType.types[client_type]
                if len(self.DVG_Hash[client_type]) == 0:
                    if len(self.VNID_Hash_IP[client_type]) != 0:
                        print 'ALERT! self.VNID_Hash_IP for %s exists but no VNID in tunnel\r' % client_type_string
                    if len(self.IP_Hash_VNID[client_type]) != 0:
                        print 'ALERT! self.IP_Hash_VNID for %s exists but no VNID in tunnel\r' % client_type_string
                    continue
                self.show_details_client_type(client_type)
        except Exception, ex:
            pass
        print '------------------------------------------------------------------\r'