Newer
Older
package nucleus
import (
"fmt"
"code.fbi.h-da.de/danet/gosdn/controller/customerrs"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/database"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
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
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
// DatabaseNetworkElementStore is used to store Network Elements.
type DatabaseNetworkElementStore struct {
storeName string
}
// NewDatabaseNetworkElementStore returns a NetworkElementStore.
func NewDatabaseNetworkElementStore(pndUUID uuid.UUID) networkelement.Store {
return &DatabaseNetworkElementStore{
storeName: fmt.Sprintf("networkElement-store-%s.json", pndUUID.String()),
}
}
// Get takes a NetworkElement's UUID or name and returns the NetworkElement.
func (s *DatabaseNetworkElementStore) Get(query store.Query) (networkelement.LoadedNetworkElement, error) {
var loadedNetworkElement networkelement.LoadedNetworkElement
if query.ID.String() != "" {
loadedNetworkElement, err := s.getByID(query.ID)
if err != nil {
return loadedNetworkElement, err
}
return loadedNetworkElement, nil
}
loadedNetworkElement, err := s.getByName(query.Name)
if err != nil {
return loadedNetworkElement, err
}
return loadedNetworkElement, nil
}
func (s *DatabaseNetworkElementStore) getByID(idOfNetworkElement uuid.UUID) (loadedNetworkElement networkelement.LoadedNetworkElement, err error) {
client, ctx, cancel, err := database.GetMongoConnection()
if err != nil {
return loadedNetworkElement, err
}
defer cancel()
defer func() {
if ferr := client.Disconnect(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfNetworkElement.String()}})
if result == nil {
return loadedNetworkElement, customerrs.CouldNotFindError{ID: idOfNetworkElement}
}
err = result.Decode(&loadedNetworkElement)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedNetworkElement, customerrs.CouldNotMarshallError{Identifier: idOfNetworkElement, Type: loadedNetworkElement, Err: err}
}
return loadedNetworkElement, nil
}
func (s *DatabaseNetworkElementStore) getByName(nameOfNetworkElement string) (loadedNetworkElement networkelement.LoadedNetworkElement, err error) {
client, ctx, cancel, err := database.GetMongoConnection()
if err != nil {
return loadedNetworkElement, err
}
defer cancel()
defer func() {
if ferr := client.Disconnect(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfNetworkElement}})
if result == nil {
return loadedNetworkElement, customerrs.CouldNotFindError{Name: nameOfNetworkElement}
}
err = result.Decode(&loadedNetworkElement)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedNetworkElement, customerrs.CouldNotMarshallError{Identifier: nameOfNetworkElement, Type: loadedNetworkElement, Err: err}
}
return loadedNetworkElement, nil
}
// GetAll returns all stored network elements.
func (s *DatabaseNetworkElementStore) GetAll() (loadedNetworkElements []networkelement.LoadedNetworkElement, err error) {
client, ctx, cancel, err := database.GetMongoConnection()
if err != nil {
return nil, err
}
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
defer cancel()
defer func() {
if ferr := client.Disconnect(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
cursor, err := collection.Find(ctx, bson.D{})
if err != nil {
return nil, err
}
defer func() {
if ferr := cursor.Close(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
err = cursor.All(ctx, &loadedNetworkElements)
if err != nil {
log.Printf("Failed marshalling %v", err)
return nil, customerrs.CouldNotMarshallError{Type: loadedNetworkElements, Err: err}
}
return loadedNetworkElements, nil
}
// Add adds a network element to the network element store.
func (s *DatabaseNetworkElementStore) Add(networkElementToAdd networkelement.NetworkElement) (err error) {
client, ctx, cancel, err := database.GetMongoConnection()
if err != nil {
return err
}
defer cancel()
defer func() {
if ferr := client.Disconnect(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
_, err = client.Database(database.DatabaseName).
Collection(s.storeName).
InsertOne(ctx, networkElementToAdd)
if err != nil {
log.Printf("Could not create NetworkElement: %v", err)
return customerrs.CouldNotCreateError{Identifier: networkElementToAdd.ID(), Type: networkElementToAdd, Err: err}
}
return nil
}
// Update updates a existing network element.
func (s *DatabaseNetworkElementStore) Update(networkElementToUpdate networkelement.NetworkElement) (err error) {
var updatedLoadedNetworkElement networkelement.LoadedNetworkElement
client, ctx, cancel, err := database.GetMongoConnection()
if err != nil {
return err
}
defer cancel()
defer func() {
if ferr := client.Disconnect(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
// 1. Start Transaction
wcMajority := writeconcern.Majority()
wcMajorityCollectionOpts := options.Collection().SetWriteConcern(wcMajority)
userCollection := client.Database(database.DatabaseName).Collection(s.storeName, wcMajorityCollectionOpts)
session, err := client.StartSession()
if err != nil {
return err
defer session.EndSession(ctx)
// 2. Fetch exisiting Entity
existingNetworkElement, err := s.getByID(networkElementToUpdate.ID())
if err != nil {
return err
}
// 3. Check if Entity.Metadata.ResourceVersion == UpdatedEntity.Metadata.ResourceVersion
if networkElementToUpdate.GetMetadata().ResourceVersion != existingNetworkElement.Metadata.ResourceVersion {
// 3.1.1 End transaction
// 3.1.2 If no -> return error
return fmt.Errorf(
"resource version %d of provided network element %s is older or newer than %d in the store",
networkElementToUpdate.GetMetadata().ResourceVersion,
networkElementToUpdate.ID().String(), existingNetworkElement.Metadata.ResourceVersion,
)
}
// 3.2.1 If yes -> Update entity in callback
callback := func(sessCtx mongo.SessionContext) (interface{}, error) {
// Important: You must pass sessCtx as the Context parameter to the operations for them to be executed in the
// transaction.
u, _ := networkElementToUpdate.(*CommonNetworkElement)
u.Metadata.ResourceVersion = u.Metadata.ResourceVersion + 1
update := bson.D{primitive.E{Key: "$set", Value: u}}
upsert := false
after := options.After
opt := options.FindOneAndUpdateOptions{
Upsert: &upsert,
ReturnDocument: &after,
}
err = userCollection.
FindOneAndUpdate(
ctx, bson.M{"_id": networkElementToUpdate.ID().String()}, update, &opt).
Decode(&updatedLoadedNetworkElement)
if err != nil {
log.Printf("Could not update network element: %v", err)
return nil, customerrs.CouldNotUpdateError{Identifier: networkElementToUpdate.ID(), Type: networkElementToUpdate, Err: err}
}
// 3.2.2 End transaction
return "", nil
}
_, err = session.WithTransaction(ctx, callback)
if err != nil {
return err
}
return nil
}
// Delete deletes a network element from the network element store.
func (s *DatabaseNetworkElementStore) Delete(networkElementToDelete networkelement.NetworkElement) (err error) {
client, ctx, cancel, err := database.GetMongoConnection()
if err != nil {
return err
}
defer cancel()
defer func() {
if ferr := client.Disconnect(ctx); ferr != nil {
fErrString := ferr.Error()
err = fmt.Errorf("InternalError=%w DeferError=%+s", err, fErrString)
}
}()
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
_, err = collection.DeleteOne(ctx, bson.D{primitive.E{Key: "_id", Value: networkElementToDelete.ID().String()}})
if err != nil {
return customerrs.CouldNotDeleteError{Identifier: networkElementToDelete.ID(), Type: networkElementToDelete, Err: err}
}
return nil
}