Skip to content
Snippets Groups Projects
Commit d38ee202 authored by Manuel Kieweg's avatar Manuel Kieweg
Browse files

Merge branch 'develop' into grpc-nbi

parents c3d82e7f 92c32dce
No related branches found
No related tags found
1 merge request!155Northbound Interface
# Collection of programming stuff
## Dependencies
* github.com/spf13/cobra: used for basic cli of gosdn, such as starting the daemon, get versioning info etc
* grpc
* ygot
## Structure of the code
main.go: main() function
nucleus/: core functionality of gosdn
ygot (yang for go tools).
Checkout this in go: go get github.com/openconfig/ygot/ygot
## Usefull things to know
Regenerate gRPC code (https://grpc.io/docs/languages/go/quickstart/#regenerate-grpc-code)
* ( cd ~/go/src/github.com/grpc-go/cmd/protoc-gen-go-grpc/ && go install . )
*
protoc \
--go_out=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config:. \
--go-grpc_out=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config:. \
--go_opt=paths=source_relative \
--go-grpc_opt=paths=source_relative \
cliInterface/gosdnCLI.proto
Generate the ygot code:
just type: go generate
......@@ -4,7 +4,7 @@ import (
"context"
"os"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
nucleus "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
log "github.com/sirupsen/logrus"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
......
......@@ -7,7 +7,7 @@ run:
- test
skip-dirs-default: true
skip-files:
- nucleus/http.go
- http.go
# output settings -> code-climate for GitLab
output:
format: code-climate
......@@ -26,8 +26,11 @@ linters:
disable-all: true
enable:
- gofmt
- goimports
- golint
- gocyclo
- govet
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
\ No newline at end of file
......@@ -44,6 +44,5 @@ unit-test:
controller-test:
script:
- cd ./nucleus
- go test -race -v -run TestRun
<<: *test
\ No newline at end of file
package cli
import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"context"
"fmt"
gpb "github.com/openconfig/gnmi/proto/gnmi"
"strings"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
)
// Capabilities sends a gNMI Capabilities request to the specified target
......
package cli
import (
"context"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"context"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
)
......@@ -31,7 +34,7 @@ func Get(a, u, p string, args ...string) (*gpb.GetResponse, error) {
log.Debug(resp)
r, ok := resp.(*gpb.GetResponse)
if !ok {
return nil, &nucleus.ErrInvalidTypeAssertion{}
return nil, &errors.ErrInvalidTypeAssertion{}
}
return r, nil
}
package cli
import (
"context"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"context"
)
// Set sends a gNMI Set request to the specified target. Only one
......
package cli
import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"context"
"fmt"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
"os"
"os/signal"
"syscall"
"time"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
)
// Subscribe starts a gNMI subscriber requersting the specified paths on the target and
......@@ -47,7 +50,7 @@ func Subscribe(a, u, p string, sample, heartbeat int64, args ...string) error {
}
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGILL, syscall.SIGTERM)
ctx := context.WithValue(context.Background(), nucleus.CtxKeyOpts, opts) //nolint
ctx := context.WithValue(context.Background(), types.CtxKeyOpts, opts) //nolint
go func() {
if err := device.Transport.Subscribe(ctx); err != nil {
log.Fatal(err)
......
package cli
import (
"context"
"net"
"reflect"
"code.fbi.h-da.de/cocsn/gosdn/forks/google/gnmi"
oc "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig"
"context"
pb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/goyang/pkg/yang"
"github.com/openconfig/ygot/util"
......@@ -11,8 +14,6 @@ import (
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"net"
"reflect"
)
type server struct {
......
......@@ -35,7 +35,8 @@ import (
"context"
"os"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/cocsn/gosdn"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
......@@ -60,7 +61,7 @@ for REST API calls.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
return nucleus.Run(ctx)
return gosdn.Run(ctx)
},
}
......
......@@ -33,6 +33,7 @@ package cmd
import (
"errors"
"github.com/spf13/cobra"
)
......
package nucleus
package gosdn
import (
"code.fbi.h-da.de/cocsn/gosdn/database"
"context"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"net/http"
"os"
"os/signal"
"sync"
"time"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"code.fbi.h-da.de/cocsn/gosdn/database"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
var coreLock sync.RWMutex
......@@ -20,8 +24,8 @@ type Core struct {
// deprecated
database database.Database
pndc pndStore
sbic sbiStore
pndc *nucleus.PndStore
sbic *nucleus.SbiStore
httpServer *http.Server
stopChan chan os.Signal
}
......@@ -31,8 +35,8 @@ var c *Core
func init() {
c = &Core{
database: database.Database{},
pndc: pndStore{store{}},
sbic: sbiStore{store{}},
pndc: nucleus.NewPndStore(),
sbic: nucleus.NewSbiStore(),
stopChan: make(chan os.Signal, 1),
}
......@@ -49,25 +53,26 @@ func initialize() error {
// TODO: Start grpc listener here
coreLock.Lock()
defer coreLock.Unlock()
return httpAPI()
startHttpServer()
return nil
}
// createSouthboundInterfaces initializes the controller with its supported SBIs
func createSouthboundInterfaces() error {
sbi := &OpenConfig{id: uuid.New()}
if err := c.sbic.add(sbi); err != nil {
sbi := nucleus.NewSBI(types.Openconfig)
if err := c.sbic.Add(sbi); err != nil {
return err
}
return createPrincipalNetworkDomain(sbi)
}
// createPrincipalNetworkDomain initializes the controller with an initial PND
func createPrincipalNetworkDomain(sbi SouthboundInterface) error {
pnd, err := NewPND("base", "gosdn base pnd", uuid.New(), sbi)
func createPrincipalNetworkDomain(s nucleus.SouthboundInterface) error {
pnd, err := nucleus.NewPND("base", "gosdn base pnd", uuid.New(), s)
if err != nil {
return err
}
err = c.pndc.add(pnd)
err = c.pndc.Add(pnd)
if err != nil {
return err
}
......
package nucleus
package gosdn
import (
"context"
......
......@@ -2,6 +2,7 @@ package database
import (
"errors"
"github.com/neo4j/neo4j-go-driver/neo4j"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
......
gosdn.png

132 KiB

@startuml
namespace nucleus {
class ClientConfig << (S,Aquamarine) >> {
+ Identifier string
+ Endpoint string
+ Username string
+ Password string
+ GjsonDefaultPath string
+ GjsonConnectionsPath string
}
class Core << (S,Aquamarine) >> {
- devices <font color=blue>map</font>[string]Device
- southboundInterfaces <font color=blue>map</font>[string]interfaces.SouthboundInterface
- prinipalNetworkDomains <font color=blue>map</font>[uuid.UUID]interfaces.PrincipalNetworkDomain
- database database.Database
- config controllerConfig
+ IsRunning <font color=blue>chan</font> bool
- readControllerConfig(configFileController string) error
+ Init(socket string, configFileController string, configFileClient string, IsRunningChannel <font color=blue>chan</font> bool)
+ AttachDatabase()
+ Shutdown()
}
class Device << (S,Aquamarine) >> {
- device ygot.GoStruct
+ SBI interfaces.SouthboundInterface
+ Config DeviceConfig
}
class DeviceConfig << (S,Aquamarine) >> {
+ Identifier uuid.UUID
+ Endpoint string
+ Username string
+ Password string
}
interface PrincipalNetworkDomain {
+ Destroy() error
+ AddSbi() error
+ RemoveSbi() error
+ AddDevice( interfaces.Device) error
+ RemoveDevice(uuid uuid.UUID) error
}
interface SouthboundInterface {
}
class buf << (S,Aquamarine) >> {
+ Write(p []byte) (int, error)
}
class controllerConfig << (S,Aquamarine) >> {
+ CliSocket string
+ DatabaseSocket string
+ DatabaseUser string
+ DatabasePassword string
+ DatabaseCrypto bool
+ ConfigPath string
}
class logConnection << (S,Aquamarine) >> {
- stream proto.GrpcCli_CreateLogStreamServer
- id string
- active bool
- error <font color=blue>chan</font> error
}
class nucleus.buf << (T, #FF7700) >> {
}
class pndImplementation << (S,Aquamarine) >> {
- name string
- sbiContainer <font color=blue>map</font>[string]*interfaces.SouthboundInterface
- devices <font color=blue>map</font>[uuid.UUID]Device
+ Destroy() error
+ AddSbi() error
+ RemoveSbi() error
+ AddDevice(device Device) error
+ RemoveDevice(uuid uuid.UUID) error
}
class server << (S,Aquamarine) >> {
- core *Core
- logConnections []*logConnection
+ SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloReply, error)
+ CreateLogStream(req *emptypb.Empty, stream proto.GrpcCli_CreateLogStreamServer) error
+ BroadcastLog(log *proto.LogReply)
+ Shutdown(ctx context.Context, in *proto.ShutdownRequest) (*proto.ShutdownReply, error)
+ TAPIGetEdge(ctx context.Context, in *proto.TAPIRequest) (*proto.TAPIReply, error)
+ TAPIGetEdgeNode(ctx context.Context, in *proto.TAPIRequest) (*proto.TAPIReply, error)
+ TAPIGetLink(ctx context.Context, in *proto.TAPIRequest) (*proto.TAPIReply, error)
}
}
"proto.UnimplementedGrpcCliServer" *-- "nucleus.server"
namespace sbi {
class OpenConfig << (S,Aquamarine) >> {
- name string
- clientContainer []Client
+ AddClient() error
+ RemoveClient() error
+ CollectHeartbeats() error
+ ListClients() <font color=blue>map</font>[int]interfaces.Client
}
}
"__builtin__.[]byte" #.. "nucleus.buf"
@enduml
package nucleus
package gosdn
import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"context"
"fmt"
"github.com/google/uuid"
......@@ -13,10 +16,10 @@ import (
"time"
)
var apiOpmap = map[string]Operation{
"update": TransportUpdate,
"replace": TransportReplace,
"delete": TransportDelete,
var apiOpmap = map[string]types.Operation{
"update": types.TransportUpdate,
"replace": types.TransportReplace,
"delete": types.TransportDelete,
}
func stopHttpServer() error {
......@@ -32,19 +35,17 @@ func registerHttpHandler() {
fmt.Println("Recovered in f", r)
}
}()
http.HandleFunc("/api", httpHandler)
http.HandleFunc("/api", httpApi)
http.HandleFunc("/livez", healthCheck)
http.HandleFunc("/readyz", readynessCheck)
}
// deprecated
func httpAPI() error {
func startHttpServer() {
registerHttpHandler()
c.httpServer = &http.Server{Addr: ":8080"}
go func() {
log.Info(c.httpServer.ListenAndServe())
}()
return nil
}
func healthCheck(writer http.ResponseWriter, request *http.Request) {
......@@ -55,8 +56,9 @@ func readynessCheck(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusOK)
}
// deprecated
// nolint
func httpHandler(writer http.ResponseWriter, request *http.Request) {
func httpApi(writer http.ResponseWriter, request *http.Request) {
log.WithFields(log.Fields{
"request": request,
}).Debug("incoming request")
......@@ -89,16 +91,16 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
}
}
var pnd PrincipalNetworkDomain
var sbi SouthboundInterface
var httpPnd nucleus.PrincipalNetworkDomain
var httpSbi nucleus.SouthboundInterface
if query.Get("q") != "init" && query.Get("q") != "getIDs" {
pnd, err = c.pndc.get(pid)
httpPnd, err = c.pndc.Get(pid)
if err != nil {
handleServerError(writer, err)
return
}
sbic := pnd.GetSBIs()
sbi, err = sbic.(*sbiStore).get(sid)
sbic := httpPnd.GetSBIs()
httpSbi, err = sbic.(*nucleus.SbiStore).Get(sid)
if err != nil {
handleServerError(writer, err)
return
......@@ -107,18 +109,18 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
switch q := query.Get("q"); q {
case "addDevice":
d, err := NewDevice(sbi, &GnmiTransportOptions{
d, err := nucleus.NewDevice(httpSbi, &nucleus.GnmiTransportOptions{
Config: gnmi.Config{
Addr: query.Get("address"),
Password: query.Get("password"),
Username: query.Get("username"),
Encoding: gpb.Encoding_JSON_IETF,
},
SetNode: sbi.SetNode(),
Unmarshal: sbi.(*OpenConfig).Unmarshal(),
SetNode: httpSbi.SetNode(),
Unmarshal: httpSbi.(*nucleus.OpenConfig).Unmarshal(),
RespChan: make(chan *gpb.SubscribeResponse),
})
err = pnd.AddDevice(d)
err = httpPnd.AddDevice(d)
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Error(err)
......@@ -128,10 +130,10 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "device added\n")
fmt.Fprintf(writer, "UUID: %v\n", d.UUID)
case "request":
err = pnd.Request(id, query.Get("path"))
err = httpPnd.Request(id, query.Get("path"))
if err != nil {
switch err.(type) {
case *ErrNotFound:
case *errors.ErrNotFound:
writer.WriteHeader(http.StatusNotFound)
default:
writer.WriteHeader(http.StatusInternalServerError)
......@@ -141,10 +143,10 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
}
writer.WriteHeader(http.StatusOK)
case "requestAll":
err = pnd.RequestAll(query.Get("path"))
err = httpPnd.RequestAll(query.Get("path"))
if err != nil {
switch err.(type) {
case *ErrNotFound:
case *errors.ErrNotFound:
writer.WriteHeader(http.StatusNotFound)
default:
writer.WriteHeader(http.StatusInternalServerError)
......@@ -154,10 +156,10 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
}
writer.WriteHeader(http.StatusOK)
case "getDevice":
device, err := pnd.MarshalDevice(id)
device, err := httpPnd.MarshalDevice(id)
if err != nil {
switch err.(type) {
case *ErrNotFound:
case *errors.ErrNotFound:
writer.WriteHeader(http.StatusNotFound)
default:
writer.WriteHeader(http.StatusInternalServerError)
......@@ -173,7 +175,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
writeIDs(writer, "PNDs", pnds)
writeIDs(writer, "SBIs", c.sbic.UUIDs())
for _, id := range pnds {
p, err := c.pndc.get(id)
p, err := c.pndc.Get(id)
if err != nil {
handleServerError(writer, err)
return
......@@ -184,22 +186,22 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
writeIDs(writer, "PNDs", c.pndc.UUIDs())
writeIDs(writer, "SBIs", c.sbic.UUIDs())
case "update", "replace":
if err := pnd.ChangeOND(id, apiOpmap[q], query.Get("path"), query.Get("value")); err != nil {
if err := httpPnd.ChangeOND(id, apiOpmap[q], query.Get("path"), query.Get("value")); err != nil {
handleServerError(writer, err)
return
}
writer.WriteHeader(http.StatusOK)
case "delete":
if err := pnd.ChangeOND(id, TransportDelete, query.Get("path")); err != nil {
if err := httpPnd.ChangeOND(id, types.TransportDelete, query.Get("path")); err != nil {
handleServerError(writer, err)
return
}
writer.WriteHeader(http.StatusOK)
case "change-list":
changes := pnd.Committed()
changes := httpPnd.Committed()
writeIDs(writer, "Tentative changes", changes)
case "change-list-pending":
changes := pnd.Pending()
changes := httpPnd.Pending()
writeIDs(writer, "Pending changes", changes)
case "change-commit":
cuid, err := uuid.Parse(query.Get("cuid"))
......@@ -207,7 +209,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
handleServerError(writer, err)
return
}
if err := pnd.Commit(cuid); err != nil {
if err := httpPnd.Commit(cuid); err != nil {
handleServerError(writer, err)
return
}
......@@ -218,7 +220,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
handleServerError(writer, err)
return
}
if err := pnd.Confirm(cuid); err != nil {
if err := httpPnd.Confirm(cuid); err != nil {
handleServerError(writer, err)
return
}
......
package nucleus
package gosdn
import (
"code.fbi.h-da.de/cocsn/gosdn/mocks"
"errors"
"net/http"
"testing"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"code.fbi.h-da.de/cocsn/gosdn/mocks"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/mock"
"net/http"
"testing"
)
func testSetupHTTP() {
sbi = &OpenConfig{id: defaultSbiID}
sbi = nucleus.NewSBI(types.Openconfig)
sbi.Schema()
defaultSbiID = sbi.ID()
var err error
httpTestPND, err = NewPND("test", "test pnd", defaultPndID, sbi)
httpTestPND, err = nucleus.NewPND("test", "test pnd", defaultPndID, sbi)
if err != nil {
log.Fatal(err)
}
......@@ -30,10 +35,10 @@ func testSetupHTTP() {
}
args = "&uuid=" + mdid.String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String()
argsNotFound = "&uuid=" + uuid.New().String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String()
if err := c.sbic.add(sbi); err != nil {
if err := c.sbic.Add(sbi); err != nil {
log.Fatal(err)
}
if err := c.pndc.add(httpTestPND); err != nil {
if err := c.pndc.Add(httpTestPND); err != nil {
log.Fatal(err)
}
}
......@@ -182,10 +187,7 @@ func Test_httpApi(t *testing.T) {
},
}
coreLock.Lock()
if err := httpAPI(); err != nil {
t.Errorf("httpApi() error = %v", err)
return
}
startHttpServer()
coreLock.Unlock()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
......@@ -198,7 +200,7 @@ func Test_httpApi(t *testing.T) {
t.Errorf("httpApi() got: %v, want %v", got.StatusCode, tt.want.StatusCode)
}
if tt.name == "add-device" {
for k := range httpTestPND.(*pndImplementation).devices.store {
for _, k := range httpTestPND.Devices() {
if k != mdid {
if err := httpTestPND.RemoveDevice(k); err != nil {
t.Error(err)
......
package gosdn
import (
"context"
"os"
"testing"
"code.fbi.h-da.de/cocsn/gosdn/mocks"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto"
"github.com/google/uuid"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/mock"
pb "google.golang.org/protobuf/proto"
)
const apiEndpoint = "http://localhost:8080"
// UUIDs for test cases
var mdid uuid.UUID
var defaultSbiID uuid.UUID
var defaultPndID uuid.UUID
var cuid uuid.UUID
var sbi nucleus.SouthboundInterface
var httpTestPND nucleus.PrincipalNetworkDomain
var gnmiMessages map[string]pb.Message
var httpTestDevice nucleus.Device
var args string
var argsNotFound string
var mockContext = mock.MatchedBy(func(ctx context.Context) bool { return true })
// TestMain bootstraps all tests. Humongous beast
// TODO: Move somewhere more sensible
func TestMain(m *testing.M) {
log.SetReportCaller(true)
if os.Getenv("GOSDN_LOG") == "nolog" {
log.SetLevel(log.PanicLevel)
}
gnmiMessages = map[string]pb.Message{
"./test/proto/cap-resp-arista-ceos": &gpb.CapabilityResponse{},
"./test/proto/req-full-node": &gpb.GetRequest{},
"./test/proto/req-full-node-arista-ceos": &gpb.GetRequest{},
"./test/proto/req-interfaces-arista-ceos": &gpb.GetRequest{},
"./test/proto/req-interfaces-interface-arista-ceos": &gpb.GetRequest{},
"./test/proto/req-interfaces-wildcard": &gpb.GetRequest{},
"./test/proto/resp-full-node": &gpb.GetResponse{},
"./test/proto/resp-full-node-arista-ceos": &gpb.GetResponse{},
"./test/proto/resp-interfaces-arista-ceos": &gpb.GetResponse{},
"./test/proto/resp-interfaces-interface-arista-ceos": &gpb.GetResponse{},
"./test/proto/resp-interfaces-wildcard": &gpb.GetResponse{},
"./test/proto/resp-set-system-config-hostname": &gpb.SetResponse{},
}
for k, v := range gnmiMessages {
if err := proto.Read(k, v); err != nil {
log.Fatalf("error parsing %v: %v", k, err)
}
}
readTestUUIDs()
testSetupHTTP()
os.Exit(m.Run())
}
func readTestUUIDs() {
var err error
mdid, err = uuid.Parse("688a264e-5f85-40f8-bd13-afc42fcd5c7a")
defaultPndID, err = uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
cuid, err = uuid.Parse("3e8219b0-e926-400d-8660-217f2a25a7c6")
if err != nil {
log.Fatal(err)
}
}
func mockDevice() nucleus.Device {
sbi := &nucleus.OpenConfig{}
return nucleus.Device{
UUID: mdid,
GoStruct: sbi.Schema().Root,
SBI: sbi,
Transport: &mocks.Transport{},
}
}
package pnd
package nucleus
import (
"errors"
"os"
"sync"
"time"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"os"
"sync"
"time"
)
var changeTimeout time.Duration
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment