Newer
Older
package nucleus
import (
"fmt"
spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
Fabian Seidl
committed
"code.fbi.h-da.de/danet/gosdn/controller/event"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
Fabian Seidl
committed
eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
log "github.com/sirupsen/logrus"
Fabian Seidl
committed
const (
// DeviceEventTopic is the used topic for device related entity changes.
DeviceEventTopic = "device"
)
// DeviceService provides a device service implementation.
// This services provides abstraction between the user (e.g a PND) and the matching store (e.g. deviceStore).
type DeviceService struct {
Fabian Seidl
committed
deviceStore device.Store
sbiService southbound.Service
eventService eventInterfaces.Service
}
// NewDeviceService creates a device service.
Fabian Seidl
committed
func NewDeviceService(
deviceStore device.Store,
sbiService southbound.Service,
eventService eventInterfaces.Service,
) device.Service {
return &DeviceService{
Fabian Seidl
committed
deviceStore: deviceStore,
sbiService: sbiService,
eventService: eventService,
}
}
// Get takes a Device's UUID or name and returns the Device.
func (s *DeviceService) Get(query store.Query) (device.Device, error) {
loadedDevice, err := s.deviceStore.Get(query)
if err != nil {
return nil, err
}
device, err := s.createDeviceFromStore(loadedDevice)
if err != nil {
return nil, err
}
return device, nil
}
// GetAll returns all stored devices.
Malte Bauch
committed
func (s *DeviceService) GetAll() ([]device.Device, error) {
var devices []device.Device
loadedDevices, err := s.deviceStore.GetAll()
if err != nil {
return nil, err
}
for _, loadedDevice := range loadedDevices {
Malte Bauch
committed
device, err := s.createDeviceFromStore(loadedDevice)
if err != nil {
return nil, err
}
devices = append(devices, device)
}
return devices, nil
}
// GetAllAsLoaded returns all stored devices as LoadedDevice.
// This method should be used if there is no need for a device.Device, since
// requesting device information through this method is a lot faster than the
// usual `GetAll` method.
func (s *DeviceService) GetAllAsLoaded() ([]device.LoadedDevice, error) {
loadedDevices, err := s.deviceStore.GetAll()
if err != nil {
return nil, err
return loadedDevices, nil
}
// Add adds a device to the device store.
func (s *DeviceService) Add(deviceToAdd device.Device) error {
err := s.deviceStore.Add(deviceToAdd)
if err != nil {
return err
}
if err := s.eventService.PublishEvent(DeviceEventTopic, event.NewAddEvent(deviceToAdd.ID())); err != nil {
log.Error(err)
}
Fabian Seidl
committed
return nil
}
// UpdateModel updates a existing device with a new model provided as string.
func (s *DeviceService) UpdateModel(deviceToUpdate device.Device, modelAsString string) error {
exisitingDevice, err := s.Get(store.Query{ID: deviceToUpdate.ID()})
if err != nil {
return 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 err
}
// Use unmarshall from the devices SBI to unmarshall ygot json in go struct.
err = exisitingDevice.SBI().Unmarshal([]byte(modelAsString), path, exisitingDevice.GetModel())
if err != nil {
return err
}
err = s.deviceStore.Update(exisitingDevice)
if err != nil {
return err
}
if err := s.eventService.PublishEvent(DeviceEventTopic, event.NewUpdateEvent(deviceToUpdate.ID())); err != nil {
log.Error(err)
}
return nil
}
// Update updates a existing device.
func (s *DeviceService) Update(deviceToUpdate device.Device) error {
err := s.deviceStore.Update(deviceToUpdate)
if err != nil {
return err
}
if err := s.eventService.PublishEvent(DeviceEventTopic, event.NewUpdateEvent(deviceToUpdate.ID())); err != nil {
log.Error(err)
}
Fabian Seidl
committed
return nil
}
// Delete deletes a device from the device store.
func (s *DeviceService) Delete(deviceToDelete device.Device) error {
err := s.deviceStore.Delete(deviceToDelete)
if err != nil {
return err
}
Shrey Garg
committed
if deviceToDelete.SBI().Type() == spb.Type_TYPE_PLUGIN {
err = s.sbiService.Delete(deviceToDelete.SBI())
if err != nil {
return err
}
}
Fabian Seidl
committed
if err := s.eventService.PublishEvent(DeviceEventTopic, event.NewDeleteEvent(deviceToDelete.ID())); err != nil {
log.Error(err)
}
Fabian Seidl
committed
return nil
}
func (s *DeviceService) createDeviceFromStore(loadedDevice device.LoadedDevice) (device.Device, error) {
if loadedDevice.SBI == "" {
return nil, fmt.Errorf("no sbi found for device")
}
sbiForDevice, err := s.sbiService.Get(store.Query{ID: uuid.MustParse(loadedDevice.SBI)})
if err != nil {
return nil, err
}
d, err := NewDevice(
loadedDevice.Name,
uuid.MustParse(loadedDevice.ID),
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
212
&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
}