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" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" "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/plugin" "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" "github.com/google/uuid" ) // ConfigurationManagementServer represents ConfigurationManagementServer... type ConfigurationManagementServer struct { cmpb.UnimplementedConfigurationManagementServiceServer pndStore networkdomain.PndStore topologyService topology.Service nodeService nodes.Service portService ports.Service pluginService plugin.Service } // NewConfigurationManagementServer creates the ConfigurationManagementServer.. func NewConfigurationManagementServer( pndStore networkdomain.PndStore, topologyService topology.Service, nodeService nodes.Service, portService ports.Service, pluginService plugin.Service, ) *ConfigurationManagementServer { return &ConfigurationManagementServer{ pndStore: pndStore, topologyService: topologyService, nodeService: nodeService, portService: portService, pluginService: pluginService, } } // 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"` Plugins []plugin.LoadedPlugin `json:"plugins"` 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"` Plugins []plugin.LoadedPlugin `json:"plugins"` 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() sdnConfig.NetworkElements = networkElements 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 { if err := c.deleteNetworkElements(pndUUID); err != nil { return err } if err := c.deletePlugins(); err != nil { return err } if err := c.deleteTopology(); 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) deleteNetworkElements(pndUUID uuid.UUID) error { pnd, err := c.pndStore.Get(store.Query{ID: pndUUID}) 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) deletePlugins() error { plugins, err := c.pluginService.GetAll() if err != nil { return err } for _, plugin := range plugins { err = c.pluginService.Delete(plugin) if err != nil { return err } } return nil } func (c ConfigurationManagementServer) createElementsFromSDNConfig(sdnConfig *loadedSDNConfig, pndUUID uuid.UUID) error { if err := c.createTopology(sdnConfig); err != nil { return err } if err := c.createNetworkElements(sdnConfig, pndUUID); 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) createNetworkElements(sdnConfig *loadedSDNConfig, pndUUID uuid.UUID) error { pnd, err := c.pndStore.Get(store.Query{ID: pndUUID}) 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{}, }, // TODO: change TransportOption - type is not needed; this should // be removed as soon as we remove the csbi device type Type: spb.Type_TYPE_OPENCONFIG, } _, err := pnd.AddNetworkElement( inputNetworkElement.Name, &transportOption, nil, uuid.MustParse(inputNetworkElement.Plugin), 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 }