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/conflict" "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/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" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/bufbuild/protovalidate-go" "github.com/google/uuid" ) // ConfigurationManagementServer represents ConfigurationManagementServer... type ConfigurationManagementServer struct { cmpb.UnimplementedConfigurationManagementServiceServer pndService networkdomain.Service mneService networkelement.Service topologyService topology.Service nodeService nodes.Service portService ports.Service pluginService plugin.Service protoValidator *protovalidate.Validator } // NewConfigurationManagementServer creates the ConfigurationManagementServer.. func NewConfigurationManagementServer( pndService networkdomain.Service, mneService networkelement.Service, topologyService topology.Service, nodeService nodes.Service, portService ports.Service, pluginService plugin.Service, protoValidator *protovalidate.Validator, ) *ConfigurationManagementServer { return &ConfigurationManagementServer{ pndService: pndService, mneService: mneService, topologyService: topologyService, nodeService: nodeService, portService: portService, pluginService: pluginService, protoValidator: protoValidator, } } // 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) { if err := c.protoValidator.Validate(request); err != nil { return nil, status.Errorf(codes.Aborted, "%v", err) } var sdnConfig = sdnConfig{} var err error sdnConfig.PndID = request.Pid sdnConfig.NetworkElements, err = c.mneService.GetAll() 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, }, nil } // ImportSDNConfig receives an SDN configuration and imports it. func (c ConfigurationManagementServer) ImportSDNConfig(ctx context.Context, request *cmpb.ImportSDNConfigRequest) (*cmpb.ImportSDNConfigResponse, error) { if err := c.protoValidator.Validate(request); err != nil { return nil, status.Errorf(codes.Aborted, "%v", err) } pndUUID := uuid.MustParse(request.Pid) var sdnConfig = loadedSDNConfig{} err := json.Unmarshal([]byte(request.SdnConfigData), &sdnConfig) if err != nil { return nil, err } err = c.deleteAllElementsFromDatabase() if err != nil { return nil, err } err = c.createElementsFromSDNConfig(&sdnConfig, pndUUID) if err != nil { return nil, err } return &cmpb.ImportSDNConfigResponse{ Timestamp: time.Now().UnixNano(), }, nil } func (c ConfigurationManagementServer) deleteAllElementsFromDatabase() error { if err := c.deleteNetworkElements(); 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() error { networkElements, err := c.mneService.GetAll() if err != nil { return err } for _, networkElement := range networkElements { err = c.mneService.Delete(networkElement) 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 { 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, Tls: inputNetworkElement.TransportTLS, } plugin, err := c.pluginService.RequestPlugin(uuid.MustParse(inputNetworkElement.Plugin)) if err != nil { return err } createdNetworkElement, err := nucleus.NewNetworkElement( inputNetworkElement.Name, uuid.MustParse(inputNetworkElement.ID), &transportOption, pndUUID, plugin, inputNetworkElement.GnmiSubscriptionPaths, conflict.Metadata{ResourceVersion: inputNetworkElement.Metadata.ResourceVersion}, ) if err != nil { return err } if err := c.mneService.Add(createdNetworkElement); err != nil { return err } if err := c.pluginService.Add(plugin); err != nil { return err } err = c.mneService.UpdateModel(createdNetworkElement.ID(), inputNetworkElement.Model) if err != nil { return err } networkElement, err := c.mneService.Get(store.Query{ID: uuid.MustParse(inputNetworkElement.ID)}) if err != nil { return err } if err := networkelement.EnsureIntendedConfigurationIsAppliedOnNetworkElement(networkElement); err != nil { return err } } return nil }