Modulo Identities - Analisi Funzionale

Ruolo del modulo

Il modulo Identities si occupa di organizzare le diverse identità che vivono nel nodo.

Il modulo Identities consente la creazione di una prima identità all'avvio del nodo.

In seguito il modulo, data una identità j, consente di creare una nuova identità i che prende il posto di j. In questo momento l'identità j resta in vita, ma in modo temporaneo. L'identità j diventa una identità di connettività. Si veda la trattazione del modulo QSPN per comprendere la funzione di una identità di connettività e per quale motivo essa sia temporanea.

In contrapposizione al significato di una identità di connettività introduciamo il concetto di identità principale. Un nodo ha sempre una e una sola identità principale. La prima identità del nodo che viene creata all'avvio è dunque l'identità principale del nodo.

Abbiamo detto che in seguito il nodo può creare una nuova identità i partendo da una precedente identità j. L'identità j poteva essere in precedenza l'identità principale oppure una identità di connettività. Dicendo che l'identità i prende il posto di j, intendiamo significare, tra le altre cose, che se j era la principale allora adesso i diventa la principale. Mentre se j era una identità di connettività allora adesso i diventa anch'essa una identità di connettività.

Ogni volta che il modulo crea una identità, inizializza un identificativo per tale identità, cioè una istanza di NodeID.

Il modulo Identities associa ad ogni identità che viene creata un numero di istanze di classi di altri moduli, chiamati membri dell'identità. Questi membri di identità sono semplicemente degli Object per il modulo Identities, in quanto esso non ha dipendenza sugli altri moduli dell'applicazione. Ogni membro è individuato con una stringa. Cioè, data una stringa ed una identità abbiamo un solo oggetto.

Il modulo Identities consente inoltre di gestire gli archi-identità. Il modulo viene portato a conoscenza di archi che collegano il nodo corrente ad un altro nodo diretto vicino. Sopra ogni arco poi l'utilizzatore può richiedere al modulo di aggiungere (o rimuovere) uno o più archi-identità che collegano una certa identità del nodo corrente con una certa identità del nodo vicino. In seguito il modulo potrà in autonomia aggiungere/modificare/rimuovere un arco-identità sulla base di operazioni di aggiunta/modifica/rimozione di identità (di norma a seguito di migrazioni) nel nodo corrente e/o nel nodo vicino. Le modifiche che il modulo Identities apporta in autonomia ad un arco-identità (cioè che non sono state richieste direttamente dall'utilizzatore del modulo) vengono notificate attraverso un segnale.

Sopra ogni arco arc esiste sempre un particolare arco-identità: quello che collega le due identità principali dei due nodi collegati dall'arco arc. Questo particolare arco-identità lo chiamiamo arco-identità principale dell'arco arc. Un tale arco-identità c'è sempre sopra ogni arco arc (fino alla completa rimozione dell'arco arc) anche se può cambiare nel tempo: infatti l'identità principale del nodo corrente o del nodo collegato tramite arc può cambiare.

Il modulo Identities consente infine al suo utilizzatore di richiedere la rimozione di una identità di connettività dal nodo corrente.

Network namespace

Il modulo Identities assume che la creazione di una nuova identità comporta nel nodo la replicazione dell'intero stack di gestione del networking (detto network stack). Quindi il modulo richiede che il sistema operativo sia in grado di realizzare questa replicazione. Volendo però essere agnostico rispetto al sistema operativo che gestisce il nodo, il modulo lascia che sia il suo utilizzatore ad eseguire le operazioni necessarie a richiedere al sistema operativo questa replicazione.

Il modulo assume che ogni replica del network stack possa essere associata ad un nome, che d'ora in poi chiamiamo network namespace. Il termine è preso in prestito dalla implementazione di questa feature presente nelle versioni recenti del kernel Linux. Inoltre assume di poter indicare con il nome "" (stringa vuota) il primo network namespace (o network namespace default) che è lo stesso sul quale è in esecuzione l'applicazione stessa.

Il modulo richiede che il sistema operativo sia in grado di realizzare la costruzione di una pseudo-interfaccia di rete sopra una interfaccia di rete reale e di rendere operativa tale pseudo-interfaccia in una particolare replica del network stack (associarla ad un namespace). Anche in questo caso, il modulo demanda al suo utilizzatore l'esecuzione delle operazioni necessarie a richiedere al sistema operativo questa costruzione.

Archi-identità

Abbiamo detto che il modulo sa quali archi collegano il nodo corrente ad un altro nodo diretto vicino. Per la precisione un arco arci collega una certa interfaccia di rete reale ifj del nodo corrente ad una certa interfaccia di rete reale del nodo vicino, di cui il nodo corrente conosce il MAC address arci.peer_mac e l'indirizzo link-local arci.peer_linklocal.

Sopra l'arco arci il modulo consente la creazione di uno o più archi-identità che collegano una certa identità del nodo corrente con una certa identità del nodo vicino. Consideriamo ad esempio l'identità idk. Il modulo Identities può creare l'arco-identità id-arc0 associato all'arco arci e alla sua identità idk e che collega alla identità del vicino bq. Può anche creare un altro arco-identità id-arc1, sempre associato all'arco arci e alla sua identità idk, ma che collega alla identità del vicino bw.

Limitiamoci a considerare l'arco-identità id-arc0. Consideriamo il vertice nel nostro nodo di id-arc0. Sappiamo che esso è associato all'arco arci, il quale è stato fatto sulla interfaccia di rete reale ifj del nodo corrente. Sappiamo anche che esso è associato all'identità idk, la quale è associata ad un particolare network namespace nsk. In questo network namespace, se non è quello default, opera una certa pseudo-interfaccia di rete costruita sull'interfaccia di rete reale ifj, chiamiamola ifj,k. Sappiamo che tale pseudo-interfaccia ha un suo MAC address e un suo indirizzo link-local che sono distinti da quelli di ifj.

Facciamo delle considerazioni analoghe per il vertice nel nodo vicino di id-arc0. Sappiamo che esso è associato all'identità del vicino bq. Questa potrebbe non essere l'identità associata al network namespace default: quindi vogliamo mantenere l'informazione id-arc0.peer_mac e id-arc0.peer_linklocal che possono differire da arci.peer_mac e arci.peer_linklocal.

Riassumendo, un arco-identità id-arc0 è associato ad una identità idk e ad un arco arci ed ha queste caratteristiche (o membri):

Aggiungiamo infine che, siccome vedremo in seguito che nel tempo nel nodo b può cambiare l'associazione fra l'identità bq e un network namespace, nel tempo i valori peer_mac e peer_linklocal di id-arc0 possono cambiare. Eventuali cambiamenti sono gestiti in autonomia dal modulo Identities, cioè non è l'utilizzatore del modulo a richiederli.

Operazioni

Il modulo fa uso delle tasklet, un sistema di multithreading cooperativo.

Il modulo fa uso del framework ZCD, precisamente appoggiandosi alla libreria di livello intermedio ntkdrpc prodotta con questo framework per formalizzare i metodi remoti usati nel demone ntkd.

Le operazioni del modulo sono implementate per la maggior parte in metodi di una classe chiamata IdentityManager. Di essa viene creata una sola istanza dall'applicazione in esecuzione su un nodo. Di seguito ci possiamo riferire a tale istanza semplicemente con il termine manager.

Poiché il manager è uno solo nel nodo, diciamo che il modulo Identities è un modulo di nodo. Si veda nella trattazione del modulo Neighborhood la differenza tra moduli di nodo e moduli di identità.

Nel costruire l'istanza del manager, l'utilizzatore del modulo specifica quali sono i nomi delle interfacce di rete reali gestite dal nodo, i relativi MAC e gli indirizzi link-local (che gli sono stati definitivamente assegnati dal modulo Neighborhood). Inoltre gli passa un manager di network namespace, o netns-manager, cioè un oggetto (si veda sotto la descrizione dell'interfaccia IIdmgmtNetnsManager) per svolgere le operazioni sui network namespace e le interfacce di rete. Inoltre gli passa una stub-factory, cioè un oggetto (si veda sotto la descrizione dell'interfaccia IIdmgmtStubFactory) per ottenere uno stub per comunicare con un vicino attraverso un arco.

Nel costruttore il manager crea la prima identità, chiamiamola id0. Il manager genera un identificativo per la prima identità, che è la principale. Chiamiamo identità principale quella associata al network namespace default e che gestisce quindi in esso le interfacce di rete reali. Il manager gli associa un namespace uguale a stringa vuota. Il manager inoltre, per ogni interfaccia di rete reale devi che gli è stata segnalata, associa alla coppia id0-devi una struttura dati con le informazioni della interfaccia gestita dalla identità: dev, mac, linklocal. Per la identità principale queste sono le interfacce reali.

L'utilizzatore dice al manager di associare alcuni oggetti alla identità appena creata: un oggetto per "qspn", un oggetto per "peerservices", eccetera.

In seguito, quando il nodo rileva la presenza di vicini, l'utilizzatore dice al manager che è stato formato un arco con un vicino, chiamiamolo arc0. Dall'oggetto arco (si veda sotto la descrizione dell'interfaccia IIdmgmtArc) si può risalire alla interfaccia di rete reale su cui è stato realizzato, chiamiamola ir(arc0). La prima volta questo evento accadrà quando il manager ha creato una sola identità. Ma in seguito nel tempo questo evento può accadere anche quando il manager ha più di una identità.

Il manager associa alla coppia id0-arc0 un contenitore, inizialmente vuoto, nel quale potranno essere aggiunte strutture dati che rappresentano un arco-identità.

Poi il manager realizza automaticamente il primo arco-identità, che è il principale di arc0. Cioè quello tra la propria identità principale id0 e l'identità principale del nodo vicino. Per farlo prima comunica con il vicino tramite l'arco arc0 e gli chiede l'identificativo della sua identità principale, chiamiamola bj. Di questa identità del vicino il nodo corrente conosce il MAC address e l'indirizzo link-local, in quanto tale identità gestisce le interfacce di rete reali. Dall'istanza arc0 (si veda sotto la descrizione dell'interfaccia IIdmgmtArc) si può risalire anche a questi dati.

Nel tempo, l'utilizzatore può:

Può capitare (vedremo fra poco in quali occasioni) che il modulo Identities in autonomia aggiunga o rimuova o modifichi un arco-identità. Questo evento è notificato all'utilizzatore del modulo con un segnale che esso può ascoltare.


Ad un certo punto l'utilizzatore del modulo può richiedere al manager di realizzare la duplicazione di una sua identità a causa di una migrazione.

Se la migrazione è di un singolo nodo allora le operazioni da fare sono alquanto semplici. Tuttavia anche in questo caso vedremo che un certo numero di comunicazioni vengono fatte ai nodi vicini e di questo si occupa il manager.

Se invece la migrazione è di un cluster di nodi allora le operazioni sono alquanto complesse. Si rende necessaria una concertazione delle operazioni, inizialmente con tutti i nodi del cluster e in seguito con i diretti vicini del nodo. La prima parte, cioè la concertazione con tutti i nodi del cluster, mira a far conoscere a tutti i nodi del cluster alcune informazioni sulla migrazione stessa e fra queste un identificativo di migrazione condiviso: questa parte non è di competenza del modulo Identities. La seconda parte, cioè la concertazione coi diretti vicini, mira a far sì che la duplicazione degli archi-identità avvenga in modo corretto anche fra due nodi che appartengono entrambi al cluster che migra.

Ad esempio, supponiamo che l'identità a0 nel nodo a e l'identità b0 nel nodo b siano collegate con un arco-identità. Supponiamo che entrambe le identità appartengano ad un cluster che migra e si formino a causa di questa migrazione le identità a1 nel nodo a e b1 nel nodo b. Allora deve persistere l'arco-identità a0-b0 e si deve aggiungere l'arco-identità a1-b1. Non devono invece formarsi né a0-b1a1-b0.

Invece, supponiamo che l'identità a0 nel nodo a e l'identità b0 nel nodo b siano collegate con un arco-identità. Supponiamo che solo l'identità a0 appartenga ad un cluster che migra e si formi a causa di questa migrazione l'identità a1. Allora deve persistere l'arco-identità a0-b0 e si deve aggiungere l'arco-identità a1-b0.

Quest'ultimo esempio vale anche per i casi in cui la migrazione sia di un singolo nodo, in questo esempio a0 nel nodo a.

Della concertazione coi diretti vicini si occupa il modulo Identities. Questa è particolarmente complessa quando entrambi i vicini partecipano (con una o più identità) alla migrazione.

Per realizzare questa concertazione si ha una prima fase in cui, tramite un meccanismo che non è di pertinenza del modulo Identities, su tutti i nodi del cluster viene chiamato il metodo prepare_add_identity(migration_id, old_id). Soltanto al termine, cioè quando tutti i nodi del cluster hanno processato il metodo prepare_add_identity, viene chiamato su tutti i nodi il metodo add_identity(migration_id, old_id).

Al resto pensa il modulo Identities.

Al termine delle operazioni, cioè al ritorno del metodo add_identity, l'utilizzatore del modulo viene portato a conoscenza dell'identificativo della nuova identità.


Ad un certo punto l'utilizzatore del modulo può richiedere al manager di rimuovere una sua identità idj.

Il manager per prima cosa rimuove tutti gli archi-identità associati a idj. Per ogni arco-identità id-arck associato a idj, indicando con arcq l'arco su cui esso è formato, il manager tenta anche (ma non è necessario che vi riesca) di comunicare al nodo vicino attraverso l'arco arcq che va rimosso l'arco tra idj e id-arck.peer_id. Se la comunicazione riesce, il manager nel nodo vicino ha l'opportunità di rimuovere l'arco-identità e notificarlo al suo utilizzatore con un apposito segnale.

Rimossi tutti gli archi, il manager usa il netns-manager per rimuovere le pseudo-interfacce e il network namespace associati a idj.

Rimuove infine dalla sua memoria tutte le associazioni che manteneva per idj.

Memoria del modulo

Il modulo mantiene un elenco, chiamato dev_list, dei nomi delle interfacce di rete reali attualmente gestite dal nodo.

Il modulo mantiene un elenco, chiamato arc_list, degli archi attualmente realizzati dal nodo con i suoi vicini. Da un arco può risalire al nome dell'interfaccia di rete reale su cui è stato realizzato e ai dati (MAC address e indirizzo link-local) dell'interfaccia di rete reale nel nodo diretto vicino.

Il modulo mantiene un elenco, chiamato id_list, delle identità attualmente presenti nel nodo. Inoltre mantiene un riferimento, chiamato main_id, alla identità principale.

Il modulo mantiene una associazione chiamata namespaces che partendo da una identità id0 del nodo corrente, con id0 ∈ id_list, individua il nome del network namespace che essa gestisce.

Il modulo mantiene una associazione chiamata handled_nics che partendo dalla coppia id0-devi (una identità e il nome di una interfaccia di rete reale), con id0 ∈ id_list e devi ∈ dev_list, individua la relativa istanza di HandledNic.

Il modulo mantiene una associazione chiamata identity_arcs che partendo dalla coppia id0-arc0 (una identità e un arco), con id0 ∈ id_list e arc0 ∈ arc_list, individua il relativo contenitore di istanze di IdentityArc.

Requisiti

L'utilizzatore deve fornire al modulo fin dalla sua inizializzazione questi requisiti:

Durante il tempo l'utilizzatore potrà fornire al modulo altre informazioni:

Deliverable

Il modulo Identities risponde a queste richieste:

Il modulo Identities permette queste operazioni:

Il modulo Identities segnala questi eventi:

Classi e interfacce

Il netns-manager è un oggetto di cui il modulo conosce l'interfaccia IIdmgmtNetnsManager. Tramite essa il modulo può:


La stub-factory è un oggetto di cui il modulo conosce l'interfaccia IIdmgmtStubFactory. Tramite essa il modulo può:


La classe Identity è interna al modulo. All'esterno del modulo una identità è rappresentata da un NodeID, sia per le identità del nodo corrente che per quelle dei vicini.

La classe Identity incapsula un NodeID. Fornisce inoltre un metodo to_string che produce (a partire dal NodeID) una stringa che la identifica univocamente.

Nella classe Identity vengono memorizzate le istanze delle classi dei moduli di identità. Cioè i membri dell'identità.


La classe usata per l'identificativo di una identità, cioè NodeID, è una classe serializzabile definita nella libreria Common. Il modulo Neighborhood ha una dipendenza su questa libreria, quindi conosce tale classe.

Il modulo Identities crea le istanze di questa classe relative alle identità di questo nodo.

Il modulo Identities riceve anche istanze di questa classe relative alle identità di nodi vicini. Naturalmente sa accedere alle informazioni in essa contenute.


Un arco che il nodo ha realizzato è rappresentato con una classe che il modulo non conosce. Il modulo espone l'interfaccia IIdmgmtArc che tale oggetto deve implementare.

L'interfaccia IIdmgmtArc consente di:


La classe HandledNic è una struttura dati interna al modulo. Essa si riferisce ad una interfaccia di rete (reale o pseudo) la quale è associata ad una data interfaccia di rete reale ed è gestita da una data identità del nodo corrente.

Ad esempio, se il modulo nel nodo a vuole recuperare il nome della pseudo-interfaccia di rete che la sua identità id0 (la quale non è la principale) gestisce e che è stata costruita sulla interfaccia reale "eth0", il modulo esamina l'associazione handled_nics(id0-eth0) e ottiene una istanza di HandledNic; quindi guarda il suo membro dev, che ad esempio contiene "ntkv0_eth0".

Relativamente a questa interfaccia di rete (reale o pseudo) la struttura dati contiene:


La classe IdentityArc è una struttura dati interna al modulo. Essa si riferisce ad una interfaccia di rete (reale o pseudo) gestita da una identità di un nodo vicino. Il vicino è collegato al nodo corrente tramite un dato arco e esiste un arco-identità tramite quella identità del nodo vicino e una data identità del nodo corrente.

Ad esempio, supponiamo che nel nodo corrente a abbiamo l'identità id0. Inoltre abbiamo un arco arc1 che collega una interfaccia del nodo a, diciamo ifa1, ad una interfaccia del nodo b, diciamo ifb1. Se voglio vedere quali archi-identità collegano l'identità id0 di a ad altre identità di b tramite l'arco arc1, allora il modulo esamina l'associazione identity_arcs(id0-arc1) e ottiene una lista di istanze di IdentityArc; le esamina una ad una accedendo ai suoi membri.

La classe IdentityArc contiene questi dati:

Forniamo la classe IdentityArc del metodo copy per facilitare la duplicazione degli archi-identità quando viene aggiunta una nuova identità al nodo.

L'interfaccia dell'oggetto arco-identità nota all'esterno del modulo, IIdmgmtIdentityArc, permette solo la lettura dei suddetti dati:


Il modulo Identities definisce la classe interna DuplicationData. Essa è serializzabile ed è usata come valore di ritorno nel metodo remoto match_duplication esposto dalla classe IdentityManager. Nella definizione dei metodi RPC si usa come segnaposto l'interfaccia IDuplicationData.

Questo metodo remoto è chiamato dal nodo corrente a sul nodo vicino b attraverso un dato arco arc0. Sopra tale arco esiste un arco-identità che collega l'identità a0 alla identità bj. Nel nodo a l'identità a0 è stata appena duplicata (a causa di una migrazione). La chiamata di questo metodo remoto serve a comunicare al nodo b le informazioni relative a questa duplicazione e a sapere dal nodo b se la stessa migrazione ha prodotto una duplicazione dell'identità bj. Se è così viene restituita una istanza di DuplicationData, altrimenti null.

La classe DuplicationData contiene: