From 2aa93d64f1a5a5888c8a998084574c9042a2074e Mon Sep 17 00:00:00 2001 From: Andre Sterba <andre.sterba@stud.h-da.de> Date: Tue, 11 Oct 2022 09:25:47 +0000 Subject: [PATCH] Add additional example application hostname-checker See merge request danet/gosdn!376 Co-authored-by: Fabian Seidl <fabian.b.seidl@stud.h-da.de> Co-authored-by: Malte Bauch <malte.bauch@tbnet.works> --- Makefile | 8 +- applications/hostname-checker/app.go | 127 ++++++++++++++++++++++++ applications/hostname-checker/device.go | 28 ++++++ applications/hostname-checker/main.go | 33 ++++++ 4 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 applications/hostname-checker/app.go create mode 100644 applications/hostname-checker/device.go create mode 100644 applications/hostname-checker/main.go diff --git a/Makefile b/Makefile index 1be95526c..63557f708 100644 --- a/Makefile +++ b/Makefile @@ -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: pre build-gosdn build-gosdnc build-orchestrator build-venv-manager build-arista-routing-engine-app build-hostname-checker-app build-gosdn: pre $(GOBUILD) -trimpath -o $(BUILD_ARTIFACTS_PATH)/gosdn ./controller/cmd/gosdn @@ -73,6 +73,9 @@ build-venv-manager: pre build-arista-routing-engine-app: pre $(GOBUILD) -trimpath -o $(BUILD_ARTIFACTS_PATH)/arista-routing-engine ./applications/arista-routing-engine +build-hostname-checker-app: pre + $(GOBUILD) -trimpath -o $(BUILD_ARTIFACTS_PATH)/hostname-checker ./applications/hostname-checker + containerize-all: containerize-gosdn containerize-gosdnc containerize-orchestrator containerize-target containerize-gosdn: @@ -93,6 +96,9 @@ containerize-target: containerize-arista-routing-engine-app: docker buildx build --rm -t arista-routing-engine-app -f applications/arista-routing-engine/arista-routing-engine.Dockerfile . +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 diff --git a/applications/hostname-checker/app.go b/applications/hostname-checker/app.go new file mode 100644 index 000000000..01d6867c7 --- /dev/null +++ b/applications/hostname-checker/app.go @@ -0,0 +1,127 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" + "time" + + "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/device" + + "github.com/google/uuid" + "github.com/openconfig/ygot/ygot" + + "code.fbi.h-da.de/danet/gosdn/application-framework/event" + "code.fbi.h-da.de/danet/gosdn/application-framework/models" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// Application is an example for a sdn application. +type Application struct { + eventService event.ServiceInterface + stopChannel chan os.Signal + grpcClientConn *grpc.ClientConn +} + +// Run runs the application. +func (a *Application) Run() { + signal.Notify(a.stopChannel, os.Interrupt, syscall.SIGTERM) + + a.eventService.SubscribeToEventType([]event.TypeToCallbackTuple{ + {Type: event.Update, Callback: a.callback}, + }) + a.eventService.SetupEventReciever(a.stopChannel) + + conn, err := grpc.Dial("localhost:55055", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + panic(err) + } + + a.grpcClientConn = conn + + var forever chan struct{} + + go func() { + for { + select { + case <-a.stopChannel: + close(forever) + _ = a.grpcClientConn.Close() + + return + } + } + }() + + <-forever +} + +func (a *Application) callback(event *event.Event) { + ctx := context.Background() + deviceServer := device.NewDeviceServiceClient(a.grpcClientConn) + + request := &device.GetDeviceRequest{ + Timestamp: time.Now().UnixNano(), + DeviceID: event.EntityID.String(), + } + + response, err := deviceServer.Get(ctx, request) + if err != nil { + fmt.Printf("Error %+v\n ", err) + return + } + + fmt.Printf("\n[APP] Device-ID: %v, Device-Name: %+v \n", response.Device.Id, response.Device.Name) + + d := NewDevice(uuid.MustParse(response.Device.Id), response.Device.Name) + + // Create 'root' path to be able to load the whole model from the store. + path, err := ygot.StringToPath("/", ygot.StructuredPath) + if err != nil { + panic(err) + } + + // Use unmarshall from the devices SBI to unmarshall ygot json in go struct. + err = models.Unmarshal([]byte(response.Device.Model), path, &d.Model) + if err != nil { + panic(err) + } + + if *d.Model.System.Config.Hostname != d.Name { + fmt.Printf("[APP] Device.Name (%s) doesn't match Device.Hostname (%s) in model! Updating...\n", + d.Name, + *d.Model.System.Config.Hostname, + ) + + *d.Model.System.Config.Hostname = d.Name + + modelAsString, err := models.GetModelAsString(&d.Model) + if err != nil { + panic(err) + } + + requestUpdate := &device.UpdateDeviceRequest{ + Timestamp: time.Now().UnixNano(), + Device: &device.Device{ + Id: d.UUID.String(), + Name: d.Name, + Model: modelAsString, + }, + } + + updateResponse, err := deviceServer.Update(ctx, requestUpdate) + if err != nil { + panic(err) + } + + fmt.Printf("[APP] Update response: %+v", updateResponse) + } else { + fmt.Printf("[APP] Device.Name (%s) does match Device.Hostname (%s) in model! Nothing to do for me...\n", + d.Name, + *d.Model.System.Config.Hostname, + ) + } +} diff --git a/applications/hostname-checker/device.go b/applications/hostname-checker/device.go new file mode 100644 index 000000000..3c2a1af84 --- /dev/null +++ b/applications/hostname-checker/device.go @@ -0,0 +1,28 @@ +package main + +import ( + "code.fbi.h-da.de/danet/gosdn/models/generated/arista" + + "github.com/google/uuid" +) + +// Device is a device. +type Device struct { + // UUID represents the Devices UUID + UUID uuid.UUID + + // Name is the device's human readable Name + Name string + + // Device embeds a ygot.GoStruct containing the device details + Model arista.Device +} + +// NewDevice creates a new device. +func NewDevice(id uuid.UUID, name string) *Device { + return &Device{ + UUID: id, + Model: arista.Device{}, + Name: name, + } +} diff --git a/applications/hostname-checker/main.go b/applications/hostname-checker/main.go new file mode 100644 index 000000000..d68ebc980 --- /dev/null +++ b/applications/hostname-checker/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "os" + + "code.fbi.h-da.de/danet/gosdn/application-framework/event" + "code.fbi.h-da.de/danet/gosdn/application-framework/registration" + "github.com/sirupsen/logrus" +) + +func main() { + queueCredentials, err := registration.Register("localhost:55055", "hostname-checker", "SecurePresharedToken") + if err != nil { + logrus.Errorf("failed to register application on control plane. %v", err) + os.Exit(1) + } + + eventService, err := event.NewEventService( + queueCredentials, + []event.Topic{event.Device}, + ) + if err != nil { + logrus.Errorf("failed to create event service. %v", err) + os.Exit(1) + } + + app := &Application{ + eventService: eventService, + stopChannel: make(chan os.Signal, 1), + } + + app.Run() +} -- GitLab