package server import ( "context" "encoding/json" "time" cmpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/nucleus" "code.fbi.h-da.de/danet/gosdn/controller/store" "code.fbi.h-da.de/danet/gosdn/controller/topology" "code.fbi.h-da.de/danet/gosdn/controller/topology/links" "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" "github.com/google/uuid" ) // ConfigurationManagementServer represents ConfigurationManagementServer... type ConfigurationManagementServer struct { cmpb.UnimplementedConfigurationManagementServiceServer pndStore networkdomain.PndStore topologyService topology.Service nodeService nodes.Service portService ports.Service } // NewConfigurationManagementServer creates the ConfigurationManagementServer.. func NewConfigurationManagementServer( pndStore networkdomain.PndStore, topologyService topology.Service, nodeService nodes.Service, portService ports.Service, ) *ConfigurationManagementServer { return &ConfigurationManagementServer{ pndStore: pndStore, topologyService: topologyService, nodeService: nodeService, portService: portService} } // sdnConfig is used to parse the sdnConfig into JSON. type sdnConfig struct { PndID string `json:"pndID"` Nodes []nodes.Node `json:"nodes"` Ports []ports.Port `json:"ports"` Links []links.Link `json:"links"` Sbis []southbound.SouthboundInterface `json:"sbis"` NetworkElements []networkelement.NetworkElement `json:"networkelements"` } // loadedSDNConfig is used to parse the stringified JSON sdnConfig into objects. type loadedSDNConfig struct { PndID string `json:"pndID"` Nodes []nodes.Node `json:"nodes"` Ports []ports.Port `json:"ports"` Links []links.Link `json:"links"` Sbis []southbound.LoadedSbi `json:"sbis"` NetworkElements []networkelement.LoadedNetworkElement `json:"networkelements"` } // ExportSDNConfig returns the SDN configuration. func (c ConfigurationManagementServer) ExportSDNConfig(ctx context.Context, request *cmpb.ExportSDNConfigRequest) (*cmpb.ExportSDNConfigResponse, error) { var sdnConfig = sdnConfig{} sdnConfig.PndID = request.Pid pndUUID := uuid.MustParse(request.Pid) pnd, err := c.pndStore.Get(store.Query{ID: pndUUID}) if err != nil { return nil, err } networkElements := pnd.NetworkElements() for _, networkElement := range networkElements { model, err := networkElement.GetModelAsFilteredCopy() if err != nil { return nil, err } networkElement.SetModel(model) } sdnConfig.NetworkElements = networkElements sdnConfig.Sbis, err = pnd.GetSBIs() if err != nil { return nil, err } sdnConfig.Nodes, err = c.nodeService.GetAll() if err != nil { return nil, err } sdnConfig.Ports, err = c.portService.GetAll() if err != nil { return nil, err } sdnConfig.Links, err = c.topologyService.GetAll() if err != nil { return nil, err } jsonSDNConfig, err := json.MarshalIndent(sdnConfig, "", " ") if err != nil { return nil, err } sdnConfigDataString := string(jsonSDNConfig) return &cmpb.ExportSDNConfigResponse{ Timestamp: time.Now().UnixNano(), SdnConfigData: sdnConfigDataString, Status: cmpb.Status_STATUS_OK}, nil } // ImportSDNConfig receives an SDN configuration and imports it. func (c ConfigurationManagementServer) ImportSDNConfig(ctx context.Context, request *cmpb.ImportSDNConfigRequest) (*cmpb.ImportSDNConfigResponse, error) { pndUUID := uuid.MustParse(request.Pid) var sdnConfig = loadedSDNConfig{} err := json.Unmarshal([]byte(request.SdnConfigData), &sdnConfig) if err != nil { return nil, err } err = c.deleteAllElementsFromDatabase(pndUUID) if err != nil { return nil, err } err = c.createElementsFromSDNConfig(&sdnConfig, pndUUID) if err != nil { return nil, err } return &cmpb.ImportSDNConfigResponse{ Timestamp: time.Now().UnixNano(), Status: cmpb.Status_STATUS_OK}, nil } func (c ConfigurationManagementServer) deleteAllElementsFromDatabase(pndUUID uuid.UUID) error { err := c.deleteNetworkElementsAndSBIs(pndUUID) if err != nil { return err } err = c.deleteTopology() if err != nil { return err } return nil } func (c ConfigurationManagementServer) deleteTopology() error { links, err := c.topologyService.GetAll() if err != nil { return err } for _, link := range links { err = c.topologyService.DeleteLink(link) if err != nil { return err } } ports, err := c.portService.GetAll() if err != nil { return err } for _, port := range ports { err = c.portService.Delete(port) if err != nil { return err } } nodes, err := c.nodeService.GetAll() if err != nil { return err } for _, node := range nodes { err = c.nodeService.Delete(node) if err != nil { return err } } return nil } func (c ConfigurationManagementServer) deleteNetworkElementsAndSBIs(pndUUID uuid.UUID) error { pnd, err := c.pndStore.Get(store.Query{ID: pndUUID}) if err != nil { return err } sbis, err := pnd.GetSBIs() if err != nil { return err } for _, sbi := range sbis { err = pnd.RemoveSbi(sbi.ID()) if err != nil { return err } } networkElements := pnd.NetworkElements() for _, networkElement := range networkElements { err = pnd.RemoveNetworkElement(networkElement.ID()) if err != nil { return err } } return nil } func (c ConfigurationManagementServer) createElementsFromSDNConfig(sdnConfig *loadedSDNConfig, pndUUID uuid.UUID) error { err := c.createTopology(sdnConfig) if err != nil { return err } err = c.createNetworkElementsAndSBIs(sdnConfig, pndUUID) if err != nil { return err } return nil } func (c ConfigurationManagementServer) createTopology(sdnConfig *loadedSDNConfig) error { for _, inputNode := range sdnConfig.Nodes { node := nodes.Node{ ID: inputNode.ID, Name: inputNode.Name, } _, err := c.nodeService.EnsureExists(node) if err != nil { return err } } for _, inputPort := range sdnConfig.Ports { port := ports.Port{ ID: inputPort.ID, Name: inputPort.Name, Configuration: inputPort.Configuration, } _, err := c.portService.EnsureExists(port) if err != nil { return err } } for _, inputPort := range sdnConfig.Links { sourceNode, err := c.nodeService.Get(store.Query{ID: inputPort.SourceNode.ID}) if err != nil { return err } targetNode, err := c.nodeService.Get(store.Query{ID: inputPort.TargetNode.ID}) if err != nil { return err } sourcePort, err := c.portService.Get(store.Query{ID: inputPort.SourcePort.ID}) if err != nil { return err } targetPort, err := c.portService.Get(store.Query{ID: inputPort.TargetPort.ID}) if err != nil { return err } link := links.Link{ ID: inputPort.ID, Name: inputPort.Name, SourceNode: sourceNode, TargetNode: targetNode, SourcePort: sourcePort, TargetPort: targetPort, } err = c.topologyService.AddLink(link) if err != nil { return err } } return nil } func (c ConfigurationManagementServer) createNetworkElementsAndSBIs(sdnConfig *loadedSDNConfig, pndUUID uuid.UUID) error { pnd, err := c.pndStore.Get(store.Query{ID: pndUUID}) if err != nil { return err } for _, inputSBI := range sdnConfig.Sbis { sbi, err := nucleus.NewSBI(inputSBI.Type, uuid.MustParse(inputSBI.ID)) if err != nil { return err } err = pnd.AddSbi(sbi) if err != nil { return err } } for _, inputNetworkElement := range sdnConfig.NetworkElements { transportOption := tpb.TransportOption{ Address: inputNetworkElement.TransportAddress, Username: inputNetworkElement.TransportUsername, Password: inputNetworkElement.TransportPassword, TransportOption: &tpb.TransportOption_GnmiTransportOption{ GnmiTransportOption: &tpb.GnmiTransportOption{}, }, Type: spb.Type_TYPE_OPENCONFIG, } _, err := pnd.AddNetworkElement( inputNetworkElement.Name, &transportOption, uuid.MustParse(inputNetworkElement.SBI), uuid.MustParse(inputNetworkElement.ID), ) if err != nil { return err } networkelement, err := pnd.GetNetworkElement(inputNetworkElement.ID) if err != nil { return err } err = pnd.UpdateNetworkElement(networkelement.ID(), inputNetworkElement.Model) if err != nil { return err } } return nil }