Skip to content
Snippets Groups Projects
networkElementService.go 7.75 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"
    	eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event"
    	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
    
    	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin"
    
    	"code.fbi.h-da.de/danet/gosdn/controller/store"
    	"github.com/google/uuid"
    
    	"github.com/openconfig/gnmi/proto/gnmi"
    
    	"github.com/openconfig/ygot/ygot"
    
    	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
    	log "github.com/sirupsen/logrus"
    )
    
    const (
    	// NetworkElementEventTopic is the used topic for network element related entity changes.
    	NetworkElementEventTopic = "managedNetworkElement"
    )
    
    // NetworkElementService provides a network element service implementation.
    // This services provides abstraction between the user (e.g a PND) and the matching store (e.g. networkElementStore).
    type NetworkElementService struct {
    	networkElementStore networkelement.Store
    
    	eventService        eventInterfaces.Service
    }
    
    // NewNetworkElementService creates a network element service.
    func NewNetworkElementService(
    	networkElementStore networkelement.Store,
    
    	eventService eventInterfaces.Service,
    ) networkelement.Service {
    	return &NetworkElementService{
    		networkElementStore: networkElementStore,
    
    		eventService:        eventService,
    	}
    }
    
    // Get takes a network element's UUID or name and returns the network element.
    func (s *NetworkElementService) Get(query store.Query) (networkelement.NetworkElement, error) {
    	loadedNetworkElement, err := s.networkElementStore.Get(query)
    	if err != nil {
    		return nil, err
    	}
    
    	mne, err := s.createNetworkElementFromStore(loadedNetworkElement)
    	if err != nil {
    		return nil, err
    	}
    
    	return mne, nil
    }
    
    // GetAll returns all stored network elements.
    func (s *NetworkElementService) GetAll() ([]networkelement.NetworkElement, error) {
    	var mnes []networkelement.NetworkElement
    
    	loadedNetworkElements, err := s.networkElementStore.GetAll()
    	if err != nil {
    		return nil, err
    	}
    
    	for _, loadedNetworkElement := range loadedNetworkElements {
    		mne, err := s.createNetworkElementFromStore(loadedNetworkElement)
    		if err != nil {
    			return nil, err
    		}
    
    		mnes = append(mnes, mne)
    	}
    
    	return mnes, nil
    }
    
    // GetAllAsLoaded returns all stored network elements as LoadedNetworkElement.
    // This method should be used if there is no need for a networkelement.NetworkElement, since
    // requesting network element information through this method is a lot faster than the
    // usual `GetAll` method.
    func (s *NetworkElementService) GetAllAsLoaded() ([]networkelement.LoadedNetworkElement, error) {
    	loadedNetworkElements, err := s.networkElementStore.GetAll()
    	if err != nil {
    		return nil, err
    	}
    
    	return loadedNetworkElements, nil
    }
    
    // Add adds a network element to the network element store.
    func (s *NetworkElementService) Add(networkElementToAdd networkelement.NetworkElement) error {
    	err := s.networkElementStore.Add(networkElementToAdd)
    	if err != nil {
    		return err
    	}
    
    
    	pubEvent := event.NewAddEvent(networkElementToAdd.ID())
    	if err := s.eventService.PublishEvent(NetworkElementEventTopic, pubEvent); err != nil {
    		go func() {
    			s.eventService.Reconnect()
    
    			retryErr := s.eventService.RetryPublish(NetworkElementEventTopic, pubEvent)
    			if retryErr != nil {
    				log.Error(retryErr)
    			}
    		}()
    
    	}
    
    	return nil
    }
    
    // UpdateModel updates a existing network element with a new model provided as string.
    
    Malte Bauch's avatar
    Malte Bauch committed
    func (s *NetworkElementService) UpdateModel(networkElementID uuid.UUID, modelAsString string) error {
    	exisitingNetworkElement, err := s.Get(store.Query{ID: networkElementID})
    
    	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
    	}
    
    
    	typedValue := &gnmi.TypedValue{
    		Value: &gnmi.TypedValue_JsonIetfVal{
    			JsonIetfVal: []byte(modelAsString),
    		},
    	}
    
    
    	// Use SetNode within the related plugin to map the path/value pair to the
    	// given ygot.GoStruct.
    
    	err = exisitingNetworkElement.GetPlugin().SetNode(path, typedValue)
    
    	if err != nil {
    		return err
    	}
    
    	err = s.networkElementStore.Update(exisitingNetworkElement)
    	if err != nil {
    		return err
    	}
    
    
    	// TODO (faseid): check if we want to add the paths with values here instead of empty map!
    
    	pubEvent := event.NewUpdateEvent(networkElementID)
    
    	if err := s.eventService.PublishEvent(NetworkElementEventTopic, pubEvent); err != nil {
    		go func() {
    			s.eventService.Reconnect()
    
    			retryErr := s.eventService.RetryPublish(NetworkElementEventTopic, pubEvent)
    			if retryErr != nil {
    				log.Error(retryErr)
    			}
    		}()
    
    	}
    
    	return nil
    }
    
    // Update updates a existing network element.
    func (s *NetworkElementService) Update(networkElementToUpdate networkelement.NetworkElement) error {
    	err := s.networkElementStore.Update(networkElementToUpdate)
    	if err != nil {
    		return err
    	}
    
    
    	// TODO (faseid): check if we want to add the paths with values here instead of empty map!
    
    	pubEvent := event.NewUpdateEvent(networkElementToUpdate.ID())
    
    	if err := s.eventService.PublishEvent(NetworkElementEventTopic, pubEvent); err != nil {
    		go func() {
    			s.eventService.Reconnect()
    
    			retryErr := s.eventService.RetryPublish(NetworkElementEventTopic, pubEvent)
    			if retryErr != nil {
    				log.Error(retryErr)
    			}
    		}()
    
    	}
    
    	return nil
    }
    
    // Delete deletes a network element from the network element store.
    func (s *NetworkElementService) Delete(networkElementToDelete networkelement.NetworkElement) error {
    	err := s.networkElementStore.Delete(networkElementToDelete)
    	if err != nil {
    		return err
    	}
    
    
    	pubEvent := event.NewDeleteEvent(networkElementToDelete.ID())
    	if err := s.eventService.PublishEvent(NetworkElementEventTopic, pubEvent); err != nil {
    		go func() {
    			s.eventService.Reconnect()
    
    			retryErr := s.eventService.RetryPublish(NetworkElementEventTopic, pubEvent)
    			if retryErr != nil {
    				log.Error(retryErr)
    			}
    		}()
    
    	}
    
    	return nil
    }
    
    func (s *NetworkElementService) createNetworkElementFromStore(loadedNetworkElement networkelement.LoadedNetworkElement) (networkelement.NetworkElement, error) {
    
    	if loadedNetworkElement.Plugin == "" {
    		return nil, fmt.Errorf("can not get device, no running plugin found for network element")
    
    	pluginForNetworkElement, err := s.pluginService.Get(store.Query{ID: uuid.MustParse(loadedNetworkElement.Plugin)})
    
    	if err != nil {
    		return nil, err
    	}
    
    	mne, err := NewNetworkElement(
    		loadedNetworkElement.Name,
    		uuid.MustParse(loadedNetworkElement.ID),
    		&tpb.TransportOption{
    			Address:  loadedNetworkElement.TransportAddress,
    			Username: loadedNetworkElement.TransportUsername,
    			Password: loadedNetworkElement.TransportPassword,
    			TransportOption: &tpb.TransportOption_GnmiTransportOption{
    				GnmiTransportOption: &tpb.GnmiTransportOption{},
    			},
    
    			Tls:  loadedNetworkElement.TransportTLS,
    
    			Type: spb.Type_TYPE_OPENCONFIG,
    
    		uuid.MustParse(loadedNetworkElement.PndID),
    
    		loadedNetworkElement.Metadata,
    	)
    
    	// if the plugin is not initialized, we need to initialize it with the
    	// model data from the associated network element.
    	if mne.GetPlugin().State() != plugin.INITIALIZED {
    		// 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
    		}
    
    		typedValue := &gnmi.TypedValue{
    			Value: &gnmi.TypedValue_JsonIetfVal{
    				JsonIetfVal: []byte(loadedNetworkElement.Model),
    			},
    		}
    
    		// Use SetNode within the related plugin to map the path/value pair to
    		// the given ygot.GoStruct.
    		err = mne.GetPlugin().SetNode(path, typedValue)
    		if err != nil {
    			return nil, err
    		}