Skip to content
Snippets Groups Projects
Commit 3c462c78 authored by Neil-Jocelyn Schark's avatar Neil-Jocelyn Schark
Browse files

Enable export and import of SDN configuration


See merge request !404

Co-authored-by: default avatarMalte Bauch <malte.bauch@stud.h-da.de>
parent b67c4840
Branches fix-docker-registry-error
Tags
2 merge requests!442Develop,!404Enable export and import of SDN configuration
Pipeline #130905 failed
Showing
with 1748 additions and 95 deletions
......@@ -3,9 +3,8 @@
artifacts/
# containerlab
.gosdn.clab.yml
clab-gosdn_csbi_arista_base/
.gosdn.clab.yml.bak
clab-gosdn*/
*clab.yml.bak
# non vimmers
.vscode/
......@@ -52,9 +51,12 @@ stores/*.json
# gosdn
configs/gosdn.toml
config/.gosdnc.toml
gosdn.toml
applications/venv-manager/*.clab.yaml
# venv-manager
applications/venv-manager/*.yaml
applications/venv-manager/*.json
applications/venv-manager/clab-*
#debug
__debug_bin
......
......@@ -52,7 +52,7 @@ containerlab-destroy:
before_script:
- cd ${CLAB_DIR}
script:
- sudo containerlab destroy --topo ${CLAB_DIR}/${CLAB_NAME}.clab.yml
- sudo containerlab destroy --topo ${CLAB_DIR}/${CLAB_NAME}.clab.yml -c
- docker volume rm -f ${CLAB_NAME}-volume
- echo ${CLAB_NAME}
- ls -la
......
......@@ -32,10 +32,3 @@ controller-test:
- cd controller
- make ci-controller-test
<<: *test
test-build:
artifacts:
when: never
script:
- make build
<<: *test
......@@ -56,7 +56,7 @@ generate-csbi-yang-models: install-tools
../../$(TOOLS_DIR)/go-ygot-generator-generator config.yaml gostructs.go &&\
go generate
build: pre build-gosdn build-gosdnc build-orchestrator build-venv-manager build-arista-routing-engine-app build-hostname-checker-app build-basic-interface-monitoring-app
build: pre build-gosdn build-gosdnc build-venv-manager build-arista-routing-engine-app build-hostname-checker-app build-basic-interface-monitoring-app #build-orchestrator
build-gosdn: pre
$(GOBUILD) -trimpath -o $(BUILD_ARTIFACTS_PATH)/gosdn ./controller/cmd/gosdn
......@@ -79,7 +79,7 @@ build-hostname-checker-app: pre
build-basic-interface-monitoring-app: pre
$(GOBUILD) -trimpath -o $(BUILD_ARTIFACTS_PATH)/basic-interface-monitoring ./applications/basic-interface-monitoring
containerize-all: containerize-gosdn containerize-gosdnc containerize-orchestrator containerize-target
containerize-all: containerize-gosdn containerize-gosdnc containerize-target #containerize-orchestrator
containerize-gosdn:
docker buildx build --rm -t gosdn --load -f controller/controller.Dockerfile .
......@@ -103,13 +103,13 @@ containerize-hostname-checker-app:
docker buildx build --rm -t hostname-checker-app -f applications/hostname-checker/hostname-checker.Dockerfile .
containerlab-start: containerize-all
sudo containerlab deploy --topo gosdn.clab.yaml
sudo containerlab deploy --topo dev_env_data/clab/gosdn.clab.yaml
containerlab-stop:
sudo containerlab destroy --topo gosdn.clab.yaml
sudo containerlab destroy --topo dev_env_data/clab/gosdn.clab.yaml
containerlab-graph:
sudo containerlab graph --topo gosdn.clab.yaml
sudo containerlab graph --topo dev_env_data/clab/gosdn.clab.yaml
shell-gosdn:
docker exec -it clab-gosdn_csbi_arista_base-gosdn bash
......
......@@ -45,7 +45,7 @@ A simple showcase how the controller can be adressed after
- `csbi` is the implementation of Containerised-Southbound-Interfaces (based on
the idea and the proof of concept of Manuel Kieweg). Allowing to request
capabilities of MNEs and generate a containerised Southbound-Interface based
on them.
on them. They are currently unsupported.
- `controller` represents the `goSDN-controller`.
# Concepts
......@@ -54,7 +54,7 @@ The `goSDN` controllers core - also called `nucleus` - is a lightweight library
that manages principal network domains and provides southbound interface
operations for managed network elements.
In addition we provide a simple Northbound-API for the controller ()
In addition, we provide a simple Northbound-API for the controller ()
## Principal Networking Domain (PND)
......@@ -130,7 +130,7 @@ TBD
## Backup and restore your mongodb
If you are using the mongodb provided via the `docker-compose.yaml`, you can easily backup and restore your data via two scripts.
If you are using the mongodb provided via the `docker-compose.yaml`, you can easily back up and restore your data via two scripts.
```bash
./scripts/backup_mongo_volume.sh $(suffix_of_backup)
......
This diff is collapsed.
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: gosdn/configurationmanagement/configurationmanagement.proto
/*
Package core is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package core
import (
"context"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
var (
filter_ConfigurationManagementService_ExportSDNConfig_0 = &utilities.DoubleArray{Encoding: map[string]int{"pid": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
func request_ConfigurationManagementService_ExportSDNConfig_0(ctx context.Context, marshaler runtime.Marshaler, client ConfigurationManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ExportSDNConfigRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pid"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pid")
}
protoReq.Pid, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pid", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ConfigurationManagementService_ExportSDNConfig_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ExportSDNConfig(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_ConfigurationManagementService_ExportSDNConfig_0(ctx context.Context, marshaler runtime.Marshaler, server ConfigurationManagementServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ExportSDNConfigRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pid"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pid")
}
protoReq.Pid, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pid", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ConfigurationManagementService_ExportSDNConfig_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ExportSDNConfig(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_ConfigurationManagementService_ImportSDNConfig_0 = &utilities.DoubleArray{Encoding: map[string]int{"pid": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
func request_ConfigurationManagementService_ImportSDNConfig_0(ctx context.Context, marshaler runtime.Marshaler, client ConfigurationManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ImportSDNConfigRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pid"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pid")
}
protoReq.Pid, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pid", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ConfigurationManagementService_ImportSDNConfig_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ImportSDNConfig(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_ConfigurationManagementService_ImportSDNConfig_0(ctx context.Context, marshaler runtime.Marshaler, server ConfigurationManagementServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ImportSDNConfigRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pid"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pid")
}
protoReq.Pid, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pid", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ConfigurationManagementService_ImportSDNConfig_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ImportSDNConfig(ctx, &protoReq)
return msg, metadata, err
}
// RegisterConfigurationManagementServiceHandlerServer registers the http handlers for service ConfigurationManagementService to "mux".
// UnaryRPC :call ConfigurationManagementServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterConfigurationManagementServiceHandlerFromEndpoint instead.
func RegisterConfigurationManagementServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ConfigurationManagementServiceServer) error {
mux.Handle("GET", pattern_ConfigurationManagementService_ExportSDNConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.configurationmanagement.ConfigurationManagementService/ExportSDNConfig", runtime.WithHTTPPathPattern("/export/{pid}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_ConfigurationManagementService_ExportSDNConfig_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ConfigurationManagementService_ExportSDNConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ConfigurationManagementService_ImportSDNConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.configurationmanagement.ConfigurationManagementService/ImportSDNConfig", runtime.WithHTTPPathPattern("/import/{pid}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_ConfigurationManagementService_ImportSDNConfig_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ConfigurationManagementService_ImportSDNConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterConfigurationManagementServiceHandlerFromEndpoint is same as RegisterConfigurationManagementServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterConfigurationManagementServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterConfigurationManagementServiceHandler(ctx, mux, conn)
}
// RegisterConfigurationManagementServiceHandler registers the http handlers for service ConfigurationManagementService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterConfigurationManagementServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterConfigurationManagementServiceHandlerClient(ctx, mux, NewConfigurationManagementServiceClient(conn))
}
// RegisterConfigurationManagementServiceHandlerClient registers the http handlers for service ConfigurationManagementService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ConfigurationManagementServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ConfigurationManagementServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "ConfigurationManagementServiceClient" to call the correct interceptors.
func RegisterConfigurationManagementServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ConfigurationManagementServiceClient) error {
mux.Handle("GET", pattern_ConfigurationManagementService_ExportSDNConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.configurationmanagement.ConfigurationManagementService/ExportSDNConfig", runtime.WithHTTPPathPattern("/export/{pid}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ConfigurationManagementService_ExportSDNConfig_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ConfigurationManagementService_ExportSDNConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ConfigurationManagementService_ImportSDNConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.configurationmanagement.ConfigurationManagementService/ImportSDNConfig", runtime.WithHTTPPathPattern("/import/{pid}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ConfigurationManagementService_ImportSDNConfig_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ConfigurationManagementService_ImportSDNConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_ConfigurationManagementService_ExportSDNConfig_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"export", "pid"}, ""))
pattern_ConfigurationManagementService_ImportSDNConfig_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"import", "pid"}, ""))
)
var (
forward_ConfigurationManagementService_ExportSDNConfig_0 = runtime.ForwardResponseMessage
forward_ConfigurationManagementService_ImportSDNConfig_0 = runtime.ForwardResponseMessage
)
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package core
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// ConfigurationManagementServiceClient is the client API for ConfigurationManagementService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ConfigurationManagementServiceClient interface {
// Allows for export of the whole SDN configuration
ExportSDNConfig(ctx context.Context, in *ExportSDNConfigRequest, opts ...grpc.CallOption) (*ExportSDNConfigResponse, error)
// Allows for import of the whole SDN configuration
ImportSDNConfig(ctx context.Context, in *ImportSDNConfigRequest, opts ...grpc.CallOption) (*ImportSDNConfigResponse, error)
}
type configurationManagementServiceClient struct {
cc grpc.ClientConnInterface
}
func NewConfigurationManagementServiceClient(cc grpc.ClientConnInterface) ConfigurationManagementServiceClient {
return &configurationManagementServiceClient{cc}
}
func (c *configurationManagementServiceClient) ExportSDNConfig(ctx context.Context, in *ExportSDNConfigRequest, opts ...grpc.CallOption) (*ExportSDNConfigResponse, error) {
out := new(ExportSDNConfigResponse)
err := c.cc.Invoke(ctx, "/gosdn.configurationmanagement.ConfigurationManagementService/ExportSDNConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *configurationManagementServiceClient) ImportSDNConfig(ctx context.Context, in *ImportSDNConfigRequest, opts ...grpc.CallOption) (*ImportSDNConfigResponse, error) {
out := new(ImportSDNConfigResponse)
err := c.cc.Invoke(ctx, "/gosdn.configurationmanagement.ConfigurationManagementService/ImportSDNConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ConfigurationManagementServiceServer is the server API for ConfigurationManagementService service.
// All implementations must embed UnimplementedConfigurationManagementServiceServer
// for forward compatibility
type ConfigurationManagementServiceServer interface {
// Allows for export of the whole SDN configuration
ExportSDNConfig(context.Context, *ExportSDNConfigRequest) (*ExportSDNConfigResponse, error)
// Allows for import of the whole SDN configuration
ImportSDNConfig(context.Context, *ImportSDNConfigRequest) (*ImportSDNConfigResponse, error)
mustEmbedUnimplementedConfigurationManagementServiceServer()
}
// UnimplementedConfigurationManagementServiceServer must be embedded to have forward compatible implementations.
type UnimplementedConfigurationManagementServiceServer struct {
}
func (UnimplementedConfigurationManagementServiceServer) ExportSDNConfig(context.Context, *ExportSDNConfigRequest) (*ExportSDNConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExportSDNConfig not implemented")
}
func (UnimplementedConfigurationManagementServiceServer) ImportSDNConfig(context.Context, *ImportSDNConfigRequest) (*ImportSDNConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ImportSDNConfig not implemented")
}
func (UnimplementedConfigurationManagementServiceServer) mustEmbedUnimplementedConfigurationManagementServiceServer() {
}
// UnsafeConfigurationManagementServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ConfigurationManagementServiceServer will
// result in compilation errors.
type UnsafeConfigurationManagementServiceServer interface {
mustEmbedUnimplementedConfigurationManagementServiceServer()
}
func RegisterConfigurationManagementServiceServer(s grpc.ServiceRegistrar, srv ConfigurationManagementServiceServer) {
s.RegisterService(&ConfigurationManagementService_ServiceDesc, srv)
}
func _ConfigurationManagementService_ExportSDNConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ExportSDNConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ConfigurationManagementServiceServer).ExportSDNConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/gosdn.configurationmanagement.ConfigurationManagementService/ExportSDNConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ConfigurationManagementServiceServer).ExportSDNConfig(ctx, req.(*ExportSDNConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ConfigurationManagementService_ImportSDNConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ImportSDNConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ConfigurationManagementServiceServer).ImportSDNConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/gosdn.configurationmanagement.ConfigurationManagementService/ImportSDNConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ConfigurationManagementServiceServer).ImportSDNConfig(ctx, req.(*ImportSDNConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
// ConfigurationManagementService_ServiceDesc is the grpc.ServiceDesc for ConfigurationManagementService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ConfigurationManagementService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "gosdn.configurationmanagement.ConfigurationManagementService",
HandlerType: (*ConfigurationManagementServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ExportSDNConfig",
Handler: _ConfigurationManagementService_ExportSDNConfig_Handler,
},
{
MethodName: "ImportSDNConfig",
Handler: _ConfigurationManagementService_ImportSDNConfig_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "gosdn/configurationmanagement/configurationmanagement.proto",
}
......@@ -32,6 +32,9 @@
{
"name": "SbiService"
},
{
"name": "ConfigurationManagementService"
},
{
"name": "CoreService"
},
......@@ -97,6 +100,88 @@
]
}
},
"/export/{pid}": {
"get": {
"summary": "Allows for export of the whole SDN configuration",
"operationId": "ConfigurationManagementService_ExportSDNConfig",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/configurationmanagementExportSDNConfigResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googlerpcStatus"
}
}
},
"parameters": [
{
"name": "pid",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "timestamp",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
}
],
"tags": [
"ConfigurationManagementService"
]
}
},
"/import/{pid}": {
"post": {
"summary": "Allows for import of the whole SDN configuration",
"operationId": "ConfigurationManagementService_ImportSDNConfig",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/configurationmanagementImportSDNConfigResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googlerpcStatus"
}
}
},
"parameters": [
{
"name": "pid",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "timestamp",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
},
{
"name": "sdnConfigData",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"ConfigurationManagementService"
]
}
},
"/login": {
"post": {
"summary": "Allows a user to login creating a session for further actions.",
......@@ -1750,6 +1835,33 @@
}
}
},
"configurationmanagementExportSDNConfigResponse": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"format": "int64"
},
"sdnConfigData": {
"type": "string"
},
"status": {
"$ref": "#/definitions/gosdnconfigurationmanagementStatus"
}
}
},
"configurationmanagementImportSDNConfigResponse": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"format": "int64"
},
"status": {
"$ref": "#/definitions/gosdnconfigurationmanagementStatus"
}
}
},
"conflictMetadata": {
"type": "object",
"properties": {
......@@ -2930,6 +3042,15 @@
],
"default": "STATUS_UNSPECIFIED"
},
"gosdnconfigurationmanagementStatus": {
"type": "string",
"enum": [
"STATUS_UNSPECIFIED",
"STATUS_OK",
"STATUS_ERROR"
],
"default": "STATUS_UNSPECIFIED"
},
"gosdncoreStatus": {
"type": "string",
"enum": [
......
......@@ -4,8 +4,8 @@ deps:
- remote: buf.build
owner: googleapis
repository: googleapis
commit: d1263fe26f8e430a967dc22a4d0cad18
commit: 75b4300737fb4efca0831636be94e517
- remote: buf.build
owner: grpc-ecosystem
repository: grpc-gateway
commit: b96615cde70c403f8075c48e56178f88
commit: a1ecdc58eccd49aa8bea2a7a9022dc27
syntax = "proto3";
package gosdn.configurationmanagement;
import "google/api/annotations.proto";
import "gosdn/pnd/pnd.proto";
option go_package = "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core";
// The ConfigurationManagementService allows to access configurations
service ConfigurationManagementService{
// Allows for export of the whole SDN configuration
rpc ExportSDNConfig(ExportSDNConfigRequest) returns (ExportSDNConfigResponse) {
option (google.api.http) = {
get: "/export/{pid}"
};
}
// Allows for import of the whole SDN configuration
rpc ImportSDNConfig(ImportSDNConfigRequest) returns (ImportSDNConfigResponse) {
option (google.api.http) = {
post: "/import/{pid}"
};
}
}
message ExportSDNConfigRequest {
int64 timestamp = 1; // Timestamp in nanoseconds since Epoch.
string pid = 2;
}
message ExportSDNConfigResponse {
int64 timestamp = 1; // Timestamp in nanoseconds since Epoch.
string sdnConfigData = 2;
Status status = 3;
}
message ImportSDNConfigRequest {
int64 timestamp = 1; // Timestamp in nanoseconds since Epoch.
string pid = 2;
string sdnConfigData = 3;
}
message ImportSDNConfigResponse {
int64 timestamp = 1; // Timestamp in nanoseconds since Epoch.
Status status = 2;
}
enum Status {
STATUS_UNSPECIFIED = 0;
STATUS_OK = 1;
STATUS_ERROR = 2;
}
......@@ -13,44 +13,75 @@ import (
func main() {
var dialConnectionURL string
var yamlFilepath string
var toplogyFilepath string
var customContainerRegistryURL string
var sdnConfigFilepath string
var mode string
dialOption := grpc.WithTransportCredentials(insecure.NewCredentials())
flag.StringVar(&mode, "mode", "extract", "")
flag.StringVar(&dialConnectionURL, "controller", "localhost:55055", "")
flag.StringVar(&yamlFilepath, "file", "venv.clab.yaml", "")
flag.StringVar(&toplogyFilepath, "topology", "venv.clab.yaml", "")
flag.StringVar(&customContainerRegistryURL, "registry", "", "")
flag.StringVar(&sdnConfigFilepath, "sdnconfig", "sdn_config.json", "")
// Define own output of --help and parsing error, as some library also uses the flags library and adds there flags to ours, which is not intended.
flag.Usage = func() {
fmt.Printf("Usable flags of the venv-manager:\n\n")
fmt.Println("--mode string\n\t The mode of the venv-manager. use either 'extract', 'deploy' or 'apply'. (Default: 'extract')")
fmt.Println("--controller string\n\t Controller URL and Port. (Default: 'localhost:55055')")
fmt.Println("--file string\n\t Filename of the resulting topology file. (Default: 'venv.clab.yaml')")
fmt.Println("--registry string\n\t URL of the container registry to use. Keep in mind that cEOS images and prebuild linux images with the gnmi target are not available on dockerhub. (Default: dockerhub)")
fmt.Println("--topology string\n\t Filename of the resulting topology file. (Default: 'venv.clab.yaml')")
fmt.Println("--sdnconfig string\n\t Filename of the resulting SDN configuration file. (Default: 'sdn_config.json')")
fmt.Println("--registry string\n\t URL of the container registry to use. Keep in mind that cEOS images and prebuild linux images with the gnmi target are not available on dockerhub. (Default: none (dockerhub))")
fmt.Println("--help\n\t Shows this help screen.")
}
flag.Parse()
customContainerRegistryURL = ensureLastCharIsSlash(customContainerRegistryURL)
fmt.Println("I will try to connect to goSDN located at", dialConnectionURL)
venvManager := venvmanager.NewVenvManager(dialConnectionURL, dialOption, yamlFilepath, customContainerRegistryURL)
fmt.Println("I will try to connect to goSDN located at:", dialConnectionURL)
venvManager := venvmanager.NewVenvManager(dialConnectionURL, dialOption, toplogyFilepath, sdnConfigFilepath, customContainerRegistryURL)
err := venvManager.TestConnection()
if err != nil {
fmt.Println(err)
fmt.Println("Can't reach controller, exiting.")
os.Exit(1)
}
fmt.Println("Connection successful!")
fmt.Println("Generating file...")
err = venvManager.CreateTopologyFile()
if err != nil {
fmt.Println(err)
fmt.Println("An error occurred, exiting.")
os.Exit(1)
if mode == "extract" {
fmt.Println("Generating topology file...")
err := venvManager.CreateTopologyFile()
if err != nil {
printErrAndAbort(err)
}
fmt.Println("Generating SDN configuration file...")
err = venvManager.CreateSDNConfigFile()
if err != nil {
printErrAndAbort(err)
}
} else if mode == "deploy" {
fmt.Println("Creating environment... (This might take a long time and may require sudo rights and the password of the user)")
err := venvManager.StartVirtualEnvironment()
if err != nil {
printErrAndAbort(err)
}
fmt.Println("Environment created.")
fmt.Println("Applying SDN config to controller...")
err = venvManager.ReadAndSendSDNConfig()
if err != nil {
printErrAndAbort(err)
}
fmt.Println("SDN config applied.")
fmt.Println("To stop or restart the virtual environment interact with containerlab itself.\nFor more information visit their documentation: https://containerlab.dev/")
} else if mode == "apply" {
fmt.Println("Sending SDN config to controller...")
err := venvManager.ReadAndSendSDNConfig()
if err != nil {
printErrAndAbort(err)
}
fmt.Println("SDN config applied.")
} else {
fmt.Println("No valid mode selected. Choose either 'extract', 'deploy' or 'apply'.")
}
fmt.Println("All done!")
os.Exit(0)
}
......@@ -66,3 +97,9 @@ func ensureLastCharIsSlash(inputString string) string {
return inputString + "/"
}
func printErrAndAbort(err error) {
fmt.Println(err)
fmt.Println("An error occurred, exiting.")
os.Exit(1)
}
......@@ -5,9 +5,11 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"strings"
"time"
configMgmtPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
corePb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
networkelementPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
pndPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
......@@ -19,8 +21,9 @@ import (
topology "code.fbi.h-da.de/danet/gosdn/applications/venv-manager/topology"
yangparser "code.fbi.h-da.de/danet/gosdn/applications/venv-manager/yang-parser"
"code.fbi.h-da.de/danet/gosdn/models/generated/openconfig"
"github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/ygot/ygot"
"google.golang.org/grpc"
"gopkg.in/yaml.v3"
)
......@@ -29,24 +32,26 @@ import (
type VenvManager struct {
dialConnectionURL string
dialOption grpc.DialOption
yamlFilepath string
topologyFilepath string
sdnConfigFilepath string
containerRegistryURL string
pnd string
pndID string
pndName string
}
// NewVenvManager creates a new VenvManager to use.
func NewVenvManager(dialConnectionURL string, dialOption grpc.DialOption, yamlFilepath string, containerRegistryURL string) *VenvManager {
func NewVenvManager(dialConnectionURL string, dialOption grpc.DialOption, topologyFilepath string, sdnConfigFilepath string, containerRegistryURL string) *VenvManager {
v := new(VenvManager)
v.dialConnectionURL = dialConnectionURL
v.dialOption = dialOption
v.yamlFilepath = yamlFilepath
v.topologyFilepath = topologyFilepath
v.sdnConfigFilepath = sdnConfigFilepath
v.containerRegistryURL = containerRegistryURL
return v
}
func (v *VenvManager) createConnection() (*grpc.ClientConn, error) {
conn, err := grpc.Dial(v.dialConnectionURL, v.dialOption)
conn, err := grpc.Dial(v.dialConnectionURL, v.dialOption, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)))
if err != nil {
return nil, err
}
......@@ -56,14 +61,41 @@ func (v *VenvManager) createConnection() (*grpc.ClientConn, error) {
func (v *VenvManager) closeConnection(conn *grpc.ClientConn) {
err := conn.Close()
if err != nil {
fmt.Println(err)
}
}
// TestConnection checks if it can reach the controller via the api.
func (v *VenvManager) TestConnection() error {
// StartVirtualEnvironment uses containerlab to start the virtual environment.
func (v *VenvManager) StartVirtualEnvironment() error {
cmd := exec.Command("sudo", "containerlab", "deploy", "-t", v.topologyFilepath)
output, err := cmd.Output()
if err != nil {
return err
}
//Might be a good idea to switch to live output of the command later
fmt.Println(string(output))
return nil
}
// ReadAndSendSDNConfig gets the SDN config data and sends it to the controller.
func (v *VenvManager) ReadAndSendSDNConfig() error {
sdnConfigData, err := v.readSDNConfigFile()
if err != nil {
return err
}
err = v.sendSDNConfigData(&sdnConfigData)
if err != nil {
return err
}
return nil
}
// getSDNConfigData gets the sDN configuration data.
func (v *VenvManager) sendSDNConfigData(sdnConfigData *string) error {
conn, err := v.createConnection()
if err != nil {
return err
......@@ -72,9 +104,71 @@ func (v *VenvManager) TestConnection() error {
ctx := context.Background()
//Replace with health check as soon as available
coreService := corePb.NewCoreServiceClient(conn)
_, err = coreService.GetPndList(ctx, &corePb.GetPndListRequest{})
pndRes, err := coreService.GetPndList(ctx, &corePb.GetPndListRequest{Timestamp: getTimestamp()})
if err != nil {
return err
}
// currently only support for default PND
v.pndID = pndRes.Pnd[0].Id
configMgmtService := configMgmtPb.NewConfigurationManagementServiceClient(conn)
_, err = configMgmtService.ImportSDNConfig(ctx, &configMgmtPb.ImportSDNConfigRequest{Timestamp: getTimestamp(), Pid: v.pndID, SdnConfigData: *sdnConfigData})
if err != nil {
return err
}
return nil
}
// CreateSDNConfigFile creates the SDN configuration file.
func (v *VenvManager) CreateSDNConfigFile() error {
sdnConfigReponse, err := v.getSDNConfigData()
if err != nil {
return err
}
err = v.writeSDNConfigFile(*sdnConfigReponse)
if err != nil {
return err
}
return nil
}
// getSDNConfigData gets the sDN configuration data.
func (v *VenvManager) getSDNConfigData() (*string, error) {
conn, err := v.createConnection()
if err != nil {
return nil, err
}
defer v.closeConnection(conn)
ctx := context.Background()
//get PND, might remove later because we won't support PND in the future
coreService := corePb.NewCoreServiceClient(conn)
pndRes, err := coreService.GetPndList(ctx, &corePb.GetPndListRequest{Timestamp: getTimestamp()})
if err != nil {
return nil, err
}
v.pndID = pndRes.Pnd[0].Id
configMgmtService := configMgmtPb.NewConfigurationManagementServiceClient(conn)
sdnConfigResponse, err := configMgmtService.ExportSDNConfig(ctx, &configMgmtPb.ExportSDNConfigRequest{Timestamp: getTimestamp(), Pid: v.pndID})
if err != nil {
return nil, err
}
return &sdnConfigResponse.SdnConfigData, nil
}
// writeSDNConfigFile writes the SDN configuration in a string to a file.
func (v *VenvManager) writeSDNConfigFile(sdnConfigToWrite string) error {
err := os.WriteFile(v.sdnConfigFilepath, []byte(sdnConfigToWrite), 0644)
if err != nil {
return err
}
......@@ -82,6 +176,16 @@ func (v *VenvManager) TestConnection() error {
return nil
}
// readSDNConfigToFile reads the SDN configuration from a file to a string.
func (v *VenvManager) readSDNConfigFile() (string, error) {
content, err := os.ReadFile(v.sdnConfigFilepath)
if err != nil {
return "", err
}
return string(content), nil
}
// CreateTopologyFile creates the topology file.
func (v *VenvManager) CreateTopologyFile() error {
topologyData, err := v.getTopologyData()
......@@ -127,7 +231,8 @@ func (v *VenvManager) getTopologyData() (*topologyPb.GetTopologyResponse, error)
if err != nil {
return nil, err
}
v.pnd = pndRes.Pnd[0].Id
v.pndID = pndRes.Pnd[0].Id
v.pndName = pndRes.Pnd[0].Name
toplogyService := topologyPb.NewTopologyServiceClient(conn)
topologyResponse, err := toplogyService.GetTopology(ctx, &topologyPb.GetTopologyRequest{Timestamp: getTimestamp()})
......@@ -193,10 +298,13 @@ func (v *VenvManager) parseTopologyDataIntoStructs(topologyData *topologyPb.GetT
func (v *VenvManager) loadNetworkElementModelPathsIntoGosdn(ctx context.Context, conn *grpc.ClientConn, nodes *[]node.Node) error {
pndService := pndPb.NewPndServiceClient(conn)
for _, node := range *nodes {
_, err := pndService.GetPath(ctx, &pndPb.GetPathRequest{Mneid: node.ID, Pid: v.pnd, Path: "/"})
if err != nil {
return err
paths := [2]string{"/lldp/config/system-description", "/system/state/"}
for _, path := range paths {
for _, node := range *nodes {
_, err := pndService.GetPath(ctx, &pndPb.GetPathRequest{Mneid: node.ID, Pid: v.pndID, Path: path})
if err != nil {
return err
}
}
}
......@@ -212,13 +320,16 @@ func (v *VenvManager) getAndAddMoreData(topologyData *topology.GoSdnTopology) (*
ctx := context.Background()
var path = "/"
var ygotPath *gnmi.Path
// Create 'root' path to be able to load the whole model from the store.
path, err := ygot.StringToPath("/", ygot.StructuredPath)
ygotPath, err = ygot.StringToPath(path, ygot.StructuredPath)
if err != nil {
return nil, err
}
// just to load model data into goSDN to have newest data available for get request
// just to load model data into goSDN to guaranteed have new data available for get request
err = v.loadNetworkElementModelPathsIntoGosdn(ctx, conn, &topologyData.Nodes)
if err != nil {
return nil, err
......@@ -234,7 +345,7 @@ func (v *VenvManager) getAndAddMoreData(topologyData *topology.GoSdnTopology) (*
var marshalledYangData openconfig.Device
err = yangparser.Unmarshal([]byte(getNetworkElementResponse.NetworkElement.Model), path, &marshalledYangData)
err = yangparser.Unmarshal([]byte(getNetworkElementResponse.NetworkElement.Model), ygotPath, &marshalledYangData)
if err != nil {
return nil, err
}
......@@ -251,7 +362,7 @@ func (v *VenvManager) getAndAddMoreData(topologyData *topology.GoSdnTopology) (*
func (v *VenvManager) parseIntoContainerlabTopology(topologyData *topology.GoSdnTopology) (*containerlab.YamlStruct, error) {
containerlabTopology := containerlab.YamlStruct{}
containerlabTopology.Name = v.pnd + topologyData.Name
containerlabTopology.Name = v.pndName
// find a better way than to do this
var managementNet string
......@@ -281,7 +392,7 @@ func (v *VenvManager) writeTopologyToYamlFile(containerlabStruct *containerlab.Y
return err
}
err = os.WriteFile(v.yamlFilepath, yaml, 0644)
err = os.WriteFile(v.topologyFilepath, yaml, 0644)
if err != nil {
return err
}
......
......@@ -20,6 +20,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
apppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/app"
cmpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
......@@ -192,6 +193,7 @@ func startGrpc() error {
tpb.RegisterTopologyServiceServer(c.grpcServer, c.nbi.Topology)
mnepb.RegisterNetworkElementServiceServer(c.grpcServer, c.nbi.NetworkElement)
tpb.RegisterRoutingTableServiceServer(c.grpcServer, c.nbi.Routes)
cmpb.RegisterConfigurationManagementServiceServer(c.grpcServer, c.nbi.ConfigurationManagement)
go func() {
if err := c.grpcServer.Serve(lislisten); err != nil {
......
......@@ -16,7 +16,7 @@ type NetworkDomain interface {
Destroy() error
AddSbi(s southbound.SouthboundInterface) error
RemoveSbi(uuid.UUID) error
AddNetworkElement(name string, opts *tpb.TransportOption, sid uuid.UUID) (uuid.UUID, error)
AddNetworkElement(name string, opts *tpb.TransportOption, sid uuid.UUID, optionalNetworkElementID ...uuid.UUID) (uuid.UUID, error)
GetNetworkElement(identifier string) (networkelement.NetworkElement, error)
RemoveNetworkElement(uuid.UUID) error
UpdateNetworkElement(uuid.UUID, string) error
......
......@@ -16,6 +16,8 @@ import (
type NetworkElement interface {
ID() uuid.UUID
GetModel() ygot.GoStruct
GetModelAsFilteredCopy() (ygot.GoStruct, error)
SetModel(ygot.GoStruct)
CreateModelCopy() (ygot.ValidatedGoStruct, error)
Transport() transport.Transport
Name() string
......
......@@ -24,13 +24,20 @@ type NetworkDomain struct {
mock.Mock
}
// AddNetworkElement provides a mock function with given fields: name, opts, sid
func (_m *NetworkDomain) AddNetworkElement(name string, opts *transport.TransportOption, sid uuid.UUID) (uuid.UUID, error) {
ret := _m.Called(name, opts, sid)
// AddNetworkElement provides a mock function with given fields: name, opts, sid, optionalNetworkElementID
func (_m *NetworkDomain) AddNetworkElement(name string, opts *transport.TransportOption, sid uuid.UUID, optionalNetworkElementID ...uuid.UUID) (uuid.UUID, error) {
_va := make([]interface{}, len(optionalNetworkElementID))
for _i := range optionalNetworkElementID {
_va[_i] = optionalNetworkElementID[_i]
}
var _ca []interface{}
_ca = append(_ca, name, opts, sid)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 uuid.UUID
if rf, ok := ret.Get(0).(func(string, *transport.TransportOption, uuid.UUID) uuid.UUID); ok {
r0 = rf(name, opts, sid)
if rf, ok := ret.Get(0).(func(string, *transport.TransportOption, uuid.UUID, ...uuid.UUID) uuid.UUID); ok {
r0 = rf(name, opts, sid, optionalNetworkElementID...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(uuid.UUID)
......@@ -38,8 +45,8 @@ func (_m *NetworkDomain) AddNetworkElement(name string, opts *transport.Transpor
}
var r1 error
if rf, ok := ret.Get(1).(func(string, *transport.TransportOption, uuid.UUID) error); ok {
r1 = rf(name, opts, sid)
if rf, ok := ret.Get(1).(func(string, *transport.TransportOption, uuid.UUID, ...uuid.UUID) error); ok {
r1 = rf(name, opts, sid, optionalNetworkElementID...)
} else {
r1 = ret.Error(1)
}
......
......@@ -75,6 +75,29 @@ func (_m *NetworkElement) GetModel() ygot.GoStruct {
return r0
}
// GetModelAsFilteredCopy provides a mock function with given fields:
func (_m *NetworkElement) GetModelAsFilteredCopy() (ygot.GoStruct, error) {
ret := _m.Called()
var r0 ygot.GoStruct
if rf, ok := ret.Get(0).(func() ygot.GoStruct); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(ygot.GoStruct)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetModelAsString provides a mock function with given fields:
func (_m *NetworkElement) GetModelAsString() (string, error) {
ret := _m.Called()
......@@ -170,6 +193,11 @@ func (_m *NetworkElement) SBI() southbound.SouthboundInterface {
return r0
}
// SetModel provides a mock function with given fields: _a0
func (_m *NetworkElement) SetModel(_a0 ygot.GoStruct) {
_m.Called(_a0)
}
// Transport provides a mock function with given fields:
func (_m *NetworkElement) Transport() transport.Transport {
ret := _m.Called()
......
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
}
......@@ -20,17 +20,18 @@ import (
// NorthboundInterface is the representation of the
// gRPC services used provided.
type NorthboundInterface struct {
Pnd *PndServer
Core *CoreServer
Csbi *CsbiServer
Sbi *SbiServer
Auth *AuthServer
User *UserServer
Role *RoleServer
Topology *TopologyServer
App *AppServer
NetworkElement *NetworkElementServer
Routes *RoutingTableServiceServer
Pnd *PndServer
Core *CoreServer
Csbi *CsbiServer
Sbi *SbiServer
Auth *AuthServer
User *UserServer
Role *RoleServer
Topology *TopologyServer
App *AppServer
NetworkElement *NetworkElementServer
Routes *RoutingTableServiceServer
ConfigurationManagement *ConfigurationManagementServer
}
// NewNBI receives a PndStore and returns a new gRPC *NorthboundInterface.
......@@ -48,17 +49,18 @@ func NewNBI(
) *NorthboundInterface {
return &NorthboundInterface{
Pnd: NewPndServer(pnds),
Core: NewCoreServer(pnds),
Csbi: NewCsbiServer(pnds),
Sbi: NewSbiServer(pnds),
Auth: NewAuthServer(&jwt, users),
User: NewUserServer(&jwt, users),
Role: NewRoleServer(&jwt, roles),
Topology: NewTopologyServer(topologyService, nodeService, portService),
App: NewAppServer(apps),
NetworkElement: NewNetworkElementServer(networkDomain),
Routes: NewRoutingTableServiceServer(routeService, nodeService, portService),
Pnd: NewPndServer(pnds),
Core: NewCoreServer(pnds),
Csbi: NewCsbiServer(pnds),
Sbi: NewSbiServer(pnds),
Auth: NewAuthServer(&jwt, users),
User: NewUserServer(&jwt, users),
Role: NewRoleServer(&jwt, roles),
Topology: NewTopologyServer(topologyService, nodeService, portService),
App: NewAppServer(apps),
NetworkElement: NewNetworkElementServer(networkDomain),
Routes: NewRoutingTableServiceServer(routeService, nodeService, portService),
ConfigurationManagement: NewConfigurationManagementServer(pnds, topologyService, nodeService, portService),
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment