Skip to content

Documentation for Fcneighborpolicy Module

FCNeighborPolicy

Bases: NeighborPolicy

Neighbor policy for fully-connected (FC) structured topologies.

This policy assumes a fully-connected topology where every node should attempt to connect to all known nodes. It always accepts incoming neighbor connections and considers the neighbor list incomplete if there are known nodes that are not yet connected.

The goal is to maintain full connectivity across all known nodes in the federation.

Attributes:

Name Type Description
max_neighbors int | None

Unused in FC topology, but kept for compatibility.

nodes_known set[str]

Set of all known node IDs discovered in the federation.

neighbors set[str]

Set of currently connected neighbor node IDs.

addr str | None

The address of this node (used for self-identification).

neighbors_lock Locker

Async lock for safe access to neighbors.

nodes_known_lock Locker

Async lock for safe access to nodes_known.

_verbose bool

Whether to enable verbose logging for debugging purposes.

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
class FCNeighborPolicy(NeighborPolicy):
    """
    Neighbor policy for fully-connected (FC) structured topologies.

    This policy assumes a fully-connected topology where every node should attempt 
    to connect to all known nodes. It always accepts incoming neighbor connections 
    and considers the neighbor list incomplete if there are known nodes that are not yet connected.

    The goal is to maintain full connectivity across all known nodes in the federation.

    Attributes:
        max_neighbors (int | None): Unused in FC topology, but kept for compatibility.
        nodes_known (set[str]): Set of all known node IDs discovered in the federation.
        neighbors (set[str]): Set of currently connected neighbor node IDs.
        addr (str | None): The address of this node (used for self-identification).
        neighbors_lock (Locker): Async lock for safe access to `neighbors`.
        nodes_known_lock (Locker): Async lock for safe access to `nodes_known`.
        _verbose (bool): Whether to enable verbose logging for debugging purposes.
    """

    def __init__(self):
        self.max_neighbors = None
        self.nodes_known = set()
        self.neighbors = set()
        self.addr = None
        self.neighbors_lock = Locker(name="neighbors_lock")
        self.nodes_known_lock = Locker(name="nodes_known_lock")
        self._verbose = False

    async def need_more_neighbors(self):
        """
        Fully connected network requires to be connected to all devices, therefore,
        if there are more nodes known that self.neighbors, more neighbors are required
        """
        self.neighbors_lock.acquire()
        need_more = len(self.neighbors) < len(self.nodes_known)
        self.neighbors_lock.release()
        return need_more

    async def set_config(self, config):
        """
        Args:
            config[0] -> list of self neighbors
            config[1] -> list of nodes known on federation
            config[2] -> self addr
            config[3] -> stricted_topology
        """
        logging.info("Initializing Fully-Connected Topology Neighbor Policy")
        self.neighbors_lock.acquire()
        self.neighbors = config[0]
        self.neighbors_lock.release()
        for addr in config[1]:
            self.nodes_known.add(addr)
        self.addr

    async def accept_connection(self, source, joining=False):
        """
        return true if connection is accepted
        """
        self.neighbors_lock.acquire()
        ac = source not in self.neighbors
        self.neighbors_lock.release()
        return ac

    async def meet_node(self, node):
        """
        Update the list of nodes known on federation
        """
        self.nodes_known_lock.acquire()
        if node != self.addr:
            if node not in self.nodes_known:
                logging.info(f"Update nodes known | addr: {node}")
            self.nodes_known.add(node)
        self.nodes_known_lock.release()

    async def get_nodes_known(self, neighbors_too=False, neighbors_only=False):
        if neighbors_only:
            self.neighbors_lock.acquire()
            no = self.neighbors.copy()
            self.neighbors_lock.release()
            return no

        self.nodes_known_lock.acquire()
        nk = self.nodes_known.copy()
        if not neighbors_too:
            self.neighbors_lock.acquire()
            nk = self.nodes_known - self.neighbors
            self.neighbors_lock.release()
        self.nodes_known_lock.release()
        return nk

    async def forget_nodes(self, nodes, forget_all=False):
        self.nodes_known_lock.acquire()
        if forget_all:
            self.nodes_known.clear()
        else:
            for node in nodes:
                self.nodes_known.discard(node)
        self.nodes_known_lock.release()

    async def get_actions(self):
        """
        return list of actions to do in response to connection
            - First list represents addrs argument to LinkMessage to connect to
            - Second one represents the same but for disconnect from LinkMessage
        """
        return [await self._connect_to(), await self._disconnect_from()]

    async def _disconnect_from(self):
        return ""

    async def _connect_to(self):
        ct = ""
        self.neighbors_lock.acquire()
        ct = " ".join(self.neighbors)
        self.neighbors_lock.release()
        return ct

    async def update_neighbors(self, node, remove=False):
        if node == self.addr:
            return
        self.neighbors_lock.acquire()
        if remove:
            try:
                self.neighbors.remove(node)
                if self._verbose:
                    logging.info(f"Remove neighbor | addr: {node}")
            except KeyError:
                pass
        else:
            self.neighbors.add(node)
            if self._verbose:
                logging.info(f"Add neighbor | addr: {node}")
        self.neighbors_lock.release()

    async def any_leftovers_neighbors(self):
        return False

    async def get_neighbors_to_remove(self):
        return set()

    async def get_posible_neighbors(self):
        """Return set of posible neighbors to connect to."""
        return await self.get_nodes_known(neighbors_too=False)

    async def stricted_topology_status(stricted_topology: bool):
        pass

accept_connection(source, joining=False) async

return true if connection is accepted

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
62
63
64
65
66
67
68
69
async def accept_connection(self, source, joining=False):
    """
    return true if connection is accepted
    """
    self.neighbors_lock.acquire()
    ac = source not in self.neighbors
    self.neighbors_lock.release()
    return ac

get_actions() async

return list of actions to do in response to connection - First list represents addrs argument to LinkMessage to connect to - Second one represents the same but for disconnect from LinkMessage

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
107
108
109
110
111
112
113
async def get_actions(self):
    """
    return list of actions to do in response to connection
        - First list represents addrs argument to LinkMessage to connect to
        - Second one represents the same but for disconnect from LinkMessage
    """
    return [await self._connect_to(), await self._disconnect_from()]

get_posible_neighbors() async

Return set of posible neighbors to connect to.

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
148
149
150
async def get_posible_neighbors(self):
    """Return set of posible neighbors to connect to."""
    return await self.get_nodes_known(neighbors_too=False)

meet_node(node) async

Update the list of nodes known on federation

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
71
72
73
74
75
76
77
78
79
80
async def meet_node(self, node):
    """
    Update the list of nodes known on federation
    """
    self.nodes_known_lock.acquire()
    if node != self.addr:
        if node not in self.nodes_known:
            logging.info(f"Update nodes known | addr: {node}")
        self.nodes_known.add(node)
    self.nodes_known_lock.release()

need_more_neighbors() async

Fully connected network requires to be connected to all devices, therefore, if there are more nodes known that self.neighbors, more neighbors are required

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
36
37
38
39
40
41
42
43
44
async def need_more_neighbors(self):
    """
    Fully connected network requires to be connected to all devices, therefore,
    if there are more nodes known that self.neighbors, more neighbors are required
    """
    self.neighbors_lock.acquire()
    need_more = len(self.neighbors) < len(self.nodes_known)
    self.neighbors_lock.release()
    return need_more

set_config(config) async

Source code in nebula/core/situationalawareness/awareness/sanetwork/neighborpolicies/fcneighborpolicy.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
async def set_config(self, config):
    """
    Args:
        config[0] -> list of self neighbors
        config[1] -> list of nodes known on federation
        config[2] -> self addr
        config[3] -> stricted_topology
    """
    logging.info("Initializing Fully-Connected Topology Neighbor Policy")
    self.neighbors_lock.acquire()
    self.neighbors = config[0]
    self.neighbors_lock.release()
    for addr in config[1]:
        self.nodes_known.add(addr)
    self.addr