From 0933788ebd09a735736ffc64e7c539e0cd125a48 Mon Sep 17 00:00:00 2001 From: Manuel Kieweg <manuel.kieweg@h-da.de> Date: Wed, 24 Mar 2021 17:51:33 +0000 Subject: [PATCH] Simple HTTP API and CLI --- cli/http.go | 50 +++ cli/target.go | 11 +- cli/ygot.go | 6 +- cmd/addDevice.go | 56 ++++ cmd/cli.go | 56 ++++ cmd/cliSet.go | 57 ++++ cmd/getDevice.go | 50 +++ cmd/getIds.go | 50 +++ cmd/request.go | 50 +++ cmd/requestAll.go | 50 +++ cmd/root.go | 127 ++++---- cmd/subscribe.go | 6 +- forks/google/gnmi/modeldata/modeldata.go | 57 ---- forks/google/gnmi/server.go | 2 +- forks/google/gnmi/util.go | 2 +- go.mod | 2 - go.sum | 1 - nucleus/controller.go | 10 +- nucleus/errors.go | 9 +- nucleus/gnmi_transport.go | 37 ++- nucleus/gnmi_transport_test.go | 4 +- nucleus/http.go | 14 +- nucleus/principalNetworkDomain.go | 31 +- nucleus/util/proto/cap-resp-arista-ceos_test | 298 ++++++++++++++++++ .../util/proto/req-full-node-arista-ceos_test | Bin 0 -> 4 bytes nucleus/util/proto/req-full-node_test | 7 + .../proto/req-interfaces-arista-ceos_test | 5 + .../req-interfaces-interface-arista-ceos_test | 7 + .../util/proto/req-interfaces-wildcard_test | 12 + .../proto/resp-full-node-arista-ceos_test | Bin 0 -> 114535 bytes nucleus/util/proto/resp-full-node_test | 7 + .../proto/resp-interfaces-arista-ceos_test | 5 + ...resp-interfaces-interface-arista-ceos_test | 7 + .../util/proto/resp-interfaces-wildcard_test | 10 + .../resp-set-system-config-hostname_test | 8 + test/targets.go | 4 +- 36 files changed, 932 insertions(+), 176 deletions(-) create mode 100644 cli/http.go create mode 100644 cmd/addDevice.go create mode 100644 cmd/cli.go create mode 100644 cmd/cliSet.go create mode 100644 cmd/getDevice.go create mode 100644 cmd/getIds.go create mode 100644 cmd/request.go create mode 100644 cmd/requestAll.go delete mode 100644 forks/google/gnmi/modeldata/modeldata.go create mode 100644 nucleus/util/proto/cap-resp-arista-ceos_test create mode 100644 nucleus/util/proto/req-full-node-arista-ceos_test create mode 100644 nucleus/util/proto/req-full-node_test create mode 100644 nucleus/util/proto/req-interfaces-arista-ceos_test create mode 100644 nucleus/util/proto/req-interfaces-interface-arista-ceos_test create mode 100644 nucleus/util/proto/req-interfaces-wildcard_test create mode 100644 nucleus/util/proto/resp-full-node-arista-ceos_test create mode 100644 nucleus/util/proto/resp-full-node_test create mode 100644 nucleus/util/proto/resp-interfaces-arista-ceos_test create mode 100644 nucleus/util/proto/resp-interfaces-interface-arista-ceos_test create mode 100644 nucleus/util/proto/resp-interfaces-wildcard_test create mode 100644 nucleus/util/proto/resp-set-system-config-hostname_test diff --git a/cli/http.go b/cli/http.go new file mode 100644 index 000000000..2554574f2 --- /dev/null +++ b/cli/http.go @@ -0,0 +1,50 @@ +package cli + +import ( + "fmt" + log "github.com/sirupsen/logrus" + "io/ioutil" + "net/http" + "strings" +) + +const apiRoot = "/api?" + +var builder *strings.Builder + +func init() { + builder = &strings.Builder{} +} + +func HttpGet(apiEndpoint, f string, args ...string) error { + for _, p := range args { + builder.WriteString("&") + builder.WriteString(p) + } + resp, err := http.Get(apiEndpoint + apiRoot + "q=" + f + builder.String()) + if err != nil { + return err + } + builder.Reset() + switch resp.StatusCode { + case http.StatusOK: + defer resp.Body.Close() + bytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + fmt.Println(string(bytes)) + case http.StatusCreated: + defer resp.Body.Close() + bytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + fmt.Println(string(bytes)) + default: + log.WithFields(log.Fields{ + "status code": resp.StatusCode, + }).Error("operation unsuccessful") + } + return nil +} diff --git a/cli/target.go b/cli/target.go index c81936375..cd35e87a3 100644 --- a/cli/target.go +++ b/cli/target.go @@ -4,16 +4,13 @@ import ( "code.fbi.h-da.de/cocsn/gosdn/forks/google/gnmi" oc "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "context" - "github.com/google/gnxi/utils/credentials" pb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygot" log "github.com/sirupsen/logrus" "google.golang.org/grpc" - "google.golang.org/grpc/codes" "google.golang.org/grpc/reflection" - "google.golang.org/grpc/status" "net" "reflect" ) @@ -39,12 +36,6 @@ func newServer(model *gnmi.Model, config []byte) (*server, error) { // Get overrides the Get func of gnmi.Target to provide user auth. func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { - msg, ok := credentials.AuthorizeUser(ctx) - if !ok { - log.Infof("denied a Get request: %v", msg) - return nil, status.Error(codes.PermissionDenied, msg) - } - log.Infof("allowed a Get request: %v", msg) return s.Server.Get(ctx, req) } @@ -64,7 +55,7 @@ func (s *server) Set(ctx context.Context, req *pb.SetRequest) (*pb.SetResponse, // Target starts a gNMI target listening on the specified port. func Target(bindAddr string) error { entries := make([]*yang.Entry, 0) - for _,e := range oc.SchemaTree { + for _, e := range oc.SchemaTree { entries = append(entries, e) } diff --git a/cli/ygot.go b/cli/ygot.go index a414ff2a8..51b12852e 100644 --- a/cli/ygot.go +++ b/cli/ygot.go @@ -1,12 +1,12 @@ package cli import ( - log "github.com/golang/glog" "github.com/openconfig/ygot/util" + log "github.com/sirupsen/logrus" ) func LeafPaths() error { - for _,v := range testSchema.SchemaTree { + for _, v := range testSchema.SchemaTree { entry, err := util.FindLeafRefSchema(v, "/interface/") if err != nil { log.Error(err) @@ -14,4 +14,4 @@ func LeafPaths() error { log.Info(entry) } return nil -} \ No newline at end of file +} diff --git a/cmd/addDevice.go b/cmd/addDevice.go new file mode 100644 index 000000000..c534ec48d --- /dev/null +++ b/cmd/addDevice.go @@ -0,0 +1,56 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// addDeviceCmd represents the addDevice command +var addDeviceCmd = &cobra.Command{ + Use: "add-device", + Short: "adds a device to the controller", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet( + apiEndpoint, + "addDevice", + "address="+address, + "password="+password, + "username="+username, + ) + }, +} + +func init() { + cliCmd.AddCommand(addDeviceCmd) +} diff --git a/cmd/cli.go b/cmd/cli.go new file mode 100644 index 000000000..ebe9af89a --- /dev/null +++ b/cmd/cli.go @@ -0,0 +1,56 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "errors" + "github.com/spf13/cobra" +) + +var uuid string +var apiEndpoint string + +// cliCmd represents the cli command +var cliCmd = &cobra.Command{ + Use: "cli", + Short: "", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("no subcommand provided") + }, +} + +func init() { + rootCmd.AddCommand(cliCmd) + + cliCmd.PersistentFlags().StringVar(&uuid, "uuid", "", "uuid of the requested device") + cliCmd.PersistentFlags().StringVar(&apiEndpoint, "api-endpoint", "http://localhost:8080", "address of the target") +} diff --git a/cmd/cliSet.go b/cmd/cliSet.go new file mode 100644 index 000000000..324915c72 --- /dev/null +++ b/cmd/cliSet.go @@ -0,0 +1,57 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// cliSetCmd represents the cliSet command +var cliSetCmd = &cobra.Command{ + Use: "set", + Short: "set a value on a device", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet( + apiEndpoint, + "set", + "uuid="+uuid, + "path="+args[0], + "address="+address, + "value="+args[1], + ) + }, +} + +func init() { + cliCmd.AddCommand(cliSetCmd) +} diff --git a/cmd/getDevice.go b/cmd/getDevice.go new file mode 100644 index 000000000..ea4b7271a --- /dev/null +++ b/cmd/getDevice.go @@ -0,0 +1,50 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// getDeviceCmd represents the getDevice command +var getDeviceCmd = &cobra.Command{ + Use: "get-device", + Short: "gets device information from the controller", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet(apiEndpoint, "getDevice", "uuid="+uuid) + }, +} + +func init() { + cliCmd.AddCommand(getDeviceCmd) +} diff --git a/cmd/getIds.go b/cmd/getIds.go new file mode 100644 index 000000000..adab3025e --- /dev/null +++ b/cmd/getIds.go @@ -0,0 +1,50 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// getIdsCmd represents the getIds command +var getIdsCmd = &cobra.Command{ + Use: "get-ids", + Short: "gets device IDs from the controller", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet(apiEndpoint, "getIDs") + }, +} + +func init() { + cliCmd.AddCommand(getIdsCmd) +} diff --git a/cmd/request.go b/cmd/request.go new file mode 100644 index 000000000..9cb4a00ae --- /dev/null +++ b/cmd/request.go @@ -0,0 +1,50 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// requestCmd represents the request command +var requestCmd = &cobra.Command{ + Use: "request", + Short: "requests a path from a specified device on the controller", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet(apiEndpoint, "request", "uuid="+uuid, "path="+args[0]) + }, +} + +func init() { + cliCmd.AddCommand(requestCmd) +} diff --git a/cmd/requestAll.go b/cmd/requestAll.go new file mode 100644 index 000000000..d3196783d --- /dev/null +++ b/cmd/requestAll.go @@ -0,0 +1,50 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// requestAllCmd represents the requestAll command +var requestAllCmd = &cobra.Command{ + Use: "request-all", + Short: "requests specified path from all devices on the controller", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet(apiEndpoint, "requestAll", "path="+args[0]) + }, +} + +func init() { + cliCmd.AddCommand(requestAllCmd) +} diff --git a/cmd/root.go b/cmd/root.go index 66cdc3210..463bdcc35 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -31,16 +31,14 @@ POSSIBILITY OF SUCH DAMAGE. package cmd import ( - "code.fbi.h-da.de/cocsn/gosdn/nucleus" - "context" - "fmt" - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "os" - - "github.com/spf13/viper" -) + "code.fbi.h-da.de/cocsn/gosdn/nucleus" + "context" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "os" + "github.com/spf13/viper" +) var cfgFile string var username string @@ -49,79 +47,74 @@ var address string var loglevel string var grpcPort string - // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "gosdn", - Short: "starts the gosdn controller", - Long: `Set GOSDN_DEBUG environment variable to enalbe debug logging.`, - RunE: func(cmd *cobra.Command, args []string) error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - return nucleus.Run(ctx) - }, + Use: "gosdn", + Short: "starts the gosdn controller", + Long: `Set GOSDN_DEBUG environment variable to enalbe debug logging.`, + RunE: func(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + return nucleus.Run(ctx) + }, } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - if err := rootCmd.Execute(); err != nil { - log.WithFields(log.Fields{ - - }).Error(err) - os.Exit(1) - } + if err := rootCmd.Execute(); err != nil { + log.WithFields(log.Fields{}).Error(err) + os.Exit(1) + } } func init() { - cobra.OnInitialize(initConfig) + cobra.OnInitialize(initConfig) - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./configs/gosdn.toml)") - rootCmd.PersistentFlags().StringVarP(&username, "username", "u", "admin", "username for a gnmi resource") - rootCmd.PersistentFlags().StringVarP(&password, "password", "p", "arista", "password for a gnmi resource") - rootCmd.PersistentFlags().StringVarP(&username, "address", "a", "ceos-cocsn.apps.ocp.fbi.h-da.de:6030", "address to a gnmi resource") - rootCmd.PersistentFlags().StringVarP(&loglevel, "log-level", "l", "", "log level 'debug' or 'trace'") + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./configs/gosdn.toml)") + rootCmd.PersistentFlags().StringVarP(&username, "username", "u", "admin", "username for a gnmi resource") + rootCmd.PersistentFlags().StringVarP(&password, "password", "p", "arista", "password for a gnmi resource") + rootCmd.PersistentFlags().StringVarP(&address, "address", "a", "ceos-cocsn.apps.ocp.fbi.h-da.de:6030", "address to a gnmi resource") + rootCmd.PersistentFlags().StringVarP(&loglevel, "log-level", "l", "", "log level 'debug' or 'trace'") - rootCmd.Flags().StringVar(&grpcPort, "grpc-port", "55055", "port for gRPC NBI") + rootCmd.Flags().StringVar(&grpcPort, "grpc-port", "55055", "port for gRPC NBI") } - // initConfig reads in config file and ENV variables if set. func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) - } else { - viper.AddConfigPath("./configs") - viper.AddConfigPath("/usr/local/etc/gosdn/") - viper.SetConfigType("toml") - viper.SetConfigName("gosdn") - } - - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } - - viper.SetDefault("socket", ":" + grpcPort) - - loglevel = viper.GetString("GOSDN_LOG") - log.SetReportCaller(true) - switch loglevel { - case "trace": - log.SetLevel(log.TraceLevel) - case "debug": - log.SetLevel(log.DebugLevel) - default: - log.SetLevel(log.InfoLevel) - log.SetFormatter(&log.JSONFormatter{}) - log.SetReportCaller(false) - } + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + viper.AddConfigPath("./configs") + viper.AddConfigPath("/usr/local/etc/gosdn/") + viper.SetConfigType("toml") + viper.SetConfigName("gosdn") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + log.Debug("Using config file:", viper.ConfigFileUsed()) + } + + viper.SetDefault("socket", ":"+grpcPort) + + loglevel = viper.GetString("GOSDN_LOG") + log.SetReportCaller(true) + switch loglevel { + case "trace": + log.SetLevel(log.TraceLevel) + case "debug": + log.SetLevel(log.DebugLevel) + default: + log.SetLevel(log.InfoLevel) + log.SetFormatter(&log.JSONFormatter{}) + log.SetReportCaller(false) + } } - diff --git a/cmd/subscribe.go b/cmd/subscribe.go index 68b3cd99f..63f58fd72 100644 --- a/cmd/subscribe.go +++ b/cmd/subscribe.go @@ -42,7 +42,7 @@ var heartbeatInterval int64 var subscribeCmd = &cobra.Command{ Use: "subscribe", Short: "subscribe to target", - Long: `Starts a gNMI subscriber requersting the specified paths on the target and logs the response to stdout. + Long: `Starts a gNMI subscriber requesting the specified paths on the target and logs the response to stdout. Only 'stream' mode with 'sample' operation supported.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -53,6 +53,6 @@ var subscribeCmd = &cobra.Command{ func init() { rootCmd.AddCommand(subscribeCmd) - subscribeCmd.Flags().Int64Var(&sampleInterval, "sample-rate", 5, "Sample rate per second.") - subscribeCmd.Flags().Int64Var(&heartbeatInterval, "heartbeat-rate", 1, "Heartbeat rate per second.") + subscribeCmd.Flags().Int64Var(&sampleInterval, "sample-rate", 5, "Sample rate per second.") + subscribeCmd.Flags().Int64Var(&heartbeatInterval, "heartbeat-rate", 1, "Heartbeat rate per second.") } diff --git a/forks/google/gnmi/modeldata/modeldata.go b/forks/google/gnmi/modeldata/modeldata.go deleted file mode 100644 index dac86fba7..000000000 --- a/forks/google/gnmi/modeldata/modeldata.go +++ /dev/null @@ -1,57 +0,0 @@ -/* Copyright 2017 Google Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package modeldata contains the following model data in gnmi proto struct: -// openconfig-interfaces 2.0.0, -// openconfig-openflow 0.1.0, -// openconfig-platform 0.5.0, -// openconfig-system 0.2.0. -package modeldata - -import ( - pb "github.com/openconfig/gnmi/proto/gnmi" -) - -const ( - // OpenconfigInterfacesModel is the openconfig YANG model for interfaces. - OpenconfigInterfacesModel = "openconfig-interfaces" - // OpenconfigOpenflowModel is the openconfig YANG model for openflow. - OpenconfigOpenflowModel = "openconfig-openflow" - // OpenconfigPlatformModel is the openconfig YANG model for platform. - OpenconfigPlatformModel = "openconfig-platform" - // OpenconfigSystemModel is the openconfig YANG model for system. - OpenconfigSystemModel = "openconfig-system" -) - -var ( - // ModelData is a list of supported models. - ModelData = []*pb.ModelData{{ - Name: OpenconfigInterfacesModel, - Organization: "OpenConfig working group", - Version: "2.0.0", - }, { - Name: OpenconfigOpenflowModel, - Organization: "OpenConfig working group", - Version: "0.1.0", - }, { - Name: OpenconfigPlatformModel, - Organization: "OpenConfig working group", - Version: "0.5.0", - }, { - Name: OpenconfigSystemModel, - Organization: "OpenConfig working group", - Version: "0.2.0", - }} -) diff --git a/forks/google/gnmi/server.go b/forks/google/gnmi/server.go index 0cc56f64d..2f9416149 100644 --- a/forks/google/gnmi/server.go +++ b/forks/google/gnmi/server.go @@ -31,12 +31,12 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - log "github.com/golang/glog" "github.com/golang/protobuf/proto" "github.com/openconfig/gnmi/value" "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" + log "github.com/sirupsen/logrus" dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" pb "github.com/openconfig/gnmi/proto/gnmi" diff --git a/forks/google/gnmi/util.go b/forks/google/gnmi/util.go index 0a12c05bc..73d17b49f 100644 --- a/forks/google/gnmi/util.go +++ b/forks/google/gnmi/util.go @@ -4,8 +4,8 @@ import ( "fmt" "strconv" - log "github.com/golang/glog" "github.com/openconfig/goyang/pkg/yang" + log "github.com/sirupsen/logrus" pb "github.com/openconfig/gnmi/proto/gnmi" ) diff --git a/go.mod b/go.mod index a816066f9..faee747e2 100644 --- a/go.mod +++ b/go.mod @@ -8,11 +8,9 @@ require ( github.com/aristanetworks/goarista v0.0.0-20201120222254-94a892eb0c6a github.com/go-openapi/runtime v0.19.22 github.com/go-openapi/strfmt v0.19.5 - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/protobuf v1.4.3 github.com/google/gnxi v0.0.0-20201221102247-c26672548161 github.com/google/uuid v1.1.2 - github.com/mitchellh/go-homedir v1.1.0 github.com/neo4j/neo4j-go-driver v1.8.3 github.com/openconfig/gnmi v0.0.0-20200617225440-d2b4e6a45802 github.com/openconfig/goyang v0.2.3 diff --git a/go.sum b/go.sum index 70db040bd..dfffc0bc5 100644 --- a/go.sum +++ b/go.sum @@ -335,7 +335,6 @@ github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXd github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= diff --git a/nucleus/controller.go b/nucleus/controller.go index f7477d6ba..e239bca0d 100644 --- a/nucleus/controller.go +++ b/nucleus/controller.go @@ -20,7 +20,7 @@ type Core struct { var c *Core //Initialize does start-up housekeeping like reading controller config files -func initialize() error { +func initialize(ctx context.Context) error { c = &Core{ database: database.Database{}, pndc: pndStore{}, @@ -38,7 +38,7 @@ func initialize() error { } // TODO: Start grpc listener here - if err := httpApi(); err != nil { + if err := httpApi(ctx); err != nil { return err } @@ -62,10 +62,8 @@ func createSouthboundInterfaces() error { } func Run(ctx context.Context) error { - if err := initialize(); err != nil { - log.WithFields(log.Fields{ - - }).Error(err) + if err := initialize(ctx); err != nil { + log.WithFields(log.Fields{}).Error(err) return err } log.WithFields(log.Fields{}).Info("initialisation finished") diff --git a/nucleus/errors.go b/nucleus/errors.go index 453dccf3b..1578ecb16 100644 --- a/nucleus/errors.go +++ b/nucleus/errors.go @@ -12,6 +12,13 @@ func (e *ErrNilClient) Error() string { return fmt.Sprintf("client cannot be nil") } +type ErrNil struct { +} + +func (e *ErrNil) Error() string { + return fmt.Sprintf("struct cannot be nil") +} + type ErrNotFound struct { id interface{} } @@ -66,4 +73,4 @@ type ErrInvalidTransportOptions struct { func (e ErrInvalidTransportOptions) Error() string { return fmt.Sprintf("invalid transport options: %v", reflect.TypeOf(e.t)) -} \ No newline at end of file +} diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 8ac57497b..f496e6780 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -25,8 +25,8 @@ func NewGnmiTransport(opts *GnmiTransportOptions) (*Gnmi, error) { return nil, err } log.WithFields(log.Fields{ - "target": opts.Addr, - "tls": opts.TLS, + "target": opts.Addr, + "tls": opts.TLS, "encoding": opts.Encoding, }).Info("building new gNMI transport") return &Gnmi{ @@ -59,31 +59,35 @@ func (g *Gnmi) Get(ctx context.Context, params ...string) (interface{}, error) { // Set takes a slice of params. This slice must contain at least one operation. // It can contain an additional arbitrary amount of operations and extensions. -func (g *Gnmi) Set(ctx context.Context, params ...interface{}) (interface{}, error) { +func (g *Gnmi) Set(ctx context.Context, args ...interface{}) (interface{}, error) { if g.client == nil { return nil, &ErrNilClient{} } - if len(params) == 0 { + if len(args) == 0 { return nil, &ErrInvalidParameters{ f: "gnmi.Set()", r: "no parameters provided", } } - // Loop over params and create ops and exts - // Invalid params cause unhealable error + // Loop over args and create ops and exts + // Invalid args cause unhealable error ops := make([]*gnmi.Operation, 0) exts := make([]*gnmi_ext.Extension, 0) - for _, p := range params { + for _, p := range args { switch p.(type) { case *gnmi.Operation: - ops = append(ops, p.(*gnmi.Operation)) + op := p.(*gnmi.Operation) + if op.Target == "" { + op.Target = g.Options.Addr + } + ops = append(ops, op) case *gnmi_ext.Extension: exts = append(exts, p.(*gnmi_ext.Extension)) default: return nil, &ErrInvalidParameters{ f: "gnmi.Set()", - r: "params contain invalid type", + r: "args contain invalid type", } } } @@ -171,6 +175,9 @@ func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interf // getWithRequest takes a fully formed GetRequest, performs the Get, // and returns any response. func (g *Gnmi) getWithRequest(ctx context.Context, req *gpb.GetRequest) (interface{}, error) { + if req == nil { + return nil, &ErrNil{} + } log.WithFields(log.Fields{ "target": g.Options.Addr, "path": req.Path, @@ -190,15 +197,15 @@ func (g *Gnmi) set(ctx context.Context, setOps []*gnmi.Operation, targets := make([]string, len(setOps)) paths := make([][]string, len(setOps)) values := make([]string, len(setOps)) - for i,v := range setOps { + for i, v := range setOps { targets[i] = v.Target paths[i] = v.Path values[i] = v.Val } log.WithFields(log.Fields{ "targets": targets, - "paths": paths, - "values": values, + "paths": paths, + "values": values, }).Info("sending gNMI set request") return gnmi.Set(ctx, g.client, setOps, exts...) } @@ -209,9 +216,9 @@ func (g *Gnmi) subscribe(ctx context.Context) error { opts := ctx.Value("opts").(*gnmi.SubscribeOptions) go func() { log.WithFields(log.Fields{ - "address": opts.Target, - "paths": opts.Paths, - "mode": opts.Mode, + "address": opts.Target, + "paths": opts.Paths, + "mode": opts.Mode, "interval": opts.SampleInterval, }).Info("subscribed to gNMI target") for { diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index 246f46ecc..9bc9a4de9 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -8,11 +8,11 @@ import ( "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "context" "errors" - log "github.com/golang/glog" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/gnmi/proto/gnmi_ext" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/ytypes" + log "github.com/sirupsen/logrus" "github.com/stretchr/testify/mock" pb "google.golang.org/protobuf/proto" "os" @@ -23,6 +23,7 @@ import ( // TestMain bootstraps all tests. Humongous beast // TODO: Move somewhere more sensible func TestMain(m *testing.M) { + log.SetReportCaller(true) gnmiMessages = map[string]pb.Message{ "../test/proto/cap-resp-arista-ceos": &gpb.CapabilityResponse{}, "../test/proto/req-full-node": &gpb.GetRequest{}, @@ -640,4 +641,3 @@ func TestGnmi_set(t *testing.T) { }) } } - diff --git a/nucleus/http.go b/nucleus/http.go index 390c123ed..5d6e17763 100644 --- a/nucleus/http.go +++ b/nucleus/http.go @@ -2,6 +2,7 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" + "context" "fmt" "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" @@ -12,7 +13,7 @@ import ( const basePath = "/api" -func httpApi() (err error) { +func httpApi(ctx context.Context) (err error) { id := c.sbic.UUIDs()[0] sbi, err := c.sbic.get(id) if err != nil { @@ -63,6 +64,8 @@ func httpApi() (err error) { return } writer.WriteHeader(http.StatusCreated) + fmt.Fprintf(writer, "device added\n") + fmt.Fprintf(writer, "UUID: %v\n", d.Uuid) case "request": err = pnd.Request(id, query.Get("path")) if err != nil { @@ -108,6 +111,15 @@ func httpApi() (err error) { for i, id := range ids { fmt.Fprintf(writer, "%v: %v\n", i+1, id) } + case "set": + resp, err := pnd.(*pndImplementation).Set(id, query.Get("path"), query.Get("value")) + if err != nil { + writer.WriteHeader(http.StatusInternalServerError) + log.Error(err) + return + } + writer.WriteHeader(http.StatusOK) + fmt.Fprintln(writer, resp) default: writer.WriteHeader(http.StatusBadRequest) } diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go index e0b9fe815..23e58212f 100644 --- a/nucleus/principalNetworkDomain.go +++ b/nucleus/principalNetworkDomain.go @@ -1,6 +1,7 @@ package nucleus import ( + "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "context" log "github.com/sirupsen/logrus" @@ -151,13 +152,13 @@ func (pnd *pndImplementation) MarshalDevice(uuid uuid.UUID) (string, error) { return "", err } log.WithFields(log.Fields{ - "pnd": pnd.id, + "pnd": pnd.id, "device": uuid, }).Info("marshalled device") return string(jsonTree), nil } -//Request sends a get request to a specific device +// Request sends a get request to a specific device func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) error { d, err := pnd.getDevice(uuid) if err != nil { @@ -175,7 +176,7 @@ func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) error { return nil } -//RequestAll sends a request for all registered devices +// RequestAll sends a request for all registered devices func (pnd *pndImplementation) RequestAll(path string) error { for _, k := range pnd.devices.UUIDs() { if err := pnd.Request(k, path); err != nil { @@ -183,8 +184,30 @@ func (pnd *pndImplementation) RequestAll(path string) error { } } log.WithFields(log.Fields{ - "pnd": pnd.id, + "pnd": pnd.id, "path": path, }).Info("sent request to all devices") return nil } + +// Set sets the value to the given device path +// TODO: Design commit/confirm mechanism +func (pnd *pndImplementation) Set(uuid uuid.UUID, path string, value string) (interface{}, error) { + d, err := pnd.getDevice(uuid) + if err != nil { + return nil, err + } + ctx := context.Background() + + // TODO: Move to transport dependent func + opts := []interface{}{ + &gnmi.Operation{ + Type: "update", + Origin: "", + Target: "", + Path: gnmi.SplitPath(path), + Val: value, + }, + } + return d.Transport.Set(ctx, opts...) +} diff --git a/nucleus/util/proto/cap-resp-arista-ceos_test b/nucleus/util/proto/cap-resp-arista-ceos_test new file mode 100644 index 000000000..009569457 --- /dev/null +++ b/nucleus/util/proto/cap-resp-arista-ceos_test @@ -0,0 +1,298 @@ + +< +arista-exp-eos-vxlan$Arista Networks <http://arista.com/> +B +ietf-netconf2IETF NETCONF (Network Configuration) Working Group +< +arista-rpol-augments$Arista Networks <http://arista.com/> +C +arista-exp-eos-igmpsnooping$Arista Networks <http://arista.com/> +8 +openconfig-vlan-typesOpenConfig working group3.1.0 +? +openconfig-system-managementOpenConfig working group0.3.0 +8 +arista-eos-types$Arista Networks <http://arista.com/> +< +openconfig-openflow-typesOpenConfig working group0.1.2 +7 +openconfig-aaa-typesOpenConfig working group0.4.1 +9 +openconfig-srte-policyOpenConfig working group0.2.1 +9 +openconfig-relay-agentOpenConfig working group0.1.1 +C +openconfig-hercules-qos!OpenConfig Hercules Working Group0.1.0 +1 +openconfig-extensionsOpenConfig working group +/ +arista-mpls-deviationsArista Networks, Inc. +< +arista-vlan-augments$Arista Networks <http://arista.com/> +: +openconfig-platform-cpuOpenConfig working group0.1.1 +< +openconfig-routing-policyOpenConfig working group3.1.1 += +openconfig-isis-lsdb-typesOpenConfig working group0.4.2 +3 +openconfig-if-ipOpenConfig working group3.0.0 +; +arista-pim-augments$Arista Networks <http://arista.com/> +4 +openconfig-if-poeOpenConfig working group0.1.1 +- +arista-isis-augmentsArista Networks, Inc. +8 +openconfig-ospf-typesOpenConfig working group0.1.3 +/ +arista-intf-deviationsArista Networks, Inc. +5 +openconfig-mpls-srOpenConfig working group3.0.1 +: +openconfig-packet-matchOpenConfig working group1.1.1 +8 +openconfig-inet-typesOpenConfig working group0.3.3 +9 +openconfig-if-ethernetOpenConfig working group2.8.1 +5 +openconfig-pf-srteOpenConfig working group0.2.0 +2 +openconfig-mplsOpenConfig working group3.1.0 +# + +arista-cliArista Networks, Inc. += +openconfig-system-terminalOpenConfig working group0.3.1 +: +openconfig-platform-psuOpenConfig working group0.2.1 +8 +openconfig-yang-typesOpenConfig working group0.2.1 +8 +openconfig-lldp-typesOpenConfig working group0.1.1 +7 +openconfig-if-tunnelOpenConfig working group0.1.1 +6 +openconfig-messagesOpenConfig working group0.0.1 +B +openconfig-platform-transceiverOpenConfig working group0.7.1 +1 +openconfig-pimOpenConfig working group0.2.0 +@ +openconfig-packet-match-typesOpenConfig working group1.0.2 +C + openconfig-segment-routing-typesOpenConfig working group0.2.0 +: +openconfig-policy-typesOpenConfig working group3.1.1 +/ +arista-lldp-deviationsArista Networks, Inc. +B +openconfig-network-instance-l3OpenConfig working group0.11.1 +E +arista-exp-eos-qos-acl-config$Arista Networks <http://arista.com/> +5 +openconfig-licenseOpenConfig working group0.2.0 +: +openconfig-platform-fanOpenConfig working group0.1.1 +/ +arista-system-augmentsArista Networks, Inc. +2 +openconfig-isisOpenConfig working group0.6.0 +H +/arista-network-instance-notsupported-deviationsArista Networks, Inc. +B +)arista-interfaces-notsupported-deviationsArista Networks, Inc. +< +arista-mpls-augments$Arista Networks <http://arista.com/> +3 +arista-openflow-deviationsArista Networks, Inc. +7 +openconfig-platformOpenConfig working group0.12.2 +D +arista-exp-eos-varp-net-inst$Arista Networks <http://arista.com/> +9 +openconfig-ospf-policyOpenConfig working group0.1.3 +6 +openconfig-if-typesOpenConfig working group0.2.1 +: +arista-exp-eos-qos$Arista Networks <http://arista.com/> +2 +openconfig-igmpOpenConfig working group0.2.0 +) +arista-gnoi-certArista Networks, Inc. +/ +arista-isis-deviationsArista Networks, Inc. +4 +openconfig-systemOpenConfig working group0.9.1 +> +arista-vlan-deviations$Arista Networks <http://arista.com/> +# +vlan-translationArista Networks +; +openconfig-local-routingOpenConfig working group1.1.0 +@ +arista-exp-eos-varp-intf$Arista Networks <http://arista.com/> +; +arista-exp-eos-mlag$Arista Networks <http://arista.com/> +8 +openconfig-igmp-typesOpenConfig working group0.1.1 +1 +openconfig-aftOpenConfig working group0.4.1 +- +arista-srte-augmentsArista Networks, Inc. +E +arista-relay-agent-deviations$Arista Networks <http://arista.com/> +7 +openconfig-mpls-rsvpOpenConfig working group3.0.2 +1 +openconfig-aaaOpenConfig working group0.4.3 +6 +arista-exp-eos$Arista Networks <http://arista.com/> +H +openconfig-hercules-platform!OpenConfig Hercules Working Group0.2.0 +. +arista-acl-deviationsArista Networks, Inc. +/ +arista-lacp-deviationsArista Networks, Inc. +? +ietf-interfaces,IETF NETMOD (Network Modeling) Working Group +. +arista-bgp-deviationsArista Networks, Inc. +< +openconfig-platform-typesOpenConfig working group1.0.0 +; +"arista-acl-notsupported-deviationsArista Networks, Inc. +3 +openconfig-typesOpenConfig working group0.6.0 +M +ietf-yang-types:IETF NETMOD (NETCONF Data Modeling Language) Working Group +1 +openconfig-qosOpenConfig working group0.2.3 +. +arista-bfd-deviationsArista Networks, Inc. +@ +'arista-messages-notsupported-deviationsArista Networks, Inc. +9 +openconfig-alarm-typesOpenConfig working group0.2.1 +< +#arista-exp-eos-l2protocolforwardingArista Networks, Inc. +6 +openconfig-openflowOpenConfig working group0.1.2 +> +%arista-system-notsupported-deviationsArista Networks, Inc. +7 +openconfig-pim-typesOpenConfig working group0.1.1 +2 +openconfig-vlanOpenConfig working group3.2.0 +F +-arista-routing-policy-notsupported-deviationsArista Networks, Inc. +7 +openconfig-aft-typesOpenConfig Working Group0.3.4 +, +arista-aft-augmentsArista Networks, Inc. +< +arista-lacp-augments$Arista Networks <http://arista.com/> +1 +openconfig-bfdOpenConfig working group0.2.1 +< +openconfig-system-loggingOpenConfig working group0.3.1 +4 +openconfig-alarmsOpenConfig working group0.3.2 +8 +openconfig-isis-typesOpenConfig working group0.4.2 +? +openconfig-platform-linecardOpenConfig working group0.1.2 +< +#arista-lldp-notsupported-deviationsArista Networks, Inc. +, +arista-exp-eos-evpnArista Networks, Inc. +5 +openconfig-rib-bgpOpenConfig working group0.7.0 +@ +'arista-platform-notsupported-deviationsArista Networks, Inc. +@ +arista-exp-eos-multicast$Arista Networks <http://arista.com/> +; +"arista-bfd-notsupported-deviationsArista Networks, Inc. +? +openconfig-policy-forwardingOpenConfig working group0.2.1 +2 +openconfig-lacpOpenConfig working group1.1.1 +- +arista-lldp-augmentsArista Networks, Inc. +; +arista-bfd-augments$Arista Networks <http://arista.com/> +1 +openconfig-bgpOpenConfig working group6.0.0 + +iana-if-typeIANA +/ +arista-rpol-deviationsArista Networks, Inc. +; +openconfig-rib-bgp-typesOpenConfig working group0.5.0 +M +ietf-inet-types:IETF NETMOD (NETCONF Data Modeling Language) Working Group +8 +openconfig-bgp-policyOpenConfig working group6.0.1 +< +arista-intf-augments$Arista Networks <http://arista.com/> +8 +arista-local-routing-deviationsArista Networks, Inc. +8 +openconfig-interfacesOpenConfig working group2.4.3 +: +openconfig-if-aggregateOpenConfig working group2.4.3 +/ +arista-srte-deviationsArista Networks, Inc. +A +arista-exp-eos-qos-config$Arista Networks <http://arista.com/> +2 +openconfig-lldpOpenConfig working group0.2.1 +J +openconfig-hercules-interfaces!OpenConfig Hercules Working Group0.2.0 +6 +openconfig-mpls-ldpOpenConfig working group3.0.2 +8 +openconfig-mpls-typesOpenConfig working group3.2.0 +M +ietf-netconf-monitoring2IETF NETCONF (Network Configuration) Working Group +7 +openconfig-bgp-typesOpenConfig working group5.2.0 +< +#arista-lacp-notsupported-deviationsArista Networks, Inc. +E +,arista-local-routing-notsupported-deviationsArista Networks, Inc. +, +arista-bgp-augmentsArista Networks, Inc. +2 +arista-netinst-deviationsArista Networks, Inc. +; +"arista-bgp-notsupported-deviationsArista Networks, Inc. +D +!openconfig-network-instance-typesOpenConfig working group0.8.2 +1 +openconfig-aclOpenConfig working group1.1.1 +7 +openconfig-qos-typesOpenConfig working group0.2.1 +5 +openconfig-procmonOpenConfig working group0.4.0 +, +arista-qos-augmentsArista Networks, Inc. +; +openconfig-platform-portOpenConfig working group0.3.3 +4 +openconfig-ospfv2OpenConfig working group0.2.2 +1 +arista-system-deviationsArista Networks, Inc. +> +openconfig-transport-typesOpenConfig working group0.11.0 +? +openconfig-network-instanceOpenConfig working group0.14.0 ++ +arista-rpc-netconfArista Networks, Inc. += +openconfig-segment-routingOpenConfig working group0.3.0 +; +"arista-qos-notsupported-deviationsArista Networks, Inc. +C +arista-exp-eos-vxlan-config$Arista Networks <http://arista.com/>�0.7.0 \ No newline at end of file diff --git a/nucleus/util/proto/req-full-node-arista-ceos_test b/nucleus/util/proto/req-full-node-arista-ceos_test new file mode 100644 index 0000000000000000000000000000000000000000..6223295e2984b8002ea5573e8fbbecb77ba9dc6d GIT binary patch literal 4 LcmWe)&|m=o0H6Rr literal 0 HcmV?d00001 diff --git a/nucleus/util/proto/req-full-node_test b/nucleus/util/proto/req-full-node_test new file mode 100644 index 000000000..087f7d827 --- /dev/null +++ b/nucleus/util/proto/req-full-node_test @@ -0,0 +1,7 @@ +2 + +interfaces + interface + +interfaces + interface( \ No newline at end of file diff --git a/nucleus/util/proto/req-interfaces-arista-ceos_test b/nucleus/util/proto/req-interfaces-arista-ceos_test new file mode 100644 index 000000000..e444e33aa --- /dev/null +++ b/nucleus/util/proto/req-interfaces-arista-ceos_test @@ -0,0 +1,5 @@ + + +interfaces + +interfaces( \ No newline at end of file diff --git a/nucleus/util/proto/req-interfaces-interface-arista-ceos_test b/nucleus/util/proto/req-interfaces-interface-arista-ceos_test new file mode 100644 index 000000000..087f7d827 --- /dev/null +++ b/nucleus/util/proto/req-interfaces-interface-arista-ceos_test @@ -0,0 +1,7 @@ +2 + +interfaces + interface + +interfaces + interface( \ No newline at end of file diff --git a/nucleus/util/proto/req-interfaces-wildcard_test b/nucleus/util/proto/req-interfaces-wildcard_test new file mode 100644 index 000000000..bd113697d --- /dev/null +++ b/nucleus/util/proto/req-interfaces-wildcard_test @@ -0,0 +1,12 @@ +c + +interfaces +interface[name=*] +state +name + +interfaces + interface +name* +state +name( \ No newline at end of file diff --git a/nucleus/util/proto/resp-full-node-arista-ceos_test b/nucleus/util/proto/resp-full-node-arista-ceos_test new file mode 100644 index 0000000000000000000000000000000000000000..9bcd16e666a683a570c5141eb01b4ed16ad52b8b GIT binary patch literal 114535 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 UGxCc|paBHl%mCsm)z;Pm0A2p7qW}N^ literal 0 HcmV?d00001 diff --git a/nucleus/util/proto/resp-full-node_test b/nucleus/util/proto/resp-full-node_test new file mode 100644 index 000000000..4614be84e --- /dev/null +++ b/nucleus/util/proto/resp-full-node_test @@ -0,0 +1,7 @@ + +�""�" +0 + +interfaces + interface +nameEthernet510�"Z�"{"openconfig-interfaces:config":{"description":"","enabled":true,"arista-intf-augments:load-interval":300,"loopback-mode":false,"mtu":0,"name":"Ethernet510","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-if-ethernet:ethernet":{"config":{"arista-intf-augments:fec-encoding":{"disabled":false,"fire-code":false,"reed-solomon":false,"reed-solomon544":false},"openconfig-hercules-interfaces:forwarding-viable":true,"mac-address":"00:00:00:00:00:00","port-speed":"SPEED_UNKNOWN","arista-intf-augments:sfp-1000base-t":false},"arista-intf-augments:pfc":{"priorities":{"priority":[{"index":0,"state":{"in-frames":"0","index":0,"out-frames":"0"}},{"index":1,"state":{"in-frames":"0","index":1,"out-frames":"0"}},{"index":2,"state":{"in-frames":"0","index":2,"out-frames":"0"}},{"index":3,"state":{"in-frames":"0","index":3,"out-frames":"0"}},{"index":4,"state":{"in-frames":"0","index":4,"out-frames":"0"}},{"index":5,"state":{"in-frames":"0","index":5,"out-frames":"0"}},{"index":6,"state":{"in-frames":"0","index":6,"out-frames":"0"}},{"index":7,"state":{"in-frames":"0","index":7,"out-frames":"0"}}]}},"state":{"auto-negotiate":false,"counters":{"in-crc-errors":"0","in-fragment-frames":"0","in-jabber-frames":"0","in-mac-control-frames":"0","in-mac-pause-frames":"0","in-oversize-frames":"0","out-mac-control-frames":"0","out-mac-pause-frames":"0"},"duplex-mode":"FULL","enable-flow-control":false,"openconfig-hercules-interfaces:forwarding-viable":true,"hw-mac-address":"02:42:c0:a8:02:41","mac-address":"02:42:c0:a8:02:41","negotiated-port-speed":"SPEED_UNKNOWN","port-speed":"SPEED_UNKNOWN","arista-intf-augments:supported-speeds":["SPEED_5GB","SPEED_25GB","SPEED_50GB","SPEED_100GB","SPEED_10MB","SPEED_100GB_2LANE","SPEED_100MB","SPEED_1GB","SPEED_2500MB","SPEED_400GB","SPEED_10GB","SPEED_40GB","SPEED_200GB_4LANE","SPEED_200GB_8LANE","SPEED_50GB_1LANE"]}},"openconfig-interfaces:hold-time":{"config":{"down":0,"up":0},"state":{"down":0,"up":0}},"openconfig-interfaces:name":"Ethernet510","openconfig-interfaces:state":{"admin-status":"UP","counters":{"in-broadcast-pkts":"294224","in-discards":"0","in-errors":"0","in-fcs-errors":"0","in-multicast-pkts":"1412","in-octets":"72226989","in-unicast-pkts":"642","out-broadcast-pkts":"0","out-discards":"0","out-errors":"0","out-multicast-pkts":"0","out-octets":"0","out-unicast-pkts":"0"},"description":"","enabled":true,"openconfig-platform-port:hardware-port":"Port510","ifindex":510,"arista-intf-augments:inactive":false,"last-change":"1612959137249521152","loopback-mode":false,"mtu":0,"name":"Ethernet510","oper-status":"UP","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-interfaces:subinterfaces":{"subinterface":[{"config":{"description":"","enabled":true,"index":0},"index":0,"openconfig-if-ip:ipv4":{"config":{"dhcp-client":false,"enabled":true,"mtu":1500},"state":{"dhcp-client":false,"enabled":true,"mtu":1500},"unnumbered":{"config":{"enabled":false},"state":{"enabled":false}}},"openconfig-if-ip:ipv6":{"addresses":{"address":[{"config":{"ip":"fdfd::ce05","prefix-length":64},"ip":"fdfd::ce05","state":{"ip":"fdfd::ce05","origin":"STATIC","prefix-length":64,"status":"PREFERRED"}}]},"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"neighbors":{"neighbor":[{"config":{"ip":"fdfd::1"},"ip":"fdfd::1","state":{"ip":"fdfd::1","link-layer-address":"74:83:c2:fe:86:ad","neighbor-state":"REACHABLE","origin":"DYNAMIC"}},{"config":{"ip":"fe80::7683:c2ff:fefe:86ad"},"ip":"fe80::7683:c2ff:fefe:86ad","state":{"ip":"fe80::7683:c2ff:fefe:86ad","link-layer-address":"74:83:c2:fe:86:ad","neighbor-state":"REACHABLE","origin":"DYNAMIC"}},{"config":{"ip":"fe80::c3:43ff:fec5:da0b"},"ip":"fe80::c3:43ff:fec5:da0b","state":{"ip":"fe80::c3:43ff:fec5:da0b","link-layer-address":"02:c3:43:c5:da:0b","neighbor-state":"REACHABLE","origin":"DYNAMIC"}},{"config":{"ip":"fdfd::28"},"ip":"fdfd::28","state":{"ip":"fdfd::28","link-layer-address":"02:c3:43:c5:da:0b","neighbor-state":"REACHABLE","origin":"DYNAMIC"}},{"config":{"ip":"fe80::1"},"ip":"fe80::1","state":{"ip":"fe80::1","link-layer-address":"74:83:c2:fe:86:ad","neighbor-state":"REACHABLE","origin":"DYNAMIC"}}]},"state":{"dhcp-client":false,"enabled":false,"mtu":1500}},"state":{"counters":{"in-fcs-errors":"0"},"description":"","enabled":true,"index":0}}]}} \ No newline at end of file diff --git a/nucleus/util/proto/resp-interfaces-arista-ceos_test b/nucleus/util/proto/resp-interfaces-arista-ceos_test new file mode 100644 index 000000000..58e139172 --- /dev/null +++ b/nucleus/util/proto/resp-interfaces-arista-ceos_test @@ -0,0 +1,5 @@ + +�"� + + +interfaces�Z�{"openconfig-interfaces:interface":[{"config":{"description":"","enabled":true,"arista-intf-augments:load-interval":300,"loopback-mode":false,"mtu":0,"name":"Ethernet510","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-if-ethernet:ethernet":{"config":{"arista-intf-augments:fec-encoding":{"disabled":false,"fire-code":false,"reed-solomon":false,"reed-solomon544":false},"openconfig-hercules-interfaces:forwarding-viable":true,"mac-address":"00:00:00:00:00:00","port-speed":"SPEED_UNKNOWN","arista-intf-augments:sfp-1000base-t":false},"arista-intf-augments:pfc":{"priorities":{"priority":[{"index":0,"state":{"in-frames":"0","index":0,"out-frames":"0"}},{"index":1,"state":{"in-frames":"0","index":1,"out-frames":"0"}},{"index":2,"state":{"in-frames":"0","index":2,"out-frames":"0"}},{"index":3,"state":{"in-frames":"0","index":3,"out-frames":"0"}},{"index":4,"state":{"in-frames":"0","index":4,"out-frames":"0"}},{"index":5,"state":{"in-frames":"0","index":5,"out-frames":"0"}},{"index":6,"state":{"in-frames":"0","index":6,"out-frames":"0"}},{"index":7,"state":{"in-frames":"0","index":7,"out-frames":"0"}}]}},"state":{"auto-negotiate":false,"counters":{"in-crc-errors":"0","in-fragment-frames":"0","in-jabber-frames":"0","in-mac-control-frames":"0","in-mac-pause-frames":"0","in-oversize-frames":"0","out-mac-control-frames":"0","out-mac-pause-frames":"0"},"duplex-mode":"FULL","enable-flow-control":false,"openconfig-hercules-interfaces:forwarding-viable":true,"hw-mac-address":"02:42:c0:a8:02:42","mac-address":"02:42:c0:a8:02:42","negotiated-port-speed":"SPEED_UNKNOWN","port-speed":"SPEED_UNKNOWN","arista-intf-augments:supported-speeds":["SPEED_200GB_8LANE","SPEED_100MB","SPEED_1GB","SPEED_10GB","SPEED_400GB","SPEED_40GB","SPEED_2500MB","SPEED_50GB","SPEED_50GB_1LANE","SPEED_25GB","SPEED_100GB","SPEED_100GB_2LANE","SPEED_10MB","SPEED_200GB_4LANE","SPEED_5GB"]}},"hold-time":{"config":{"down":0,"up":0},"state":{"down":0,"up":0}},"name":"Ethernet510","state":{"admin-status":"UP","counters":{"in-broadcast-pkts":"344691","in-discards":"0","in-errors":"0","in-fcs-errors":"0","in-multicast-pkts":"1","in-octets":"93260151","in-unicast-pkts":"0","out-broadcast-pkts":"0","out-discards":"0","out-errors":"0","out-multicast-pkts":"0","out-octets":"0","out-unicast-pkts":"0"},"description":"","enabled":true,"openconfig-platform-port:hardware-port":"Port510","ifindex":510,"arista-intf-augments:inactive":false,"last-change":"1614091948142304000","loopback-mode":false,"mtu":0,"name":"Ethernet510","oper-status":"UP","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"subinterfaces":{"subinterface":[{"config":{"description":"","enabled":true,"index":0},"index":0,"openconfig-if-ip:ipv4":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500},"unnumbered":{"config":{"enabled":false},"state":{"enabled":false}}},"openconfig-if-ip:ipv6":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500}},"state":{"counters":{"in-fcs-errors":"0"},"description":"","enabled":true,"index":0}}]}}]} \ No newline at end of file diff --git a/nucleus/util/proto/resp-interfaces-interface-arista-ceos_test b/nucleus/util/proto/resp-interfaces-interface-arista-ceos_test new file mode 100644 index 000000000..05f0804b1 --- /dev/null +++ b/nucleus/util/proto/resp-interfaces-interface-arista-ceos_test @@ -0,0 +1,7 @@ + +�"� +0 + +interfaces + interface +nameEthernet510�Z�{"openconfig-interfaces:config":{"description":"","enabled":true,"arista-intf-augments:load-interval":300,"loopback-mode":false,"mtu":0,"name":"Ethernet510","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-if-ethernet:ethernet":{"config":{"arista-intf-augments:fec-encoding":{"disabled":false,"fire-code":false,"reed-solomon":false,"reed-solomon544":false},"openconfig-hercules-interfaces:forwarding-viable":true,"mac-address":"00:00:00:00:00:00","port-speed":"SPEED_UNKNOWN","arista-intf-augments:sfp-1000base-t":false},"arista-intf-augments:pfc":{"priorities":{"priority":[{"index":0,"state":{"in-frames":"0","index":0,"out-frames":"0"}},{"index":1,"state":{"in-frames":"0","index":1,"out-frames":"0"}},{"index":2,"state":{"in-frames":"0","index":2,"out-frames":"0"}},{"index":3,"state":{"in-frames":"0","index":3,"out-frames":"0"}},{"index":4,"state":{"in-frames":"0","index":4,"out-frames":"0"}},{"index":5,"state":{"in-frames":"0","index":5,"out-frames":"0"}},{"index":6,"state":{"in-frames":"0","index":6,"out-frames":"0"}},{"index":7,"state":{"in-frames":"0","index":7,"out-frames":"0"}}]}},"state":{"auto-negotiate":false,"counters":{"in-crc-errors":"0","in-fragment-frames":"0","in-jabber-frames":"0","in-mac-control-frames":"0","in-mac-pause-frames":"0","in-oversize-frames":"0","out-mac-control-frames":"0","out-mac-pause-frames":"0"},"duplex-mode":"FULL","enable-flow-control":false,"openconfig-hercules-interfaces:forwarding-viable":true,"hw-mac-address":"02:42:c0:a8:02:42","mac-address":"02:42:c0:a8:02:42","negotiated-port-speed":"SPEED_UNKNOWN","port-speed":"SPEED_UNKNOWN","arista-intf-augments:supported-speeds":["SPEED_200GB_8LANE","SPEED_100MB","SPEED_1GB","SPEED_10GB","SPEED_400GB","SPEED_40GB","SPEED_2500MB","SPEED_50GB","SPEED_50GB_1LANE","SPEED_25GB","SPEED_100GB","SPEED_100GB_2LANE","SPEED_10MB","SPEED_200GB_4LANE","SPEED_5GB"]}},"openconfig-interfaces:hold-time":{"config":{"down":0,"up":0},"state":{"down":0,"up":0}},"openconfig-interfaces:name":"Ethernet510","openconfig-interfaces:state":{"admin-status":"UP","counters":{"in-broadcast-pkts":"344691","in-discards":"0","in-errors":"0","in-fcs-errors":"0","in-multicast-pkts":"1","in-octets":"93260151","in-unicast-pkts":"0","out-broadcast-pkts":"0","out-discards":"0","out-errors":"0","out-multicast-pkts":"0","out-octets":"0","out-unicast-pkts":"0"},"description":"","enabled":true,"openconfig-platform-port:hardware-port":"Port510","ifindex":510,"arista-intf-augments:inactive":false,"last-change":"1614091948142304000","loopback-mode":false,"mtu":0,"name":"Ethernet510","oper-status":"UP","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-interfaces:subinterfaces":{"subinterface":[{"config":{"description":"","enabled":true,"index":0},"index":0,"openconfig-if-ip:ipv4":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500},"unnumbered":{"config":{"enabled":false},"state":{"enabled":false}}},"openconfig-if-ip:ipv6":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500}},"state":{"counters":{"in-fcs-errors":"0"},"description":"","enabled":true,"index":0}}]}} \ No newline at end of file diff --git a/nucleus/util/proto/resp-interfaces-wildcard_test b/nucleus/util/proto/resp-interfaces-wildcard_test new file mode 100644 index 000000000..50cb9f4c7 --- /dev/null +++ b/nucleus/util/proto/resp-interfaces-wildcard_test @@ -0,0 +1,10 @@ + +T"R +A + +interfaces + interface +nameEthernet510 +state +name +Ethernet510 \ No newline at end of file diff --git a/nucleus/util/proto/resp-set-system-config-hostname_test b/nucleus/util/proto/resp-set-system-config-hostname_test new file mode 100644 index 000000000..c656ee5bb --- /dev/null +++ b/nucleus/util/proto/resp-set-system-config-hostname_test @@ -0,0 +1,8 @@ +>: +system +config +hostname +system +config + +hostname �����ȶ \ No newline at end of file diff --git a/test/targets.go b/test/targets.go index ec405e446..309b81847 100644 --- a/test/targets.go +++ b/test/targets.go @@ -64,13 +64,13 @@ func GnmiTarget(stop chan bool, bindAddr string) error { } entries := make([]*yang.Entry, 0) - for _,e := range oc.SchemaTree { + for _, e := range oc.SchemaTree { entries = append(entries, e) } modelData, err := util.FindModelData(entries) if err != nil { - return err + log.Error(err) } // Google stuff from here -- GitLab