diff --git a/database/client.go b/database/client.go index ecd24e25c13eb8af4d0522f4bfe00958041cabb3..10e5414835fb523e273eecb87bf011535617f955 100644 --- a/database/client.go +++ b/database/client.go @@ -7,19 +7,20 @@ import ( "github.com/spf13/viper" ) -//Database is a database +// Database is a database +// deprecated type Database struct { driver neo4j.Driver } -//PND is a principle network domain +// PND is a principle network domain type PND struct { name string description string interfaces []string } -//NewDatabaseClient creates a database ciena +// NewDatabaseClient creates a database ciena func NewDatabaseClient() Database { uri := viper.GetString("db.socket") username := viper.GetString("db.user") @@ -32,7 +33,7 @@ func NewDatabaseClient() Database { } } -//createDriver creates a neo4j.Driver instance +// createDriver creates a neo4j.Driver instance func createDriver(uri, username, password string, encrypted bool) neo4j.Driver { driver, err := neo4j.NewDriver( uri, @@ -49,7 +50,7 @@ func createDriver(uri, username, password string, encrypted bool) neo4j.Driver { return driver } -//createSession creates a neo4j.Session +// createSession creates a neo4j.Session func createSession(driver neo4j.Driver, write bool) neo4j.Session { var sessionConfig neo4j.SessionConfig @@ -68,7 +69,7 @@ func createSession(driver neo4j.Driver, write bool) neo4j.Session { return session } -//storePndTxFunc transaction to store a pnd in the database +// storePndTxFunc transaction to store a pnd in the database func storePndTxFunc(name, description string, interfaces []string) neo4j.TransactionWork { return func(tx neo4j.Transaction) (interface{}, error) { query := @@ -98,7 +99,7 @@ func storePndTxFunc(name, description string, interfaces []string) neo4j.Transac } } -//StorePND stores the given principle network domain +// StorePND stores the given principle network domain func (d Database) StorePND(pnd *PND) neo4j.Node { session := createSession(d.driver, true) defer session.Close() @@ -170,8 +171,8 @@ func storeNodesTxFunc(json string, id int64) neo4j.TransactionWork { } } -//StoreNodes stores the given nodes to the database and adds them to a -//principle networt domain (PND). It is required for a node to belong to a PND. +// StoreNodes stores the given nodes to the database and adds them to a +// principle networt domain (PND). It is required for a node to belong to a PND. func (d Database) StoreNodes(json string) []neo4j.Node { //TODO: remove this after testing and add own gRPC call for it testPND := PND{name: "test_PND", description: "very interesting", interfaces: []string{"TAPI", "RESTCONF"}} @@ -189,15 +190,15 @@ func (d Database) StoreNodes(json string) []neo4j.Node { return result.([]neo4j.Node) } -//RemoveNodes removes the given nodes and their relationships +// RemoveNodes removes the given nodes and their relationships func (d Database) RemoveNodes(json string) {} -//RemoveSingleNode removes the given node and their relationship by id. +// RemoveSingleNode removes the given node and their relationship by id. func (d Database) RemoveSingleNode(id string) {} -//storeLinksTxFunc transaction to store links from a json. -//creates relation between different devices. -//returns a slice of those created relations. +// storeLinksTxFunc transaction to store links from a json. +// creates relation between different devices. +// returns a slice of those created relations. func storeLinksTxFunc(json string) neo4j.TransactionWork { return func(tx neo4j.Transaction) (interface{}, error) { var relationsList []neo4j.Relationship @@ -235,7 +236,7 @@ func storeLinksTxFunc(json string) neo4j.TransactionWork { } } -//StoreLinks stores the links between nodes +// StoreLinks stores the links between nodes func (d Database) StoreLinks(json string) []neo4j.Relationship { session := createSession(d.driver, true) defer session.Close() @@ -250,8 +251,8 @@ func (d Database) StoreLinks(json string) []neo4j.Relationship { return result.([]neo4j.Relationship) } -//storeNodeEdgePointsTxFunc transaction to store interfaces from a json. -//returns count of added/updated interfaces +// storeNodeEdgePointsTxFunc transaction to store interfaces from a json. +// returns count of added/updated interfaces func storeNodeEdgePointsTxFunc(json string) neo4j.TransactionWork { return func(tx neo4j.Transaction) (interface{}, error) { query := @@ -287,8 +288,8 @@ func storeNodeEdgePointsTxFunc(json string) neo4j.TransactionWork { //TODO: currently this goes over each and every device/interface and adds // a interface_of relation. -> do it only for the newly added interfaces -//storeNodeEdgePointsRelationTxFunc transaction to create relations between interfaces and devices -//returns count of added/updated relations +// storeNodeEdgePointsRelationTxFunc transaction to create relations between interfaces and devices +// returns count of added/updated relations func storeNodeEdgePointsRelationTxFunc() neo4j.TransactionWork { return func(tx neo4j.Transaction) (interface{}, error) { query := @@ -314,7 +315,7 @@ func storeNodeEdgePointsRelationTxFunc() neo4j.TransactionWork { } } -//StoreNodeEdgePoints stores the given node edge points (interfaces) +// StoreNodeEdgePoints stores the given node edge points (interfaces) func (d Database) StoreNodeEdgePoints(json string) { session := createSession(d.driver, true) defer session.Close() @@ -332,27 +333,27 @@ func (d Database) StoreNodeEdgePoints(json string) { log.Info("added/updated nodeEdgePoints (count): ", result) } -//StoreConnections stores relations between nodes +// StoreConnections stores relations between nodes func (d Database) StoreConnections(json string) {} -//StoreTopology creates a new network topology node. Can also create a relation +// StoreTopology creates a new network topology node. Can also create a relation //the new node and a existing one if desired func StoreTopology() {} -//RemoveTopology removes the given network topology. This includes the node itself +// RemoveTopology removes the given network topology. This includes the node itself //aswell as the containing links and relations func RemoveTopology() {} -//CreateTopologyRelation creates a relation between two given topologies +// CreateTopologyRelation creates a relation between two given topologies func CreateTopologyRelation() {} -//CreateLink creates a link between two network elements +// CreateLink creates a link between two network elements func CreateLink() {} -//RemoveLink removes a link between two network elements +// RemoveLink removes a link between two network elements func RemoveLink() {} -//Shutdown closes the connection to the database +// Shutdown closes the connection to the database func (d Database) Shutdown() error { return d.driver.Close() } diff --git a/nucleus/controller.go b/nucleus/controller.go index a02b55424f458f6977a0b93849d8741b1056e384..21ef57b7b79cc5ded87b628e287dfe5ce30ee673 100644 --- a/nucleus/controller.go +++ b/nucleus/controller.go @@ -1,7 +1,6 @@ package nucleus import ( - "fmt" "os" "code.fbi.h-da.de/cocsn/gosdn/database" @@ -12,14 +11,27 @@ import ( // Core is the representation of the controllers core type Core struct { - southboundInterfaces map[string]SouthboundInterface + // deprecated, use sbiStore instead + southboundInterfaces map[string]SouthboundInterface + // deprecated, use pndStore instead principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain - database database.Database - IsRunning chan bool + // deprecated + database database.Database + + pndStore pndStorage + sbiStore sbiStorage + IsRunning chan bool } //Initialize does start-up housekeeping like reading controller config files -func (c *Core) Initialize(IsRunningChannel chan bool) { +func (c *Core) Initialize(IsRunningChannel chan bool) error { + c.sbiStore = sbiStorage{} + c.pndStore = pndStorage{} + + // TODO: Just for compatability remove once deprecated code is cleaned up + c.southboundInterfaces = c.sbiStore + c.principalNetworkDomains = c.pndStore + // Set config defaults viper.SetDefault("socket", "localhost:55055") @@ -30,25 +42,32 @@ func (c *Core) Initialize(IsRunningChannel chan bool) { viper.AddConfigPath("./configs/") err := viper.ReadInConfig() if err != nil { - log.Fatal(fmt.Errorf("Fatal error config file: %s \n", err)) + return err } c.AttachDatabase() - c.CreateSouthboundInterfaces() + if err := c.CreateSouthboundInterfaces(); err != nil { + return err + } c.IsRunning = IsRunningChannel + return nil } +// deprecated // AttachDatabase connects to the database and passes the connection to the controller core func (c *Core) AttachDatabase() { c.database = database.NewDatabaseClient() } -// CreateSouthboundInterfaces initializes the controller with his supported SBIs -func (c *Core) CreateSouthboundInterfaces() { - arista := &AristaOC{} - c.southboundInterfaces[arista.SbiIdentifier()] = arista - openconfig := &OpenConfig{} - c.southboundInterfaces[openconfig.SbiIdentifier()] = openconfig +// CreateSouthboundInterfaces initializes the controller with its supported SBIs +func (c *Core) CreateSouthboundInterfaces() error { + if err := c.sbiStore.add(&AristaOC{}); err != nil { + return err + } + if err := c.sbiStore.add(&OpenConfig{}); err != nil { + return err + } + return nil } // Shutdown waits for the shutdown signal and gracefully shuts down once it arrived diff --git a/nucleus/controller_test.go b/nucleus/controller_test.go index 0fa4ffa903a1f894325aaf5211341e19a2df754a..dd2a0175a06427bdea0d74e83fcd95cc5ff8b28f 100644 --- a/nucleus/controller_test.go +++ b/nucleus/controller_test.go @@ -1 +1,86 @@ package nucleus + +import ( + "code.fbi.h-da.de/cocsn/gosdn/database" + "github.com/google/uuid" + "testing" +) + +func TestCore_CreateSouthboundInterfaces(t *testing.T) { + type fields struct { + southboundInterfaces map[string]SouthboundInterface + principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain + database database.Database + IsRunning chan bool + } + tests := []struct { + name string + fields fields + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _ = &Core{ + southboundInterfaces: tt.fields.southboundInterfaces, + principalNetworkDomains: tt.fields.principalNetworkDomains, + database: tt.fields.database, + IsRunning: tt.fields.IsRunning, + } + }) + } +} + +func TestCore_Initialize(t *testing.T) { + type fields struct { + southboundInterfaces map[string]SouthboundInterface + principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain + database database.Database + IsRunning chan bool + } + type args struct { + IsRunningChannel chan bool + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _ = &Core{ + southboundInterfaces: tt.fields.southboundInterfaces, + principalNetworkDomains: tt.fields.principalNetworkDomains, + database: tt.fields.database, + IsRunning: tt.fields.IsRunning, + } + }) + } +} + +func TestCore_Shutdown(t *testing.T) { + type fields struct { + southboundInterfaces map[string]SouthboundInterface + principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain + database database.Database + IsRunning chan bool + } + tests := []struct { + name string + fields fields + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _ = &Core{ + southboundInterfaces: tt.fields.southboundInterfaces, + principalNetworkDomains: tt.fields.principalNetworkDomains, + database: tt.fields.database, + IsRunning: tt.fields.IsRunning, + } + }) + } +} diff --git a/nucleus/errors.go b/nucleus/errors.go index 5da9172dea497aac52183fb0233edef178d21295..67ebde4edd448263b1adaf0ac66cd05c94ccc6e9 100644 --- a/nucleus/errors.go +++ b/nucleus/errors.go @@ -9,10 +9,18 @@ func (e *ErrNilClient) Error() string { return fmt.Sprintf("client cannot be nil") } -type ErrDeviceNotFound struct { +type ErrNotFound struct { id interface{} } -func (e *ErrDeviceNotFound) Error() string { - return fmt.Sprintf("device not found. requested id: %v", e.id) +func (e *ErrNotFound) Error() string { + return fmt.Sprintf("%v not found", e.id) +} + +type ErrAlreadyExists struct { + item interface{} +} + +func (e *ErrAlreadyExists) Error() string { + return fmt.Sprintf("%v already exists", e.item) } diff --git a/nucleus/nucleus-core.go b/nucleus/nucleus-core.go index ae6d78056beb1e9c7d19e9385026f0117d033a91..cb21c84458485f492800bdd382128122e94a50dd 100644 --- a/nucleus/nucleus-core.go +++ b/nucleus/nucleus-core.go @@ -3,8 +3,6 @@ package nucleus import ( "time" - "code.fbi.h-da.de/cocsn/gosdn/database" - "github.com/google/uuid" log "github.com/sirupsen/logrus" ) @@ -14,12 +12,10 @@ func StartAndRun(IsRunningChannel chan bool) { log.Info("Starting my ducks") // Initialize the Core - core := Core{ - principalNetworkDomains: make(map[uuid.UUID]PrincipalNetworkDomain), - southboundInterfaces: make(map[string]SouthboundInterface), - database: database.Database{}, + core := Core{} + if err := core.Initialize(IsRunningChannel); err != nil { + log.Fatal(err) } - core.Initialize(IsRunningChannel) // Start the GRCP CLI go getCLIGoing(&core) go core.Shutdown() diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go index bda39a39ad088e5ddf298b1a3c514144a0f4d9f6..2ed3301151c1c1843a16ef7ff814b040df863851 100644 --- a/nucleus/principalNetworkDomain.go +++ b/nucleus/principalNetworkDomain.go @@ -29,6 +29,7 @@ type pndImplementation struct { description string sbi map[string]SouthboundInterface devices map[uuid.UUID]*Device + id uuid.UUID } // NewPND creates a Principle Network Domain @@ -117,7 +118,7 @@ func (pnd *pndImplementation) addDevice(device *Device) error { func (pnd *pndImplementation) getDevice(uuid uuid.UUID) (*Device, error) { d, ok := pnd.devices[uuid] if !ok { - return nil, &ErrDeviceNotFound{id: uuid} + return nil, &ErrNotFound{id: uuid} } return d, nil } @@ -167,3 +168,31 @@ func (pnd *pndImplementation) RequestAll(path string) error { } return nil } + +type pndStorage map[uuid.UUID]PrincipalNetworkDomain + +func (p pndStorage) exists(id uuid.UUID) bool { + _, ok := p[id] + return ok +} + +func (p pndStorage) add(pnd PrincipalNetworkDomain) error { + // TODO: Implement duplicate detection. Changes PrincipalNetworkDomain API + p[uuid.New()] = pnd + return nil +} + +func (p pndStorage) Sbi(id uuid.UUID) (PrincipalNetworkDomain, error) { + if !p.exists(id) { + return nil, &ErrNotFound{id: id} + } + return p[id], nil +} + +func (p pndStorage) delete(id uuid.UUID) error { + if !p.exists(id) { + return &ErrNotFound{id: id} + } + delete(p, id) + return nil +} diff --git a/nucleus/southbound.go b/nucleus/southbound.go index 2a31d78f270f01e0cb153cdca907ee9422cfbc25..7f22d24c89de3232c218aea8c79096d926bf3fbd 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -95,3 +95,33 @@ func (oc *AristaOC) SetNode() func(schema *yang.Entry, root interface{}, path *g return nil } } + +type sbiStorage map[string]SouthboundInterface + +func (s sbiStorage) exists(name string) bool { + _, ok := s[name] + return ok +} + +func (s sbiStorage) add(sbi SouthboundInterface) error { + if s.exists(sbi.SbiIdentifier()) { + return &ErrAlreadyExists{item: sbi} + } + s[sbi.SbiIdentifier()] = sbi + return nil +} + +func (s sbiStorage) Sbi(name string) (SouthboundInterface, error) { + if !s.exists(name) { + return nil, &ErrNotFound{id: name} + } + return s[name], nil +} + +func (s sbiStorage) delete(name string) error { + if !s.exists(name) { + return &ErrNotFound{id: name} + } + delete(s, name) + return nil +}