Newer
Older
"fmt"
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/device"
"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"
"code.fbi.h-da.de/danet/gosdn/controller/store"
Malte Bauch
committed
"github.com/openconfig/gnmi/proto/gnmi"
Malte Bauch
committed
"github.com/openconfig/ygot/ytypes"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
// PndServer implements a pnd server
type PndServer struct {
pndStore networkdomain.PndStore
// NewPndServer receives a pndStore and returns a new pndServer.
func NewPndServer(pndStore networkdomain.PndStore) *PndServer {
return &PndServer{
pndStore: pndStore,
}
}
// GetOnd gets a specific ond
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 {
pnd, err := p.pndStore.Get(store.Query{ID: pid})
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
Malte Bauch
committed
device, err := pnd.GetDevice(request.Did)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
ond, err := fillOndBySpecificPath(pnd, device, "/")
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
Malte Bauch
committed
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Malte Bauch
committed
Ond: ond,
// GetOndList returns a list of existing onds
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 := p.pndStore.Get(store.Query{ID: pid})
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
Malte Bauch
committed
onds := pnd.Devices()
ondsBySpecificPath := make([]*ppb.OrchestratedNetworkingDevice, len(onds))
for i, ond := range onds {
ond, err := fillOndBySpecificPath(pnd, ond, "/")
Malte Bauch
committed
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
ondsBySpecificPath[i] = ond
Malte Bauch
committed
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Ond: ondsBySpecificPath,
func fillOndBySpecificPath(pnd networkdomain.NetworkDomain, d device.Device, path string) (*ppb.OrchestratedNetworkingDevice, error) {
Malte Bauch
committed
gnmiPath, err := ygot.StringToStructuredPath(path)
Malte Bauch
committed
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Malte Bauch
committed
opts := []ytypes.GetNodeOpt{
&ytypes.GetHandleWildcards{},
&ytypes.GetPartialKeyMatch{},
}
nodes, err := ytypes.GetNode(d.SBI().Schema().RootSchema(), d.GetModel(), gnmiPath, opts...)
Malte Bauch
committed
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Malte Bauch
committed
devices := make([]*gnmi.Notification, len(nodes))
for i, node := range nodes {
dev, err := genGnmiNotification(gnmiPath, node.Data)
Malte Bauch
committed
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Malte Bauch
committed
devices[i] = dev
Malte Bauch
committed
}
sbi := spb.SouthboundInterface{}
if d.SBI() != nil {
sbi.Id = d.SBI().ID().String()
sbi.Type = d.SBI().Type()
}
ond := &ppb.OrchestratedNetworkingDevice{
Id: d.ID().String(),
Name: d.Name(),
Malte Bauch
committed
Device: devices,
Malte Bauch
committed
Sbi: &sbi,
}
return ond, nil
}
Malte Bauch
committed
func genGnmiNotification(path *gnmi.Path, val any) (*gnmi.Notification, error) {
typedVal, err := ygot.EncodeTypedValue(val, gnmi.Encoding_JSON_IETF)
Malte Bauch
committed
if err != nil {
return nil, err
}
Malte Bauch
committed
return &gnmi.Notification{
Timestamp: time.Now().UnixNano(),
Update: []*gnmi.Update{
{
Path: &gnmi.Path{
Elem: path.GetElem(),
Malte Bauch
committed
},
Malte Bauch
committed
Val: typedVal,
Malte Bauch
committed
},
},
}, nil
}
// GetSbi gets a specific sbi
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)
pnd, err := p.pndStore.Get(store.Query{ID: pid})
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
Fabian Seidl
committed
sbiID, err := uuid.Parse(request.Sid)
if err != nil {
return nil, err
}
sbi, err := pnd.GetSBI(sbiID)
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Fabian Seidl
committed
Sbi: &spb.SouthboundInterface{
Id: sbiID.String(),
Type: sbi.Type(),
},
// GetSbiList gets all existing sbis
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)
pnd, err := p.pndStore.Get(store.Query{ID: pid})
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Fabian Seidl
committed
sbis, err := fillSbis(pnd)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Sbi: sbis,
Fabian Seidl
committed
func fillSbis(pnd networkdomain.NetworkDomain) ([]*spb.SouthboundInterface, error) {
sbis, err := pnd.GetSBIs()
if err != nil {
return nil, err
fmt.Printf("SBIS: %+v\n", sbis)
sbisToReturn := []*spb.SouthboundInterface{}
for _, sbi := range sbis {
sbisToReturn = append(sbisToReturn, &spb.SouthboundInterface{
Id: sbi.ID().String(),
})
fmt.Printf("SBIS: %+v\n", sbisToReturn)
return sbisToReturn, nil
Fabian Seidl
committed
func stringArrayToUUIDs(sid []string) ([]uuid.UUID, error) {
UUIDs := make([]uuid.UUID, len(sid))
for i, id := range sid {
parsed, err := uuid.Parse(id)
if err != nil {
return nil, status.Errorf(codes.Aborted, "%v", err)
UUIDs[i] = parsed
}
return UUIDs, nil
}
// GetPath gets a path on a ond
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 := p.pndStore.Get(store.Query{ID: pid})
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
device, err := pnd.GetDevice(request.Did)
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)
}
Malte Bauch
committed
ond, err := fillOndBySpecificPath(pnd, device, path)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Malte Bauch
committed
return &ppb.GetPathResponse{
Timestamp: time.Now().UnixNano(),
Pnd: &ppb.PrincipalNetworkDomain{
Id: pnd.ID().String(),
Name: pnd.GetName(),
Description: pnd.GetDescription(),
},
Malte Bauch
committed
Device: ond.Device,
// GetChange gets a specific change of a ond
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 := p.pndStore.Get(store.Query{ID: 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
}
// GetChangeList gets all existing changes
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 := p.pndStore.Get(store.Query{ID: 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{
Malte Bauch
committed
Func: fillChanges,
Param: "length of 'did' cannot be '0' when 'all' is set to 'false'",
}
}
Fabian Seidl
committed
changeList, err = stringArrayToUUIDs(cuid)
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)
diff, err := ygot.Diff(c.PreviousState(), c.IntendedState())
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
Age: c.Age().Microseconds(),
State: c.State(),
// SetOndList updates the list of onds
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 {
pnd, err := p.pndStore.Get(store.Query{ID: pid})
deviceIDs := make([]uuid.UUID, 0, len(request.Ond))
sid, err := uuid.Parse(r.Sbi.Id)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
did, err := pnd.AddDevice(r.DeviceName, r.TransportOption, sid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
deviceIDs = append(deviceIDs, did)
r := make([]*ppb.SetResponse, len(deviceIDs))
for i, did := range deviceIDs {
r[i] = &ppb.SetResponse{Id: did.String(), Status: ppb.Status_STATUS_OK}
}
// SetChangeList sets a list of changes
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 := p.pndStore.Get(store.Query{ID: pid})
if err != nil {
return nil, handleRPCError(labels, err)
}
responses := make([]*ppb.SetResponse, len(request.Change))
for i, r := range request.Change {
cuid, err := uuid.Parse(r.Cuid)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
default:
return nil, &errors.ErrInvalidParameters{
Param: r.Op,
}
}
responses[i] = &ppb.SetResponse{
Id: cuid.String(),
Status: ppb.Status_STATUS_OK,
}
Responses: responses,
// SetPathList sets a list of paths
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 := p.pndStore.Get(store.Query{ID: pid})
if err != nil {
return nil, handleRPCError(labels, err)
}
responses := make([]*ppb.SetResponse, len(request.ChangeRequest))
for i, r := range request.ChangeRequest {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
cid, err := pnd.ChangeOND(did, r.ApiOp, r.Path, r.Value)
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
responses[i] = &ppb.SetResponse{
Status: ppb.Status_STATUS_OK,
Id: cid.String(),
}
Responses: responses,
// SetSbiList sets a list of sbis
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 := p.pndStore.Get(store.Query{ID: pid})
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
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
}
// DeleteOnd deletes a ond
func (p PndServer) DeleteOnd(ctx context.Context, request *ppb.DeleteOndRequest) (*ppb.DeleteOndResponse, error) {
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
pnd, err := p.pndStore.Get(store.Query{ID: pid})
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
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)
}