Newer
Older
package nucleus
import (
"fmt"
spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/database"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
"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/options"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
// DatabaseDeviceStore is used to store Devices
type DatabaseDeviceStore struct {
storeName string
sbiStore southbound.SbiStore
}
// NewDatabaseDeviceStore returns a DeviceStore
func NewDatabaseDeviceStore(pndUUID uuid.UUID, sbiStore southbound.SbiStore) device.Store {
return &DatabaseDeviceStore{
storeName: fmt.Sprintf("device-store-%s.json", pndUUID.String()),
sbiStore: sbiStore,
}
}
// Get takes a Device's UUID or name and returns the Device.
func (s *DatabaseDeviceStore) Get(query store.Query) (device.Device, error) {
var loadedDevice LoadedDevice
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: query.ID}})
if result == nil {
return nil, errors.ErrCouldNotFind{StoreName: deviceStoreName}
}
err := result.Decode(&loadedDevice)
if err != nil {
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: query.Name}})
if result == nil {
return nil, errors.ErrCouldNotFind{StoreName: deviceStoreName}
}
err := result.Decode(&loadedDevice)
if err != nil {
log.Printf("Failed marshalling %v", err)
return nil, errors.ErrCouldNotFind{StoreName: deviceStoreName}
if err != nil {
return nil, err
}
}
// GetAll returns all stored devices.
func (s *DatabaseDeviceStore) GetAll() ([]device.Device, error) {
var loadedDevices []LoadedDevice
var devices []device.Device
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
cursor, err := collection.Find(ctx, bson.D{})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &loadedDevices)
if err != nil {
log.Printf("Failed marshalling %v", err)
return nil, errors.ErrCouldNotMarshall{StoreName: pndStoreName}
}
for _, loadedDevice := range loadedDevices {
device, err := s.createDeviceFromStore(loadedDevice)
if err != nil {
return nil, err
}
}
return devices, nil
}
// Add adds a device to the device store.
func (s *DatabaseDeviceStore) Add(deviceToAdd device.Device) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
_, err := client.Database(database.DatabaseName).
Collection(s.storeName).
InsertOne(ctx, deviceToAdd)
if err != nil {
log.Printf("Could not create Device: %v", err)
return errors.ErrCouldNotCreate{StoreName: pndStoreName}
}
return nil
}
// Update updates a existing device.
func (s *DatabaseDeviceStore) Update(deviceToUpdate device.Device) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
update := bson.D{primitive.E{Key: "$set", Value: deviceToUpdate}}
upsert := false
after := options.After
opt := options.FindOneAndUpdateOptions{
Upsert: &upsert,
ReturnDocument: &after,
}
err := client.Database(database.DatabaseName).
Collection(s.storeName).
FindOneAndUpdate(
ctx, bson.M{"_id": deviceToUpdate.ID().String()}, update, &opt).
Decode(&updatedLoadedDevice)
if err != nil {
log.Printf("Could not update Device: %v", err)
return errors.ErrCouldNotUpdate{StoreName: pndStoreName}
}
return nil
}
// Delete deletes a device from the device store.
func (s *DatabaseDeviceStore) Delete(deviceToDelete device.Device) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
_, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: deviceToDelete.ID().String()}})
if err != nil {
return err
}
return nil
}
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
func (s *DatabaseDeviceStore) createDeviceFromStore(loadedDevice LoadedDevice) (device.Device, error) {
sbiForDevice, err := s.sbiStore.Get(store.Query{ID: uuid.MustParse(loadedDevice.SBI)})
if err != nil {
return nil, err
}
d, err := NewDevice(
loadedDevice.Name,
uuid.MustParse(loadedDevice.DeviceID),
&tpb.TransportOption{
Address: loadedDevice.TransportAddress,
Username: loadedDevice.TransportUsername,
Password: loadedDevice.TransportPassword,
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{},
},
Type: spb.Type_TYPE_OPENCONFIG,
}, sbiForDevice)
if err != nil {
return nil, err
}
// Create 'root' path to be able to load the whole model from the store.
path, err := ygot.StringToPath("/", ygot.StructuredPath)
if err != nil {
return nil, err
}
// Use unmarshall from the devices SBI to unmarshall ygot json in go struct.
err = d.SBI().Unmarshal([]byte(loadedDevice.Model), path, d.GetModel())
if err != nil {
return nil, err
}
return d, nil
}