Skip to content
Snippets Groups Projects
deviceService.go 5.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • package nucleus
    
    import (
    	"fmt"
    
    	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
    
    	"code.fbi.h-da.de/danet/gosdn/controller/event"
    
    	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
    
    	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"
    
    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 {
    
    	deviceStore  device.Store
    	sbiService   southbound.Service
    	eventService eventInterfaces.Service
    
    }
    
    // NewDeviceService creates a device service.
    
    func NewDeviceService(
    	deviceStore device.Store,
    	sbiService southbound.Service,
    	eventService eventInterfaces.Service,
    ) device.Service {
    
    	return &DeviceService{
    
    		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
    	}
    
    
    	loadedDevice.SetConvertFunction(s.createDeviceFromStore)
    
    
    	device, err := s.createDeviceFromStore(loadedDevice)
    	if err != nil {
    		return nil, err
    	}
    
    	return device, nil
    }
    
    // GetAll returns all stored devices.
    
    func (s *DeviceService) GetAll() ([]device.LoadedDevice, error) {
    
    	loadedDevices, err := s.deviceStore.GetAll()
    	if err != nil {
    		return nil, err
    	}
    
    	for _, loadedDevice := range loadedDevices {
    
    		loadedDevice.SetConvertFunction(s.createDeviceFromStore)
    
    }
    
    // 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
    	}
    
    
    	pubEvent := event.NewAddEvent(deviceToAdd.ID())
    	if err := s.eventService.PublishEvent(DeviceEventTopic, pubEvent); err != nil {
    
    		var retryErr error
    		go func() {
    			retryErr = s.eventService.RetryPublish(DeviceEventTopic, pubEvent)
    		}()
    
    		if retryErr != nil {
    
    			log.Error(retryErr)
    		}
    
    // 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
    	}
    
    
    	pubEvent := event.NewUpdateEvent(deviceToUpdate.ID())
    	if err := s.eventService.PublishEvent(DeviceEventTopic, pubEvent); err != nil {
    
    		var retryErr error
    		go func() {
    			retryErr = s.eventService.RetryPublish(DeviceEventTopic, pubEvent)
    		}()
    
    		if retryErr != nil {
    
    			log.Error(retryErr)
    		}
    
    // Update updates a existing device.
    func (s *DeviceService) Update(deviceToUpdate device.Device) error {
    	err := s.deviceStore.Update(deviceToUpdate)
    	if err != nil {
    		return err
    	}
    
    
    	pubEvent := event.NewUpdateEvent(deviceToUpdate.ID())
    	if err := s.eventService.PublishEvent(DeviceEventTopic, pubEvent); err != nil {
    
    		var retryErr error
    		go func() {
    			retryErr = s.eventService.RetryPublish(DeviceEventTopic, pubEvent)
    		}()
    
    		if retryErr != nil {
    
    			log.Error(retryErr)
    		}
    
    	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
    	}
    
    	if deviceToDelete.SBI().Type() == spb.Type_TYPE_PLUGIN {
    		err = s.sbiService.Delete(deviceToDelete.SBI())
    		if err != nil {
    			return err
    		}
    	}
    
    	pubEvent := event.NewDeleteEvent(deviceToDelete.ID())
    	if err := s.eventService.PublishEvent(DeviceEventTopic, pubEvent); err != nil {
    
    		var retryErr error
    		go func() {
    			retryErr = s.eventService.RetryPublish(DeviceEventTopic, pubEvent)
    		}()
    
    		if retryErr != nil {
    
    			log.Error(retryErr)
    		}
    
    	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),
    
    		&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
    }