Commit ce5c6074 authored by Manuel Kieweg's avatar Manuel Kieweg 🤷
Browse files

Merge branch 'remove-forks' into 'master'

use forks from separate repo

See merge request !186
parents e4ae50ad 3c88526a
......@@ -20,7 +20,6 @@ COPY ./cmd ./cmd
COPY ./cli ./cli
COPY ./configs ./configs
COPY ./database ./database
COPY ./forks ./forks
COPY ./mocks ./mocks
COPY ./nucleus ./nucleus
COPY ./test ./test
......
......@@ -41,7 +41,7 @@ Any device directly configured by `goSDN`
You can install the latest release of `goSDN` locally using the `go get` command. Since the repository and some dependencies are not publicly available you have to modify your git config first:
```sh
> git config --global url."git@code.fbi.h-da.de:".insteadOf "https://code.fbi.h-da.de"
> go env -w GOPRIVATE=code.fbi.h-da.de/cocsn/*
> go env -w GOPRIVATE=code.fbi.h-da.de/danet/*
> go get code.fbi.h-da.de/danet/gosdn/cmd/gosdn
```
......@@ -49,7 +49,7 @@ To install the development version you need to clone the repo and use `go instal
```sh
# If you haven't cloned the repo yet
> git config --global url."git@code.fbi.h-da.de:".insteadOf "https://code.fbi.h-da.de"
> go env -w GOPRIVATE=code.fbi.h-da.de/cocsn/*
> go env -w GOPRIVATE=code.fbi.h-da.de/danet/*
> cd $GOPATH/src
> git clone git@code.fbi.h-da.de:cocsn/gosdn.git
......
......@@ -5,7 +5,7 @@ import (
"fmt"
"strings"
"code.fbi.h-da.de/danet/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
)
......
......@@ -3,7 +3,7 @@ package cli
import (
"context"
"code.fbi.h-da.de/danet/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
......
......@@ -4,7 +4,7 @@ import (
"context"
"os"
"code.fbi.h-da.de/danet/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/gosdn/nucleus"
"code.fbi.h-da.de/danet/gosdn/nucleus/util/proto"
pb "google.golang.org/protobuf/proto"
......
......@@ -8,7 +8,7 @@ import (
"syscall"
"time"
"code.fbi.h-da.de/danet/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/forks/goarista/gnmi"
"code.fbi.h-da.de/danet/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
......
......@@ -5,7 +5,7 @@ import (
"net"
"reflect"
"code.fbi.h-da.de/danet/gosdn/forks/google/gnmi"
"code.fbi.h-da.de/danet/forks/google/gnmi"
oc "code.fbi.h-da.de/danet/yang-models/generated/openconfig"
pb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/goyang/pkg/yang"
......
// Copyright (c) 2019 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package gnmi
import (
"fmt"
"strconv"
"strings"
"github.com/openconfig/gnmi/proto/gnmi_ext"
)
// ArbitrationExt takes a string representation of a master arbitration value
// (e.g. "23", "role:42") and return a *gnmi_ext.Extension.
func ArbitrationExt(s string) (*gnmi_ext.Extension, error) {
if s == "" {
return nil, nil
}
roleID, electionID, err := parseArbitrationString(s)
if err != nil {
return nil, err
}
arb := &gnmi_ext.MasterArbitration{
Role: &gnmi_ext.Role{Id: roleID},
ElectionId: &gnmi_ext.Uint128{High: 0, Low: electionID},
}
ext := gnmi_ext.Extension_MasterArbitration{MasterArbitration: arb}
return &gnmi_ext.Extension{Ext: &ext}, nil
}
// parseArbitrationString parses the supplied string and returns the role and election id
// values. Input is of the form [<role>:]<election_id>, where election_id is a uint64.
//
// Examples:
// "1"
// "admin:42"
func parseArbitrationString(s string) (string, uint64, error) {
tokens := strings.Split(s, ":")
switch len(tokens) {
case 1: // just election id
id, err := parseElectionID(tokens[0])
return "", id, err
case 2: // role and election id
id, err := parseElectionID(tokens[1])
return tokens[0], id, err
}
return "", 0, fmt.Errorf("badly formed arbitration id (%s)", s)
}
func parseElectionID(s string) (uint64, error) {
id, err := strconv.ParseUint(s, 0, 64)
if err != nil {
return 0, fmt.Errorf("badly formed arbitration id (%s)", s)
}
return id, nil
}
// Copyright (c) 2019 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package gnmi
import (
"fmt"
"testing"
"github.com/aristanetworks/goarista/test"
"github.com/openconfig/gnmi/proto/gnmi_ext"
)
func arbitration(role string, id *gnmi_ext.Uint128) *gnmi_ext.Extension {
arb := &gnmi_ext.MasterArbitration{
Role: &gnmi_ext.Role{Id: role},
ElectionId: id,
}
ext := gnmi_ext.Extension_MasterArbitration{MasterArbitration: arb}
return &gnmi_ext.Extension{Ext: &ext}
}
func electionID(high, low uint64) *gnmi_ext.Uint128 {
return &gnmi_ext.Uint128{High: high, Low: low}
}
func TestArbitrationExt(t *testing.T) {
testCases := map[string]struct {
s string
ext *gnmi_ext.Extension
err error
}{
"empty": {},
"no_role": {
s: "1",
ext: arbitration("", electionID(0, 1)),
},
"with_role": {
s: "admin:1",
ext: arbitration("admin", electionID(0, 1)),
},
"large_no_role": {
s: "9223372036854775807",
ext: arbitration("", electionID(0, 9223372036854775807)),
},
"large_with_role": {
s: "admin:18446744073709551615",
ext: arbitration("admin", electionID(0, 18446744073709551615)),
},
"invalid": {
s: "cat",
err: fmt.Errorf("badly formed arbitration id (%s)", "cat"),
},
"invalid_too_many_colons": {
s: "dog:1:2",
err: fmt.Errorf("badly formed arbitration id (%s)", "dog:1:2"),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
ext, err := ArbitrationExt(tc.s)
if !test.DeepEqual(tc.ext, ext) {
t.Errorf("Expected %#v, got %#v", tc.ext, ext)
}
if !test.DeepEqual(tc.err, err) {
t.Errorf("Expected %v, got %v", tc.err, err)
}
})
}
}
// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package gnmi
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"math"
"net"
"os"
"io/ioutil"
"strings"
"github.com/golang/protobuf/proto"
pb "github.com/openconfig/gnmi/proto/gnmi"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/metadata"
)
const (
defaultPort = "6030"
// HostnameArg is the value to be replaced by the actual hostname
HostnameArg = "HOSTNAME"
)
// PublishFunc is the method to publish responses
type PublishFunc func(addr string, message proto.Message)
// ParseHostnames parses a comma-separated list of names and replaces HOSTNAME with the current
// hostname in it
func ParseHostnames(list string) ([]string, error) {
items := strings.Split(list, ",")
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
names := make([]string, len(items))
for i, name := range items {
if name == HostnameArg {
name = hostname
}
names[i] = name
}
return names, nil
}
// Config is the gnmi.Client config
type Config struct {
Addr string
CAFile string
CertFile string
KeyFile string
Password string
Username string
TLS bool
Compression string
DialOptions []grpc.DialOption
Token string
Encoding pb.Encoding
}
// SubscribeOptions is the gNMI subscription request options
type SubscribeOptions struct {
UpdatesOnly bool
Prefix string
Mode string
StreamMode string
SampleInterval uint64
SuppressRedundant bool
HeartbeatInterval uint64
Paths [][]string
Origin string
Target string
}
// accessTokenCred implements credentials.PerRPCCredentials, the gRPC
// interface for credentials that need to attach security information
// to every RPC.
type accessTokenCred struct {
bearerToken string
}
// newAccessTokenCredential constructs a new per-RPC credential from a token.
func newAccessTokenCredential(token string) credentials.PerRPCCredentials {
bearerFmt := "Bearer %s"
return &accessTokenCred{bearerToken: fmt.Sprintf(bearerFmt, token)}
}
func (a *accessTokenCred) GetRequestMetadata(ctx context.Context,
uri ...string) (map[string]string, error) {
authHeader := "Authorization"
return map[string]string{
authHeader: a.bearerToken,
}, nil
}
func (a *accessTokenCred) RequireTransportSecurity() bool { return true }
// DialContext connects to a gnmi service and returns a client
func DialContext(ctx context.Context, cfg *Config) (pb.GNMIClient, error) {
opts := append([]grpc.DialOption(nil), cfg.DialOptions...)
switch cfg.Compression {
case "":
case "gzip":
opts = append(opts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
default:
return nil, fmt.Errorf("unsupported compression option: %q", cfg.Compression)
}
if cfg.TLS || cfg.CAFile != "" || cfg.CertFile != "" || cfg.Token != "" {
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
}
if cfg.CAFile != "" {
b, err := ioutil.ReadFile(cfg.CAFile)
if err != nil {
return nil, err
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(b) {
return nil, fmt.Errorf("credentials: failed to append certificates")
}
tlsConfig.RootCAs = cp
} else {
tlsConfig.InsecureSkipVerify = true
}
if cfg.CertFile != "" {
if cfg.KeyFile == "" {
return nil, fmt.Errorf("please provide both -certfile and -keyfile")
}
cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
if cfg.Token != "" {
opts = append(opts,
grpc.WithPerRPCCredentials(newAccessTokenCredential(cfg.Token)))
}
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
} else {
opts = append(opts, grpc.WithInsecure())
}
dial := func(ctx context.Context, addrIn string) (conn net.Conn, err error) {
var network, addr string
split := strings.Split(addrIn, "://")
if l := len(split); l == 2 {
network = split[0]
addr = split[1]
} else {
network = "tcp"
addr = split[0]
}
conn, err = (&net.Dialer{}).DialContext(ctx, network, addr)
return
}
opts = append(opts,
grpc.WithContextDialer(dial),
// Allows received protobuf messages to be larger than 4MB
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)),
)
grpcconn, err := grpc.DialContext(ctx, cfg.Addr, opts...)
if err != nil {
return nil, fmt.Errorf("failed to dial: %s", err)
}
return pb.NewGNMIClient(grpcconn), nil
}
// Dial connects to a gnmi service and returns a client
func Dial(cfg *Config) (pb.GNMIClient, error) {
return DialContext(context.Background(), cfg)
}
// NewContext returns a new context with username and password
// metadata if they are set in cfg.
func NewContext(ctx context.Context, cfg *Config) context.Context {
if cfg.Username != "" {
ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(
"username", cfg.Username,
"password", cfg.Password))
}
return ctx
}
// NewGetRequest returns a GetRequest for the given paths
func NewGetRequest(ctx context.Context, paths [][]string, origin string) (*pb.GetRequest, error) {
val := ctx.Value("config")
cfg, ok := val.(*Config)
if !ok {
return nil, errors.New("invalid type assertion")
}
req := &pb.GetRequest{
Path: make([]*pb.Path, len(paths)),
Encoding: cfg.Encoding,
}
for i, p := range paths {
gnmiPath, err := ParseGNMIElements(p)
if err != nil {
return nil, err
}
req.Path[i] = gnmiPath
req.Path[i].Origin = origin
}
return req, nil
}
// NewSubscribeRequest returns a SubscribeRequest for the given paths
func NewSubscribeRequest(subscribeOptions *SubscribeOptions) (*pb.SubscribeRequest, error) {
var mode pb.SubscriptionList_Mode
switch subscribeOptions.Mode {
case "once":
mode = pb.SubscriptionList_ONCE
case "poll":
mode = pb.SubscriptionList_POLL
case "":
fallthrough
case "stream":
mode = pb.SubscriptionList_STREAM
default:
return nil, fmt.Errorf("subscribe mode (%s) invalid", subscribeOptions.Mode)
}
var streamMode pb.SubscriptionMode
switch subscribeOptions.StreamMode {
case "on_change":
streamMode = pb.SubscriptionMode_ON_CHANGE
case "sample":
streamMode = pb.SubscriptionMode_SAMPLE
case "":
fallthrough
case "target_defined":
streamMode = pb.SubscriptionMode_TARGET_DEFINED
default:
return nil, fmt.Errorf("subscribe stream mode (%s) invalid", subscribeOptions.StreamMode)
}
prefixPath, err := ParseGNMIElements(SplitPath(subscribeOptions.Prefix))
if err != nil {
return nil, err
}
subList := &pb.SubscriptionList{
Subscription: make([]*pb.Subscription, len(subscribeOptions.Paths)),
Mode: mode,
UpdatesOnly: subscribeOptions.UpdatesOnly,
Prefix: prefixPath,
}
if subscribeOptions.Target != "" {
if subList.Prefix == nil {
subList.Prefix = &pb.Path{}
}
subList.Prefix.Target = subscribeOptions.Target
}
for i, p := range subscribeOptions.Paths {
gnmiPath, err := ParseGNMIElements(p)
if err != nil {
return nil, err
}
gnmiPath.Origin = subscribeOptions.Origin
subList.Subscription[i] = &pb.Subscription{
Path: gnmiPath,
Mode: streamMode,
SampleInterval: subscribeOptions.SampleInterval,
SuppressRedundant: subscribeOptions.SuppressRedundant,
HeartbeatInterval: subscribeOptions.HeartbeatInterval,
}
}
return &pb.SubscribeRequest{Request: &pb.SubscribeRequest_Subscribe{
Subscribe: subList}}, nil
}
// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package gnmi
import (
"github.com/openconfig/gnmi/proto/gnmi"
)
// NotificationToMap converts a Notification into a map[string]interface{}
func NotificationToMap(notif *gnmi.Notification) (map[string]interface{}, error) {
m := make(map[string]interface{}, 1)
m["timestamp"] = notif.Timestamp
m["path"] = StrPath(notif.Prefix)
if len(notif.Update) != 0 {
updates := make(map[string]interface{}, len(notif.Update))
var err error
for _, update := range notif.Update {
updates[StrPath(update.Path)] = StrUpdateVal(update)
if err != nil {
return nil, err
}
}
m["updates"] = updates
}
if len(notif.Delete) != 0 {
deletes := make([]string, len(notif.Delete))
for i, del := range notif.Delete {
deletes[i] = StrPath(del)
}
m["deletes"] = deletes
}
return m, nil
}
// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package gnmi
import (
"bufio"
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path"
"strconv"
"strings"
"time"
pb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/gnmi/proto/gnmi_ext"
)
// GetWithRequest takes a fully formed GetRequest, performs the Get,
// and displays any response.
func GetWithRequest(ctx context.Context, client pb.GNMIClient,
req *pb.GetRequest) error {
resp, err := client.Get(ctx, req)
if err != nil {
return err
}
for _, notif := range resp.Notification {
prefix := StrPath(notif.Prefix)
for _, update := range notif.Update {
fmt.Printf("%s:\n", path.Join(prefix, StrPath(update.Path)))
fmt.Println(StrUpdateVal(update))
}
}
return nil
}
// Get sends a GetRequest to the given server.
func Get(ctx context.Context, client pb.GNMIClient, paths [][]string,
origin string) error {
req, err := NewGetRequest(ctx, paths, origin)
if err != nil {
return err
}
return GetWithRequest(ctx, client, req)
}
// val may be a path to a file or it may be json. First see if it is a
// file, if so return its contents, otherwise return val
func extractJSON(val string) []byte {
if jsonBytes, err := ioutil.ReadFile(val); err == nil {
return jsonBytes
}
// Best effort check if the value might a string literal, in which
// case wrap it in quotes. This is to allow a user to do:
// gnmi update ../hostname host1234
// gnmi update ../description 'This is a description'
// instead of forcing them to quote the string:
// gnmi update ../hostname '"host1234"'
// gnmi update ../description '"This is a description"'
maybeUnquotedStringLiteral := func(s string) bool {
if s == "true" || s == "false" || s == "null" || // JSON reserved words
strings.ContainsAny(s, `"'{}[]`) { // Already quoted or is a JSON object or array
return false
} else if _, err := strconv.ParseInt(s, 0, 32); err == nil {
// Integer. Using byte size of 32 because larger integer
// types are supposed to be sent as strings in JSON.
return false
} else if _, err := strconv.ParseFloat(s, 64); err == nil {
// Float