package nucleus import ( "encoding/json" "io/ioutil" "sync" "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/errors" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) // FilesystemDeviceStore is the filesystem implementation of the device store type FilesystemDeviceStore struct { sbiStore southbound.Store pndUUID uuid.UUID fileMutex sync.Mutex pathToDeviceFile string } // NewFilesystemDeviceStore returns a filesystem implementation for a pnd store. func NewFilesystemDeviceStore(pndUUID uuid.UUID) device.Store { deviceFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, filesystem.DeviceFilenameSuffix) store.EnsureFilesystemStorePathExists(deviceFilenameForUUID) return &FilesystemDeviceStore{ pathToDeviceFile: store.GetCompletePathToFileStore(deviceFilenameForUUID), fileMutex: sync.Mutex{}, pndUUID: pndUUID, } } func (s *FilesystemDeviceStore) readAllDevicesFromFile() ([]device.LoadedDevice, error) { var loadedDevices []device.LoadedDevice content, err := ioutil.ReadFile(s.pathToDeviceFile) if err != nil { return nil, err } err = json.Unmarshal(content, &loadedDevices) if err != nil { return nil, err } return loadedDevices, nil } func (s *FilesystemDeviceStore) writeAllDevicesToFile(devices []device.LoadedDevice) error { serializedData, err := json.Marshal(devices) if err != nil { return err } err = ioutil.WriteFile(s.pathToDeviceFile, serializedData, 0600) if err != nil { return err } return nil } // Get takes a Device's UUID or name and returns the Device. func (s *FilesystemDeviceStore) Get(query store.Query) (device.LoadedDevice, error) { s.fileMutex.Lock() defer s.fileMutex.Unlock() var device device.LoadedDevice devices, err := s.readAllDevicesFromFile() if err != nil { return device, err } for _, device := range devices { if device.ID == query.ID.String() { return device, nil } } return device, &errors.ErrNotFound{ID: query.ID} } // GetAll returns all stored devices. func (s *FilesystemDeviceStore) GetAll() ([]device.LoadedDevice, error) { s.fileMutex.Lock() defer s.fileMutex.Unlock() devices, err := s.readAllDevicesFromFile() return devices, err } // Add adds a device to the device store. func (s *FilesystemDeviceStore) Add(deviceToAdd device.Device) error { s.fileMutex.Lock() defer s.fileMutex.Unlock() devices, err := s.readAllDevicesFromFile() if err != nil { return err } var loadedDevice device.LoadedDevice loadedDevice, err = store.TransformObjectToLoadedObject[device.Device, device.LoadedDevice](deviceToAdd) if err != nil { return err } devices = append(devices, loadedDevice) err = s.writeAllDevicesToFile(devices) if err != nil { return err } return nil } // Update updates a existing device. func (s *FilesystemDeviceStore) Update(deviceToUpdate device.Device) error { s.fileMutex.Lock() defer s.fileMutex.Unlock() loadedDeviceToUpdate, err := store.TransformObjectToLoadedObject[device.Device, device.LoadedDevice](deviceToUpdate) devices, err := s.readAllDevicesFromFile() if err != nil { return err } for i, device := range devices { if device.ID == deviceToUpdate.ID().String() { devices[i] = loadedDeviceToUpdate err = s.writeAllDevicesToFile(devices) if err != nil { return err } return nil } } return &errors.ErrNotFound{ID: deviceToUpdate.ID().String()} } // Delete deletes a device from the device store. func (s *FilesystemDeviceStore) Delete(deviceToDelete device.Device) error { s.fileMutex.Lock() defer s.fileMutex.Unlock() devices, err := s.readAllDevicesFromFile() if err != nil { return err } for i, device := range devices { if device.ID == deviceToDelete.ID().String() { //remove item from slice devices[i] = devices[len(devices)-1] devices = devices[:len(devices)-1] err = s.writeAllDevicesToFile(devices) if err != nil { return err } return nil } } return &errors.ErrNotFound{ID: deviceToDelete.ID} }