def update_source_tunnel_rules(self, curr_dp, source_id, tunnel_id, out_port, output_table):
        """Update the tunnel rulelist for when the output port has changed"""
        src_dp = self.tunnel_sources[source_id]['dp']
        dst_dp = self.tunnel_dests[tunnel_id]['dst_dp']
        prev_list = self.dyn_tunnel_rules[tunnel_id].get(source_id, [])
        new_list = []
        pcp_flag = valve_of.PCP_TUNNEL_FLAG
        if self.tunnel_dests[tunnel_id]['reverse']:
            pcp_flag = valve_of.PCP_TUNNEL_REVERSE_DIRECTION_FLAG
        if curr_dp == src_dp and curr_dp != dst_dp:
            # SRC DP: in_port, actions=[push_vlan, output, pop_vlans]
            # Ideally, we would be able to detect if the tunnel has an `allow` action clause.
            #   However, this is difficult as a single ACL can have multiple rules using the same
            #   tunnel, but with one instance requiring the `allow` clause and another, not.
            # This means it is easier to always append the `pop_vlans` in assumption that the
            #   `allow` action does exist, and then optimize/reduce the redundant rules before
            #   outputting the flowrule.
            # We also set the tunnel VLAN header with a PCP value indicating that we are in
            #   the tunnel, which will save the VLANs from being reserved.
            new_list = [
                {'vlan_vids': [{'vid': tunnel_id, 'eth_type': ether.ETH_TYPE_8021Q}]},
                {'set_fields': [{'vlan_pcp': pcp_flag}]},
                {'port': out_port},
                {'pop_vlans': 1}]
        elif curr_dp == dst_dp and curr_dp != src_dp:
            # DST DP: in_port, vlan_vid, actions=[pop_vlan, additional_instructions, output]
            # If exit_instructions are applied, then we want to pop off the tunnel
            #   VLAN header, then apply the additional instructions, then output
            if self.tunnel_dests[tunnel_id]['maintain_encapsulation']:
                # We wish to maintain tunnel encapsulation before outputting
                #   So do not add the pop_vlans rule
                new_list = []
            else:
                new_list = [{'pop_vlans': 1}]
            exit_instructions = self.tunnel_dests[tunnel_id].get('exit_instructions', [])
            new_list.extend(copy.copy(list(exit_instructions)))
            if out_port is None:
                # DP dest tunnel, so we fall through into the eth_dst output table
                new_list.append({'goto': output_table.table_id})
            else:
                # Tunnel has port specified, so output to destination
                new_list.append({'port': out_port})
        elif curr_dp == src_dp and curr_dp == dst_dp:
            # SINGLE DP: in_port, actions=[additional_instructions, out_port]
            exit_instructions = self.tunnel_dests[tunnel_id].get('exit_instructions', [])
            new_list.extend(copy.copy(list(exit_instructions)))
            if self.tunnel_dests[tunnel_id].get('maintain_encapsulation', False):
                # Maintain encapsulation implies we want the tunnel VID on the packet,
                #   so ensure it is purposefully put onto the packet, even when
                #   there would originally be no need to push on a tunnel VID
                new_list.extend([
                    {'vlan_vids': [{'vid': tunnel_id, 'eth_type': ether.ETH_TYPE_8021Q}]},
                    {'set_fields': [{'vlan_pcp': pcp_flag}]}])
            if out_port is None:
                # DP dest tunnel, so we fall through into the eth_dst output table
                new_list.extend([{'goto': output_table.table_id}])
            else:
                # Tunnel has port specified, so output to destination
                new_list.extend([{'port': out_port}])
        else:
            # TRANSIT DP: in_port, vlan_vid, actions=[output]
            new_list = [{'port': out_port}]
        if new_list != prev_list:
            self.dyn_tunnel_rules[tunnel_id][source_id] = new_list
            return True
        return True