-
Andre Sterba authored
Co-authored-by:
Malte Bauch <malte.bauch@stud.h-da.de> Co-authored-by:
Neil-Jocelyn Schark <neil-jocelyn.schark@stud.h-da.de> Co-authored-by:
Fabian Seidel <fabian.b.seidl@stud.h-da.de>
Andre Sterba authoredCo-authored-by:
Malte Bauch <malte.bauch@stud.h-da.de> Co-authored-by:
Neil-Jocelyn Schark <neil-jocelyn.schark@stud.h-da.de> Co-authored-by:
Fabian Seidel <fabian.b.seidl@stud.h-da.de>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
pnd.go 16.16 KiB
package server
import (
"context"
"strings"
"time"
ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
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/metrics"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type pndServer struct {
ppb.UnimplementedPndServiceServer
}
func (p pndServer) GetOnd(ctx context.Context, request *ppb.GetOndRequest) (*ppb.GetOndResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
onds, err := fillOnds(pnd, false, request.Did...)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetOndResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Ond: onds,
}, nil
}
func (p pndServer) GetOndList(ctx context.Context, request *ppb.GetOndListRequest) (*ppb.GetOndListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
onds, err := fillOnds(pnd, true)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetOndListResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Ond: onds,
}, nil
}
func fillOnds(pnd networkdomain.NetworkDomain, all bool, did ...string) ([]*ppb.OrchestratedNetworkingDevice, error) {
var ondList []uuid.UUID
var onds []*ppb.OrchestratedNetworkingDevice
// all indicates if a client wants all devices or only a single one
switch all {
case true:
ondList = pnd.Devices()
onds = make([]*ppb.OrchestratedNetworkingDevice, len(ondList))
for _, id := range ondList {
did = append(did, id.String())
}
default:
if len(did) == 0 {
err := &errors.ErrInvalidParameters{
Func: fillOnds,
Param: "length of 'did' cannot be '0' when 'all' is set to 'false'",
}
log.Error(err)
return nil, err
}
onds = make([]*ppb.OrchestratedNetworkingDevice, 1)
}
for i, id := range did {
d, err := pnd.GetDevice(id)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
cfg := ygot.GNMINotificationsConfig{}
dev, err := ygot.TogNMINotifications(d.Model(), time.Now().UnixNano(), cfg)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
sbi := spb.SouthboundInterface{}
if d.SBI() != nil {
sbi.Id = d.SBI().ID().String()
sbi.Type = d.SBI().Type()
}
onds[i] = &ppb.OrchestratedNetworkingDevice{
Id: id,
Name: d.Name(),
Device: dev,
Sbi: &sbi,
}
}
return onds, nil
}
func (p pndServer) GetSbi(ctx context.Context, request *ppb.GetSbiRequest) (*ppb.GetSbiResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
sbis, err := fillSbis(pnd, false, request.Sid...)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetSbiResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Sbi: sbis,
}, nil
}
func (p pndServer) GetSbiList(ctx context.Context, request *ppb.GetSbiListRequest) (*ppb.GetSbiListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
sbis, err := fillSbis(pnd, true, "")
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetSbiListResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Sbi: sbis,
}, nil
}
func fillSbis(pnd networkdomain.NetworkDomain, all bool, sid ...string) ([]*spb.SouthboundInterface, error) {
var sbiList []uuid.UUID
sbiStore := pnd.GetSBIs()
switch all {
case true:
sbiList = sbiStore.UUIDs()
default:
var err error
if len(sid) == 0 {
return nil, &errors.ErrInvalidParameters{
Func: fillSbis,
Param: "length of 'sid' cannot be '0' when 'all' is set to 'false'",
}
}
sbiList, err = stringToUUID(sid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
}
sbis := make([]*spb.SouthboundInterface, len(sbiList))
for i, id := range sbiList {
_, err := sbiStore.Get(id)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
sbis[i] = &spb.SouthboundInterface{
Id: id.String(),
}
}
return sbis, nil
}
func stringToUUID(sid []string) ([]uuid.UUID, error) {
UUIDs := make([]uuid.UUID, len(sid))
for i, id := range sid {
parsed, err := uuid.Parse(id)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
UUIDs[i] = parsed
}
return UUIDs, nil
}
func (p pndServer) GetPath(ctx context.Context, request *ppb.GetPathRequest) (*ppb.GetPathResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
duid, err := uuid.Parse(request.Did)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
// In case we get the path from grpc-gateway we have to replace
path := strings.ReplaceAll(request.Path, "||", "/")
_, err = pnd.Request(duid, path)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
ond, err := fillOnds(pnd, false, request.Did)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetPathResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Device: ond[0].Device,
}, nil
}
func (p pndServer) GetChange(ctx context.Context, request *ppb.GetChangeRequest) (*ppb.GetChangeResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
changes, err := fillChanges(pnd, false, request.Cuid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetChangeResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Change: changes,
}, nil
}
func (p pndServer) GetChangeList(ctx context.Context, request *ppb.GetChangeListRequest) (*ppb.GetChangeListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
changes, err := fillChanges(pnd, true, "")
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.GetChangeListResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Change: changes,
}, nil
}
func fillChanges(pnd networkdomain.NetworkDomain, all bool, cuid ...string) ([]*ppb.Change, error) {
var changeList []uuid.UUID
switch all {
case true:
changeList = pnd.PendingChanges()
changeList = append(changeList, pnd.CommittedChanges()...)
default:
var err error
if len(cuid) == 0 {
return nil, &errors.ErrInvalidParameters{
Func: fillOnds,
Param: "length of 'did' cannot be '0' when 'all' is set to 'false'",
}
}
changeList, err = stringToUUID(cuid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
}
changes := make([]*ppb.Change, len(changeList))
for i, ch := range changeList {
c, err := pnd.GetChange(ch)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
changes[i] = &ppb.Change{
Id: ch.String(),
Age: c.Age().Microseconds(),
State: c.State(),
}
}
return changes, nil
}
func (p pndServer) SetOndList(ctx context.Context, request *ppb.SetOndListRequest) (*ppb.SetOndListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "set"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
for _, r := range request.Ond {
sid, err := uuid.Parse(r.Sbi.Id)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
if err := pnd.AddDevice(r.DeviceName, r.TransportOption, sid); err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
}
return &ppb.SetOndListResponse{
Timestamp: time.Now().UnixNano(),
Status: ppb.Status_STATUS_OK,
Responses: []*ppb.SetResponse{
{
Status: ppb.Status_STATUS_OK,
},
},
}, nil
}
func (p pndServer) SetChangeList(ctx context.Context, request *ppb.SetChangeListRequest) (*ppb.SetChangeListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "set"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
for _, r := range request.Change {
cuid, err := uuid.Parse(r.Cuid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
switch r.Op {
case ppb.Operation_OPERATION_COMMIT:
if err := pnd.Commit(cuid); err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
case ppb.Operation_OPERATION_CONFIRM:
if err := pnd.Confirm(cuid); err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
default:
return nil, &errors.ErrInvalidParameters{
Param: r.Op,
}
}
}
return &ppb.SetChangeListResponse{
Timestamp: time.Now().UnixNano(),
Status: ppb.Status_STATUS_OK,
Responses: []*ppb.SetResponse{
{
Status: ppb.Status_STATUS_OK,
},
},
}, nil
}
func (p pndServer) SetPathList(ctx context.Context, request *ppb.SetPathListRequest) (*ppb.SetPathListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "set"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
for _, r := range request.ChangeRequest {
did, err := uuid.Parse(r.Did)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
// TODO: Return CUID in API
_, err = pnd.ChangeOND(did, r.ApiOp, r.Path, r.Value)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
}
return &ppb.SetPathListResponse{
Timestamp: time.Now().UnixNano(),
Status: ppb.Status_STATUS_OK,
Responses: []*ppb.SetResponse{
{
Status: ppb.Status_STATUS_OK,
},
},
}, nil
}
func (p pndServer) SetSbiList(ctx context.Context, request *ppb.SetSbiListRequest) (*ppb.SetSbiListResponse, error) {
labels := prometheus.Labels{"service": "pnd", "rpc": "set"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
pid, err := uuid.Parse(request.Pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
return nil, handleRPCError(labels, err)
}
for _, r := range request.Sbi {
sbiType := filterSbiType(r.SbiType)
sbi, err := nucleus.NewSBI(sbiType)
if err != nil {
return nil, handleRPCError(labels, err)
}
err = pnd.AddSbi(sbi)
if err != nil {
return nil, handleRPCError(labels, err)
}
}
return &ppb.SetSbiListResponse{
Timestamp: time.Now().UnixNano(),
Status: ppb.Status_STATUS_OK,
Responses: []*ppb.SetResponse{
{
Status: ppb.Status_STATUS_OK,
},
},
}, nil
}
func filterSbiType(sbiType ppb.SbiType) spb.Type {
var spbType spb.Type
switch sbiType {
case ppb.SbiType_SBI_TYPE_OPENCONFIG:
spbType = spb.Type_TYPE_OPENCONFIG
case ppb.SbiType_SBI_TYPE_CONTAINERISED:
spbType = spb.Type_TYPE_CONTAINERISED
case ppb.SbiType_SBI_TYPE_PLUGIN:
spbType = spb.Type_TYPE_PLUGIN
default:
spbType = spb.Type_TYPE_UNSPECIFIED
}
return spbType
}
func (p pndServer) DeleteOnd(ctx context.Context, request *ppb.DeleteOndRequest) (*ppb.DeleteOndResponse, error) {
pid, err := uuid.Parse(request.Pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
pnd, err := pndc.GetPND(pid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
did, err := uuid.Parse(request.Did)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
if err := pnd.RemoveDevice(did); err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
return &ppb.DeleteOndResponse{
Timestamp: time.Now().UnixNano(),
Status: ppb.Status_STATUS_OK,
}, nil
}