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
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
"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 := database.GetMongoConnection()
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 := database.GetMongoConnection()
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 := database.GetMongoConnection()
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 := database.GetMongoConnection()
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 := database.GetMongoConnection()
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.New(writeconcern.WMajority())
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 := database.GetMongoConnection()
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
}