Skip to content
Snippets Groups Projects
deviceStore.go 5.97 KiB
Newer Older
  • Learn to ignore specific revisions
  • package store
    
    import (
    
    	"reflect"
    
    	"code.fbi.h-da.de/danet/gosdn/interfaces/device"
    	"code.fbi.h-da.de/danet/gosdn/interfaces/store"
    	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
    
    	"github.com/google/uuid"
    	log "github.com/sirupsen/logrus"
    )
    
    // DeviceStore is used to store Devices
    type DeviceStore struct {
    
    	DeviceNameToUUIDLookup map[string]uuid.UUID
    	*genericStore
    }
    
    // NewDeviceStore returns a DeviceStore
    
    func NewDeviceStore(pndUUID uuid.UUID) *DeviceStore {
    	return &DeviceStore{
    		genericStore:           newGenericStore(),
    		DeviceNameToUUIDLookup: make(map[string]uuid.UUID),
    		deviceStoreName:        fmt.Sprintf("device-store-%s.json", pndUUID.String()),
    	}
    
    }
    
    // GetDevice takes a Device's UUID and returns the Device. If the requested
    // Device does not exist an error is returned.
    func (s *DeviceStore) GetDevice(id uuid.UUID, parseErrors ...error) (device.Device, error) {
    	var foundID uuid.UUID
    
    	foundID = id
    
    
    	for _, parseErr := range parseErrors {
    		if parseErr != nil {
    			switch e := parseErr.(type) {
    
    			case *errors.ErrInvalidUUID:
    				myID, ok := s.DeviceNameToUUIDLookup[e.DeviceName]
    				if !ok {
    					log.Debug(fmt.Sprintf("no device named %s found", foundID))
    
    					return nil, &errors.ErrNotFound{ID: foundID}
    
    				}
    				foundID = myID
    			}
    		}
    	}
    
    	item, err := s.genericStore.Get(foundID)
    	if err != nil {
    		return nil, err
    	}
    	d, ok := item.(device.Device)
    	if !ok {
    		return nil, &errors.ErrInvalidTypeAssertion{
    			Value: d,
    
    			Type:  (*device.Device)(nil),
    
    		}
    	}
    	log.WithFields(log.Fields{
    		"uuid": foundID,
    		"name": d.Name(),
    	}).Debug("device was accessed")
    
    	return d, nil
    }
    
    
    // GetDevicesAssociatedWithSbi ranges over devices within the device store and
    // checks if they are associated with the provided SBI. Returns a slice of
    // device.Device with all associated devices.
    func (s *DeviceStore) GetDevicesAssociatedWithSbi(sid uuid.UUID) ([]device.Device, error) {
    	var devices []device.Device
    	// range over all storable items within the device store
    	for _, item := range s.Store {
    		d, ok := item.(device.Device)
    		if !ok {
    			return nil, &errors.ErrInvalidTypeAssertion{
    				Value: d,
    				Type:  reflect.TypeOf((*device.Device)(nil)),
    			}
    		}
    		// check if the device uses the provided SBI and add it to the devices
    		// slice.
    		if d.SBI().ID() == sid {
    			devices = append(devices, d)
    		}
    	}
    
    	return devices, nil
    }
    
    
    // Add adds a device to the device store.
    // It also adds the name of the device to the lookup table.
    func (s *DeviceStore) Add(item store.Storable, name string) error {
    	if s.Exists(item.ID()) {
    		return &errors.ErrAlreadyExists{Item: item}
    	}
    
    	s.DeviceNameToUUIDLookup[name] = item.ID()
    
    	s.storeLock.Lock()
    	s.genericStore.Store[item.ID()] = item
    	s.storeLock.Unlock()
    
    	log.WithFields(log.Fields{
    		"type": reflect.TypeOf(item),
    		"uuid": item.ID(),
    	}).Debug("storable was added")
    
    
    	err := s.persist(item, name)
    	if err != nil {
    		return err
    	}
    
    
    	return nil
    }
    
    // Delete deletes a device from the device store.
    func (s *DeviceStore) Delete(id uuid.UUID) error {
    	if !s.Exists(id) {
    		return &errors.ErrNotFound{ID: id}
    	}
    
    	s.storeLock.Lock()
    	delete(s.genericStore.Store, id)
    	s.storeLock.Unlock()
    
    	for key, value := range s.DeviceNameToUUIDLookup {
    		if value == id {
    			delete(s.DeviceNameToUUIDLookup, key)
    		}
    	}
    
    	log.WithFields(log.Fields{
    		"uuid": id,
    	}).Debug("storable was deleted")
    
    	return nil
    }
    
    
    func (s *DeviceStore) persist(item store.Storable, name string) error {
    	ensureFilesystemStorePathExists(s.deviceStoreName)
    
    	_, ok := item.(device.Device)
    	if !ok {
    		return fmt.Errorf("item is no Device. got=%T", item)
    	}
    
    	var devicesToPersist []device.Device
    
    	for _, value := range s.genericStore.Store {
    		dev, ok := value.(device.Device)
    		if !ok {
    			return fmt.Errorf("item is no Device. got=%T", item)
    		}
    		devicesToPersist = append(devicesToPersist, dev)
    	}
    
    	storeDataAsJSON, err := json.MarshalIndent(devicesToPersist, "", " ")
    	if err != nil {
    		return err
    	}
    
    	err = ioutil.WriteFile(getCompletePathToFileStore(s.deviceStoreName), storeDataAsJSON, 0644)
    	if err != nil {
    		return err
    	}
    
    	return nil
    }
    
    // LoadedDevice represents a Orchestrated Networking Device that was loaeded
    // by using the Load() method of the DeviceStore.
    type LoadedDevice struct {
    	// DeviceID represents the UUID of the LoadedDevice.
    	DeviceID uuid.UUID `json:"id,omitempty"`
    	// Name represents the name of the LoadedDevice.
    	Name string `json:"name,omitempty"`
    	// TransportType represent the type of the transport in use of the LoadedDevice.
    	TransportType string `json:"transport_type,omitempty"`
    	// TransportAddress represents the address from which the device can be reached via the transport method.
    	TransportAddress string `json:"transport_address,omitempty"`
    	// TransportUsername is used for authentication via the transport method in use.
    	TransportUsername string `json:"transport_username,omitempty"`
    	// TransportPassword is used for authentication via the transport method in use.
    	TransportPassword   string `json:"transport_password,omitempty"`
    	TransportOptionCsbi bool   `json:"transport_option_csbi,omitempty"`
    	// SBI indicates the southbound interface, which is used by this device as UUID.
    	SBI uuid.UUID `json:"sbi,omitempty"`
    }
    
    // ID returns the ID of the LoadedDevice as UUID.
    func (ld LoadedDevice) ID() uuid.UUID {
    	return ld.DeviceID
    }
    
    // Load unmarshals the contents of the storage file associated with a DeviceStore
    // and returns it as []LoadedDevice.
    func (s *DeviceStore) Load() ([]LoadedDevice, error) {
    	var loadedDevices []LoadedDevice
    
    	err := ensureFilesystemStorePathExists(s.deviceStoreName)
    	if err != nil {
    		log.Debug(fmt.Printf("Err: %+v\n", err))
    		return loadedDevices, err
    	}
    
    	dat, err := ioutil.ReadFile(getCompletePathToFileStore(s.deviceStoreName))
    	if err != nil {
    		log.Debug(fmt.Printf("Err: %+v\n", err))
    		return loadedDevices, err
    	}
    
    	err = json.Unmarshal(dat, &loadedDevices)
    	if err != nil {
    		log.Debug(fmt.Printf("Err: %+v\n", err))
    		return loadedDevices, err
    	}
    
    	return loadedDevices, nil
    }