Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
PndAdapter.go 8.94 KiB
package adapter

import (
	"context"

	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
	"code.fbi.h-da.de/danet/gosdn/controller/api"
	"code.fbi.h-da.de/danet/gosdn/controller/customerrs"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
	"github.com/google/uuid"
	"github.com/openconfig/goyang/pkg/yang"
	"golang.org/x/sync/errgroup"
	"google.golang.org/protobuf/proto"
)

// PndAdapter is an API adapter to reflect the NetworkDomain
// interface.
type PndAdapter struct {
	id       uuid.UUID
	endpoint string
}

// NewPndAdapter creates a PND Adapter. It requires a valid PND UUID and a reachable
// goSDN endpoint.
func NewPndAdapter(id, endpoint string) (*PndAdapter, error) {
	pid, err := uuid.Parse(id)
	if err != nil {
		return nil, err
	}
	return &PndAdapter{
		id:       pid,
		endpoint: endpoint,
	}, nil
}

// AddSbi adds an SBI to the PND Adapter. Currently not implemented.
func (p *PndAdapter) AddSbi(s southbound.SouthboundInterface) error {
	return &customerrs.NotYetImplementedError{}
}

// RemoveSbi removes an SBI from the PND Adapter. Currently not implemented.
func (p *PndAdapter) RemoveSbi(uuid.UUID) error {
	return &customerrs.NotYetImplementedError{}
}

// AddNetworkElement adds a new device to the controller. The device name is optional.
// If no name is provided a name will be generated upon device creation.
func (p *PndAdapter) AddNetworkElement(ctx context.Context, name string, opts *tpb.TransportOption, sid uuid.UUID) (*ppb.SetMneListResponse, error) {
	resp, err := api.AddNetworkElement(ctx, p.endpoint, name, opts, sid, p.ID())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// GetSbiSchemaTree requests a sbi schema tree.
func (p *PndAdapter) GetSbiSchemaTree(ctx context.Context, sid uuid.UUID) (map[string]*yang.Entry, error) {
	resp, err := api.GetSbiSchemaTree(ctx, p.Endpoint(), p.ID(), sid)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// GetNetworkElement requests one or multiple devices belonging to a given
// PrincipalNetworkDomain from the controller.
func (p *PndAdapter) GetNetworkElement(ctx context.Context, identifier string) (*ppb.GetMneResponse, error) {
	resp, err := api.GetNetworkElement(ctx, p.endpoint, p.id.String(), identifier)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// GetFlattenedNetworkElements requests all devices belonging to the PrincipalNetworkDomain
// attached to this adapter. The requested devices also contain their config
// information as gNMI notifications.
func (p *PndAdapter) GetFlattenedNetworkElements(ctx context.Context) (*ppb.GetFlattenedMneListResponse, error) {
	resp, err := api.GetFlattenedNetworkElements(ctx, p.endpoint, p.id.String())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// RemoveNetworkElement removes a device from the controller.
func (p *PndAdapter) RemoveNetworkElement(ctx context.Context, did uuid.UUID) (*ppb.DeleteMneResponse, error) {
	resp, err := api.DeleteNetworkElement(ctx, p.endpoint, p.id.String(), did.String())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// RemovePnd removes a PND from the controller.
func (p *PndAdapter) RemovePnd(ctx context.Context, pid uuid.UUID) (*core.DeletePndResponse, error) {
	resp, err := api.DeletePnd(ctx, p.endpoint, pid.String())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// ChangeMNE sends an API call to the controller requesting the creation of
// a change from the provided Operation, path and value. The Change is marked
// as Pending and times out after the specified timeout period.
func (p *PndAdapter) ChangeMNE(ctx context.Context, duid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (*ppb.SetPathListResponse, error) {
	var v string
	if len(value) != 0 {
		v = value[0]
	}
	resp, err := api.ChangeRequest(ctx, p.endpoint, duid.String(), p.id.String(), path, v, operation)
	if err != nil {
		return nil, err
	}
	return resp, err
}

// Request sends an API call to the controller requesting the specified path
// for the specified device.
func (p *PndAdapter) Request(ctx context.Context, did uuid.UUID, path string) (*ppb.GetPathResponse, error) {
	resp, err := api.GetPath(ctx, p.endpoint, p.id.String(), did.String(), path)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// SubscribeMNEPath sends an API call to the controller requesting to subscribe
// to a specific path of a specifc device.
func (p *PndAdapter) SubscribeMNEPath(ctx context.Context, did uuid.UUID, slist *ppb.SubscriptionList) (ppb.PndService_SubscribePathClient, error) {
	resp, err := api.SubscribePath(ctx, p.endpoint, p.id.String(), did.String(), slist)
	if err != nil {
		return nil, err
	}
	return resp, nil
}
// RequestAll sends an API call to the controller requesting the specified path
// for all registered devices. Not yet implemented.
func (p *PndAdapter) RequestAll(ctx context.Context, path string) ([]proto.Message, error) {
	resp, err := api.GetFlattenedNetworkElements(ctx, p.Endpoint(), p.ID().String())
	if err != nil {
		return []proto.Message{}, err
	}
	reqResult := make([]proto.Message, len(resp.Mne))
	g := new(errgroup.Group)
	for i, mne := range resp.Mne {
		i, mne := i, mne
		// TODO: probably the controller should do this; this would result in a
		// single request from CLI side.
		g.Go(func() error {
			resp, err := api.GetPath(ctx, p.endpoint, p.id.String(), mne.GetId(), path)
			if err != nil {
				return err
			}
			reqResult[i] = resp
			return nil
		})
	}
	if err := g.Wait(); err != nil {
		// return the parts that succeeded, aswell as the errors that have been
		// encountered
		return reqResult, err
	}
	return reqResult, nil
}

// ContainsNetworkElement sends an API call to the controller checking if a device
// with the given UUID is present. Not implemented, always returns false.
func (p *PndAdapter) ContainsNetworkElement(uuid.UUID) bool {
	return false
}

// GetSbi sends an API call to the controller requesting the
// registered SBI with the provided ID.
func (p *PndAdapter) GetSbi(ctx context.Context, sid string) (*ppb.GetSbiResponse, error) {
	resp, err := api.GetSbi(ctx, p.endpoint, p.id.String(), sid)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// GetSBIs sends an API call to the controller requesting the
// registered SBIs. Not implemented, always returns nil.
func (p *PndAdapter) GetSBIs(ctx context.Context) (*ppb.GetSbiListResponse, error) {
	resp, err := api.GetSBIs(ctx, p.endpoint, p.id.String())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// ID returns the PND Adapter's UUID.
func (p *PndAdapter) ID() uuid.UUID {
	return p.id
}

// Endpoint returns the PND Adapter's endpoint.
func (p *PndAdapter) Endpoint() string {
	return p.endpoint
}

// PendingChanges sends an API call to the controller requesting
// the UUIDs of all pending changes.
func (p *PndAdapter) PendingChanges(ctx context.Context) ([]*ppb.Change, error) {
	resp, err := api.GetChanges(ctx, p.endpoint, p.id.String())
	if err != nil {
		return nil, err
	}
	return filterChanges(ppb.ChangeState_CHANGE_STATE_PENDING, resp), nil
}

// CommittedChanges sends an API call to the controller requesting
// the UUIDs of all committed changes.
func (p *PndAdapter) CommittedChanges(ctx context.Context) ([]*ppb.Change, error) {
	resp, err := api.GetChanges(ctx, p.endpoint, p.id.String())
	if err != nil {
		return nil, err
	}
	return filterChanges(ppb.ChangeState_CHANGE_STATE_COMMITTED, resp), nil
}

// ConfirmedChanges sends an API call to the controller requesting
// the UUIDs of all confirmed changes.
func (p *PndAdapter) ConfirmedChanges(ctx context.Context) ([]*ppb.Change, error) {
	resp, err := api.GetChanges(ctx, p.endpoint, p.id.String())
	if err != nil {
		return nil, err
	}
	return filterChanges(ppb.ChangeState_CHANGE_STATE_CONFIRMED, resp), nil
}

// GetChange sends an API call to the controller requesting one or more changes
// for the specific PND.
func (p *PndAdapter) GetChange(ctx context.Context, identifier ...string) (*ppb.GetChangeResponse, error) {
	resp, err := api.GetChange(ctx, p.endpoint, p.id.String(), identifier...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// Commit sends an API call to the controller committing the specified change.
func (p *PndAdapter) Commit(ctx context.Context, cuid uuid.UUID) (*ppb.SetChangeListResponse, error) {
	resp, err := api.Commit(ctx, p.endpoint, p.id.String(), cuid.String())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// Confirm sends an API call to the controller confirming the specified change.
func (p *PndAdapter) Confirm(ctx context.Context, cuid uuid.UUID) (*ppb.SetChangeListResponse, error) {
	resp, err := api.Confirm(ctx, p.endpoint, p.id.String(), cuid.String())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func filterChanges(state ppb.ChangeState, resp *ppb.GetChangeListResponse) []*ppb.Change {
	changes := make([]*ppb.Change, 0)
	for _, ch := range resp.Change {
		if ch.State == state {
			changes = append(changes, ch)
		}
	}
	return changes
}