Newer
Older
"encoding/json"
"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 {
deviceStoreName string
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
}
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
}