diff --git a/build/ci/.test.yml b/build/ci/.test.yml index 225356ea6656130a0737e4b8b4a7b3ee39bcd5b2..dd4f07a0a70490c2ce539a91081ad3c196d73f3e 100644 --- a/build/ci/.test.yml +++ b/build/ci/.test.yml @@ -27,22 +27,23 @@ integration-test: allow_failure: true variables: GOSDN_LOG: "nolog" + GOSDN_CHANGE_TIMEOUT: "100ms" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH - if: $CI_NIGHTLY - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH allow_failure: true - after_script: - - go tool cover -func=coverage.out unit-test: script: - go test -short -race $(go list ./... | grep -v /forks/ | grep -v /api/ | grep -v /mocks ) -v -coverprofile=coverage.out + after_script: + - go tool cover -func=coverage.out <<: *test -http-api-test: +controller-test: script: - cd ./nucleus - - go test -race -v -run Test_httpApi -coverprofile=coverage.out + - go test -race -v -run TestRun <<: *test \ No newline at end of file diff --git a/cli/http.go b/cli/http.go index 746c4a45146a882d827ef4b58526a5e06e4ef9a3..b7c3254b655bda3c315dd79039deb5f730fc023f 100644 --- a/cli/http.go +++ b/cli/http.go @@ -3,11 +3,12 @@ package cli import ( "errors" "fmt" - log "github.com/sirupsen/logrus" - "github.com/spf13/viper" "io/ioutil" "net/http" "strings" + + log "github.com/sirupsen/logrus" + "github.com/spf13/viper" ) const apiRoot = "?" @@ -26,6 +27,7 @@ func HTTPGet(apiEndpoint, f string, args ...string) error { } resp, err := http.Get(apiEndpoint + apiRoot + "q=" + f + builder.String()) if err != nil { + log.Info(fmt.Sprintf("Err: %s", err)) return err } builder.Reset() @@ -38,8 +40,8 @@ func HTTPGet(apiEndpoint, f string, args ...string) error { } switch f { case "init": - pnd := string(bytes[:36]) - sbi := string(bytes[36:]) + pnd := string(bytes[9:45]) + sbi := string(bytes[55:91]) viper.Set("CLI_PND", pnd) viper.Set("CLI_SBI", sbi) err := viper.WriteConfig() @@ -49,6 +51,7 @@ func HTTPGet(apiEndpoint, f string, args ...string) error { default: fmt.Println(string(bytes)) } + case http.StatusCreated: defer resp.Body.Close() bytes, err := ioutil.ReadAll(resp.Body) @@ -58,6 +61,8 @@ func HTTPGet(apiEndpoint, f string, args ...string) error { uuid := string(bytes[19:55]) viper.Set("LAST_DEVICE_UUID", uuid) fmt.Println(string(bytes)) + + case http.StatusAccepted: default: log.WithFields(log.Fields{ "status code": resp.StatusCode, diff --git a/cli/set.go b/cli/set.go index 4d01d1477fd281446c3666faaf69e9a9899f7347..c5499d6f3a085db76d03fcef3227645662e829fc 100644 --- a/cli/set.go +++ b/cli/set.go @@ -3,10 +3,7 @@ package cli 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/util/proto" "context" - pb "google.golang.org/protobuf/proto" - "os" ) // Set sends a gNMI Set request to the specified target. Only one @@ -23,28 +20,5 @@ func Set(a, u, p, typ string, args ...string) error { if err != nil { return err } - - path := gnmi.SplitPath(args[0]) - req := []interface{}{ - &gnmi.Operation{ - Type: typ, - Origin: "", - Target: "", - Path: path, - Val: args[1], - }, - } - - resp, err := t.Set(context.Background(), req...) - if err != nil { - return err - } - - _, tap := os.LookupEnv("GOSDN_TAP") - if tap { - if err := proto.Write(resp.(pb.Message), "resp-set-system-config-hostname"); err != nil { - return err - } - } - return nil + return t.Set(context.Background(), args) } diff --git a/cmd/change.go b/cmd/change.go new file mode 100644 index 0000000000000000000000000000000000000000..fc10bdb4fb746317c2450b4548cfae680115769f --- /dev/null +++ b/cmd/change.go @@ -0,0 +1,49 @@ +/* +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 ( + "github.com/spf13/cobra" +) + +// changeCmd represents the change command +var changeCmd = &cobra.Command{ + Use: "change", + Short: "manage changes of the specified pnd", + Long: `use "change list" or "change list-pending" to list changes + +use "change commit" or "change confirm" respectively`, +} + +func init() { + cliCmd.AddCommand(changeCmd) +} diff --git a/cmd/cliSet.go b/cmd/cliSet.go index 9a79c630bed30d80c0d8438224edc42f084ff14d..e3f3a0743fdea2fa9c5c2ebd5686ef2e6675dc6b 100644 --- a/cmd/cliSet.go +++ b/cmd/cliSet.go @@ -41,12 +41,14 @@ var cliSetCmd = &cobra.Command{ Use: "set", Args: cobra.ExactArgs(2), Short: "set a value on a device", - Long: `Set a path value for a given device. Only one path and -only one value supported for now`, + Long: `Update a path value for a given device. Only one path and +only one value supported for now. + +Use "set replace" or "set delete" respectively`, RunE: func(cmd *cobra.Command, args []string) error { return cli.HTTPGet( apiEndpoint, - "set", + "update", "uuid="+uuid, "cliSbi="+cliSbi, "cliPnd="+cliPnd, @@ -60,5 +62,5 @@ only one value supported for now`, func init() { cliCmd.AddCommand(cliSetCmd) - cliSetCmd.Flags().StringVar(&uuid, "uuid", "", "uuid of the requested device") + cliSetCmd.PersistentFlags().StringVar(&uuid, "uuid", "", "uuid of the target device") } diff --git a/cmd/commit.go b/cmd/commit.go new file mode 100644 index 0000000000000000000000000000000000000000..a6ccb3b109b3c7f6afc4d7303c7c8ca88b196989 --- /dev/null +++ b/cmd/commit.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" +) + +// commitCmd represents the commit command +var commitCmd = &cobra.Command{ + Use: "commit", + Args: cobra.ExactArgs(1), + Short: "Commit the given change for the active PND", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "change-commit", + "pnd="+cliPnd, + "cuid="+args[0], + ) + }, +} + +func init() { + changeCmd.AddCommand(commitCmd) +} diff --git a/cmd/confirm.go b/cmd/confirm.go new file mode 100644 index 0000000000000000000000000000000000000000..59cda76985aac03a9149da41a4e08f9545d0ac77 --- /dev/null +++ b/cmd/confirm.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" +) + +// confirmCmd represents the confirm command +var confirmCmd = &cobra.Command{ + Use: "confirm", + Args: cobra.ExactArgs(1), + Short: "Confirms the given change for the active PND", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "change-confirm", + "pnd="+cliPnd, + "cuid="+args[0], + ) + }, +} + +func init() { + changeCmd.AddCommand(confirmCmd) +} diff --git a/cmd/delete.go b/cmd/delete.go new file mode 100644 index 0000000000000000000000000000000000000000..3c4e4e7603c32be00c4bba428bd22b09cd735eea --- /dev/null +++ b/cmd/delete.go @@ -0,0 +1,71 @@ +/* +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" +) + +// deleteCmd represents the delete command +var deleteCmd = &cobra.Command{ + Use: "delete", + Args: cobra.ExactArgs(1), + Short: "set a value on a device", + Long: `Set a path value for a given device. Only one path and +only one value supported for now`, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "delete", + "uuid="+uuid, + "cliSbi="+cliSbi, + "cliPnd="+cliPnd, + "path="+args[0], + "address="+address, + ) + }, +} + +func init() { + cliSetCmd.AddCommand(deleteCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // deleteCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 0000000000000000000000000000000000000000..198662268afafa640e5c73749c0f64899874188b --- /dev/null +++ b/cmd/list.go @@ -0,0 +1,55 @@ +/* +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" +) + +// listCmd represents the list command +var listCmd = &cobra.Command{ + Use: "list", + Short: "Lists all committed changes", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "change-list", + "pnd="+cliPnd, + ) + }, +} + +func init() { + changeCmd.AddCommand(listCmd) +} diff --git a/cmd/listPending.go b/cmd/listPending.go new file mode 100644 index 0000000000000000000000000000000000000000..c7893d97bfe5e891c0ca90c9df4b44856b39f045 --- /dev/null +++ b/cmd/listPending.go @@ -0,0 +1,55 @@ +/* +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" +) + +// listPendingCmd represents the listPending command +var listPendingCmd = &cobra.Command{ + Use: "listPending", + Short: "Lists all committed changes", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "change-list-pending", + "pnd="+cliPnd, + ) + }, +} + +func init() { + changeCmd.AddCommand(listPendingCmd) +} diff --git a/cmd/replace.go b/cmd/replace.go new file mode 100644 index 0000000000000000000000000000000000000000..33529c6d0e647aa9494b0dec9eee4ea2b2253818 --- /dev/null +++ b/cmd/replace.go @@ -0,0 +1,62 @@ +/* +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" +) + +// replaceCmd represents the replace command +var replaceCmd = &cobra.Command{ + Use: "replace", + Args: cobra.ExactArgs(2), + Short: "set a value on a device", + Long: `Set a path value for a given device. Only one path and +only one value supported for now`, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "replace", + "uuid="+uuid, + "cliSbi="+cliSbi, + "cliPnd="+cliPnd, + "path="+args[0], + "address="+address, + "value="+args[1], + ) + }, +} + +func init() { + cliSetCmd.AddCommand(replaceCmd) +} diff --git a/cmd/update.go b/cmd/update.go new file mode 100644 index 0000000000000000000000000000000000000000..93db8e374adf4553675bd14db98c29b09ee88052 --- /dev/null +++ b/cmd/update.go @@ -0,0 +1,62 @@ +/* +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" +) + +// updateCmd represents the update command +var updateCmd = &cobra.Command{ + Use: "update", + Args: cobra.ExactArgs(2), + Short: "update a value on a device", + Long: `Update a path value for a given device. Only one path and +only one value supported for now`, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HTTPGet( + apiEndpoint, + "update", + "uuid="+uuid, + "cliSbi="+cliSbi, + "cliPnd="+cliPnd, + "path="+args[0], + "address="+address, + "value="+args[1], + ) + }, +} + +func init() { + cliSetCmd.AddCommand(updateCmd) +} diff --git a/forks/goarista/gnmi/client.go b/forks/goarista/gnmi/client.go index fc041336eb078e224f739bbad9f93dd2767c10fb..c4512f2c8f6a3ef34b405ccfe81a33fcfcd57f72 100644 --- a/forks/goarista/gnmi/client.go +++ b/forks/goarista/gnmi/client.go @@ -118,7 +118,7 @@ func DialContext(ctx context.Context, cfg *Config) (pb.GNMIClient, error) { if cfg.TLS || cfg.CAFile != "" || cfg.CertFile != "" || cfg.Token != "" { tlsConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, + MinVersion: tls.VersionTLS12, } if cfg.CAFile != "" { b, err := ioutil.ReadFile(cfg.CAFile) diff --git a/go.mod b/go.mod index ac1e4cbb4135509efb5ecdec1fee89a8acb384bb..8e06b5944cab2bd7ffa365c59d78b8295043bc1f 100644 --- a/go.mod +++ b/go.mod @@ -9,17 +9,17 @@ require ( github.com/google/gnxi v0.0.0-20201221102247-c26672548161 github.com/google/uuid v1.1.2 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 - github.com/openconfig/ygot v0.10.0 - github.com/sirupsen/logrus v1.7.0 - github.com/spf13/cobra v1.1.1 + github.com/openconfig/gnmi v0.0.0-20210428141518-ae4d850000ab + github.com/openconfig/goyang v0.2.4 + github.com/openconfig/ygot v0.10.5 + github.com/sirupsen/logrus v1.8.1 + github.com/spf13/cobra v1.1.3 github.com/spf13/viper v1.7.1 - github.com/stretchr/testify v1.6.1 - golang.org/x/net v0.0.0-20201216054612-986b41b23924 - google.golang.org/grpc v1.34.0 + github.com/stretchr/testify v1.7.0 + golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 + google.golang.org/grpc v1.37.0 google.golang.org/protobuf v1.26.0 - k8s.io/api v0.20.5 - k8s.io/apimachinery v0.20.5 - k8s.io/client-go v0.20.5 + k8s.io/api v0.21.0 + k8s.io/apimachinery v0.21.0 + k8s.io/client-go v0.21.0 ) diff --git a/go.sum b/go.sum index ff5b2f4c4e4729afb273a8e33337041676f2c38d..611ef282280814b75535b07660de63a3df5b77fa 100644 --- a/go.sum +++ b/go.sum @@ -25,11 +25,9 @@ code.fbi.h-da.de/cocsn/yang-models v0.0.7 h1:3TOo8J+EdAJKeq4o3aaNWZRhjSwguIS8wci code.fbi.h-da.de/cocsn/yang-models v0.0.7/go.mod h1:M+2HinfhTT8nA8qvn2cpWNlOtuiizTNDWA3yfy72K/g= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= @@ -72,6 +70,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -88,7 +87,6 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= @@ -102,6 +100,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -112,7 +111,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -122,8 +120,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= @@ -134,8 +132,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -189,6 +187,7 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/protobuf v3.11.4+incompatible/go.mod h1:lUQ9D1ePzbH2PrIS7ob/bjm9HXyH5WHB0Akwh7URreM= +github.com/google/protobuf v3.14.0+incompatible/go.mod h1:lUQ9D1ePzbH2PrIS7ob/bjm9HXyH5WHB0Akwh7URreM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= @@ -248,7 +247,7 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -293,6 +292,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/moby v1.13.1/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -322,17 +322,20 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= -github.com/openconfig/gnmi v0.0.0-20200617225440-d2b4e6a45802 h1:WXFwJlWOJINlwlyAZuNo4GdYZS6qPX36+rRUncLmN8Q= github.com/openconfig/gnmi v0.0.0-20200617225440-d2b4e6a45802/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= +github.com/openconfig/gnmi v0.0.0-20210428141518-ae4d850000ab h1:iLOhSO6bYXR+Ja9pa0HdX8ZSoAcY0EtRu1vfkLFY6+w= +github.com/openconfig/gnmi v0.0.0-20210428141518-ae4d850000ab/go.mod h1:H/20NXlnWbCPFC593nxpiKJ+OU//7mW7s7Qk7uVdg3Q= github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea/go.mod h1:dhXaV0JgHJzdrHi2l+w0fZrwArtXL7jEFoiqLEdmkvU= github.com/openconfig/goyang v0.2.2/go.mod h1:vX61x01Q46AzbZUzG617vWqh/cB+aisc+RrNkXRd3W8= -github.com/openconfig/goyang v0.2.3 h1:pYxQ+VG6KNS3N5zkQeLmIBtc3gRs6JHZOKMD2/knlv4= github.com/openconfig/goyang v0.2.3/go.mod h1:vX61x01Q46AzbZUzG617vWqh/cB+aisc+RrNkXRd3W8= +github.com/openconfig/goyang v0.2.4 h1:xGmGr3zuhq9ASCu5jRdtMFdRnixhbg8TJEQ0nylyvxA= +github.com/openconfig/goyang v0.2.4/go.mod h1:vX61x01Q46AzbZUzG617vWqh/cB+aisc+RrNkXRd3W8= github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw= github.com/openconfig/ygot v0.6.0/go.mod h1:o30svNf7O0xK+R35tlx95odkDmZWS9JyWWQSmIhqwAs= github.com/openconfig/ygot v0.9.0/go.mod h1:oCQNdXnv7dWc8scTDgoFkauv1wwplJn5HspHcjlxSAQ= -github.com/openconfig/ygot v0.10.0 h1:EmgwLXbFiCBmEUlSI4/1fPuRzgf4EsD0sThmAmRqbYM= github.com/openconfig/ygot v0.10.0/go.mod h1:oCQNdXnv7dWc8scTDgoFkauv1wwplJn5HspHcjlxSAQ= +github.com/openconfig/ygot v0.10.5 h1:8RQxM/AY4bbfKAtYihWlyIEwKuLjNE1jzXI4gxPN27w= +github.com/openconfig/ygot v0.10.5/go.mod h1:oCQNdXnv7dWc8scTDgoFkauv1wwplJn5HspHcjlxSAQ= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= @@ -379,8 +382,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -395,8 +398,9 @@ github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -415,8 +419,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= @@ -432,6 +437,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -453,8 +460,8 @@ golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -484,6 +491,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -510,9 +518,12 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -525,6 +536,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -559,26 +571,30 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -612,6 +628,8 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -664,8 +682,10 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -684,6 +704,7 @@ gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPA gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -709,8 +730,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -720,24 +742,24 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.5 h1:zsMTffV0Le2EiI0aKvlTHEnXGxk1HiqGRhJcCPiI7JI= -k8s.io/api v0.20.5/go.mod h1:FQjAceXnVaWDeov2YUWhOb6Yt+5UjErkp6UO3nczO1Y= -k8s.io/apimachinery v0.20.5 h1:wO/FxMVRn223rAKxnBbwCyuN96bS9MFTIvP0e/V7cps= -k8s.io/apimachinery v0.20.5/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/client-go v0.20.5 h1:dJGtYUvFrFGjQ+GjXEIby0gZWdlAOc0xJBJqY3VyDxA= -k8s.io/client-go v0.20.5/go.mod h1:Ee5OOMMYvlH8FCZhDsacjMlCBwetbGZETwo1OA+e6Zw= +k8s.io/api v0.21.0 h1:gu5iGF4V6tfVCQ/R+8Hc0h7H1JuEhzyEi9S4R5LM8+Y= +k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= +k8s.io/apimachinery v0.21.0 h1:3Fx+41if+IRavNcKOz09FwEXDBG6ORh6iMsTSelhkMA= +k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= +k8s.io/client-go v0.21.0 h1:n0zzzJsAQmJngpC0IhgFcApZyoGXPrDIAD601HD09ag= +k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/mocks/PrincipalNetworkDomain.go b/mocks/PrincipalNetworkDomain.go index 38233e76794d9913cf722aa848b2409fe4e1e499..6c795cb7388b80a8446f5c2c8314ed98fa3eba5f 100644 --- a/mocks/PrincipalNetworkDomain.go +++ b/mocks/PrincipalNetworkDomain.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.6.0. DO NOT EDIT. +// Code generated by mockery 2.7.4. DO NOT EDIT. package mocks @@ -6,6 +6,8 @@ import ( mock "github.com/stretchr/testify/mock" uuid "github.com/google/uuid" + + ygot "github.com/openconfig/ygot/ygot" ) // PrincipalNetworkDomain is an autogenerated mock type for the PrincipalNetworkDomain type @@ -41,6 +43,71 @@ func (_m *PrincipalNetworkDomain) AddSbi(_a0 interface{}) error { return r0 } +// ChangeOND provides a mock function with given fields: _a0, operation, path, value +func (_m *PrincipalNetworkDomain) ChangeOND(_a0 uuid.UUID, operation interface{}, path string, value ...string) error { + _va := make([]interface{}, len(value)) + for _i := range value { + _va[_i] = value[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, operation, path) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(uuid.UUID, interface{}, string, ...string) error); ok { + r0 = rf(_a0, operation, path, value...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Commit provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) Commit(_a0 uuid.UUID) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(uuid.UUID) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Committed provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) Committed() []uuid.UUID { + ret := _m.Called() + + var r0 []uuid.UUID + if rf, ok := ret.Get(0).(func() []uuid.UUID); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]uuid.UUID) + } + } + + return r0 +} + +// Confirm provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) Confirm(_a0 uuid.UUID) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(uuid.UUID) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // ContainsDevice provides a mock function with given fields: _a0 func (_m *PrincipalNetworkDomain) ContainsDevice(_a0 uuid.UUID) bool { ret := _m.Called(_a0) @@ -69,6 +136,22 @@ func (_m *PrincipalNetworkDomain) Destroy() error { return r0 } +// Devices provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) Devices() []uuid.UUID { + ret := _m.Called() + + var r0 []uuid.UUID + if rf, ok := ret.Get(0).(func() []uuid.UUID); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]uuid.UUID) + } + } + + return r0 +} + // GetDescription provides a mock function with given fields: func (_m *PrincipalNetworkDomain) GetDescription() string { ret := _m.Called() @@ -83,6 +166,29 @@ func (_m *PrincipalNetworkDomain) GetDescription() string { return r0 } +// GetDevice provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) GetDevice(_a0 uuid.UUID) (ygot.GoStruct, error) { + ret := _m.Called(_a0) + + var r0 ygot.GoStruct + if rf, ok := ret.Get(0).(func(uuid.UUID) ygot.GoStruct); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ygot.GoStruct) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetName provides a mock function with given fields: func (_m *PrincipalNetworkDomain) GetName() string { ret := _m.Called() @@ -113,7 +219,7 @@ func (_m *PrincipalNetworkDomain) GetSBIs() interface{} { return r0 } -// Id provides a mock function with given fields: +// ID provides a mock function with given fields: func (_m *PrincipalNetworkDomain) ID() uuid.UUID { ret := _m.Called() @@ -150,6 +256,22 @@ func (_m *PrincipalNetworkDomain) MarshalDevice(_a0 uuid.UUID) (string, error) { return r0, r1 } +// Pending provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) Pending() []uuid.UUID { + ret := _m.Called() + + var r0 []uuid.UUID + if rf, ok := ret.Get(0).(func() []uuid.UUID); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]uuid.UUID) + } + } + + return r0 +} + // RemoveDevice provides a mock function with given fields: _a0 func (_m *PrincipalNetworkDomain) RemoveDevice(_a0 uuid.UUID) error { ret := _m.Called(_a0) diff --git a/mocks/SouthboundInterface.go b/mocks/SouthboundInterface.go index 6217f5a7674db004f64254bfe5d5c8348ac733a7..38ffb81ff021989bb8153c33169ce40ad8863a33 100644 --- a/mocks/SouthboundInterface.go +++ b/mocks/SouthboundInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.6.0. DO NOT EDIT. +// Code generated by mockery 2.7.4. DO NOT EDIT. package mocks @@ -18,7 +18,7 @@ type SouthboundInterface struct { mock.Mock } -// Id provides a mock function with given fields: +// ID provides a mock function with given fields: func (_m *SouthboundInterface) ID() uuid.UUID { ret := _m.Called() diff --git a/mocks/Storable.go b/mocks/Storable.go index 630f70145d05ec4878461e243d238fe59958a483..e55a23eb41a51d2715f2ee94dfbb9e7a285a0145 100644 --- a/mocks/Storable.go +++ b/mocks/Storable.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.6.0. DO NOT EDIT. +// Code generated by mockery 2.7.4. DO NOT EDIT. package mocks @@ -13,7 +13,7 @@ type Storable struct { mock.Mock } -// Id provides a mock function with given fields: +// ID provides a mock function with given fields: func (_m *Storable) ID() uuid.UUID { ret := _m.Called() diff --git a/mocks/Transport.go b/mocks/Transport.go index 6dc256acae1ee730623be6ab1fd5508f9f4ed9f5..7d8531e57334fd330a698165132428273d618f39 100644 --- a/mocks/Transport.go +++ b/mocks/Transport.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.6.0. DO NOT EDIT. +// Code generated by mockery 2.7.4. DO NOT EDIT. package mocks @@ -76,29 +76,20 @@ func (_m *Transport) ProcessResponse(resp interface{}, root interface{}, models } // Set provides a mock function with given fields: ctx, params -func (_m *Transport) Set(ctx context.Context, params ...interface{}) (interface{}, error) { +func (_m *Transport) Set(ctx context.Context, params ...interface{}) error { var _ca []interface{} _ca = append(_ca, ctx) _ca = append(_ca, params...) ret := _m.Called(_ca...) - var r0 interface{} - if rf, ok := ret.Get(0).(func(context.Context, ...interface{}) interface{}); ok { + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, ...interface{}) error); ok { r0 = rf(ctx, params...) } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, ...interface{}) error); ok { - r1 = rf(ctx, params...) - } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } // Subscribe provides a mock function with given fields: ctx, params diff --git a/mocks/TransportOptions.go b/mocks/TransportOptions.go index 4893655f8a5bc0fef06e4898e116fc3df7171421..b969fdb60e490658017a191dfb2bae818408b83a 100644 --- a/mocks/TransportOptions.go +++ b/mocks/TransportOptions.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.6.0. DO NOT EDIT. +// Code generated by mockery 2.7.4. DO NOT EDIT. package mocks diff --git a/nucleus/controller_test.go b/nucleus/controller_test.go index 47fa1026ccc54a35ca3296ce0779fc58af856e9b..1dc5b8bd001270ace098c39a81e027263df7869c 100644 --- a/nucleus/controller_test.go +++ b/nucleus/controller_test.go @@ -9,6 +9,9 @@ import ( ) func TestRun(t *testing.T) { + if testing.Short() { + t.Skip("this test is executed separately") + } type args struct { request string } diff --git a/nucleus/errors.go b/nucleus/errors.go index 8bd01fe67150cba93b9c6f2cb5710ae63383b11c..7466b3ec10d37bf51b5f5605643882c359664f74 100644 --- a/nucleus/errors.go +++ b/nucleus/errors.go @@ -90,3 +90,13 @@ type ErrInvalidTransportOptions struct { func (e ErrInvalidTransportOptions) Error() string { return fmt.Sprintf("invalid transport options: %v", reflect.TypeOf(e.t)) } + +// ErrOperationNotSupported implements the Error interface and is called if the +// wrong Operation has been provided. +type ErrOperationNotSupported struct { + o interface{} +} + +func (e ErrOperationNotSupported) Error() string { + return fmt.Sprintf("transport operation not supported: %v", reflect.TypeOf(e.o)) +} diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 3bbcc03c6146804d862af1932f5cac2a4a0fca86..58ecadda376e4decae176cb8d6f079dc1a57727e 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -2,19 +2,21 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" + pathutils "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path" "context" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/gnmi/proto/gnmi_ext" "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" log "github.com/sirupsen/logrus" "reflect" - "strings" ) // CtxKeyType is a custom type to be used as key in a context.WithValue() or // context.Value() call. For more information see: // https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/ +// TODO: Unexport to comply with best practice type CtxKeyType string const ( @@ -22,8 +24,16 @@ const ( CtxKeyOpts CtxKeyType = "opts" // CtxKeyConfig is a context key for gnmi.Config CtxKeyConfig = "config" + // CtxKeyOperation is a context key for a gNMI operation (update, replace, delete) + CtxKeyOperation = "op" ) +var opmap = map[Operation]string{ + TransportUpdate: "update", + TransportReplace: "replace", + TransportDelete: "delete", +} + // Gnmi implements the Transport interface and provides an SBI with the // possibility to access a gNMI endpoint. type Gnmi struct { @@ -75,22 +85,55 @@ 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, args ...interface{}) (interface{}, error) { +func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error { if g.client == nil { - return nil, &ErrNilClient{} + return &ErrNilClient{} } if len(args) == 0 { - return nil, &ErrInvalidParameters{ + return &ErrInvalidParameters{ f: "gnmi.Set()", r: "no parameters provided", } } + if len(args) == 2 { + switch args[0].(type) { + case ygot.GoStruct: + return g.applyDiff(ctx, args[0]) + default: + + } + } + + opts := make([]interface{}, 0) + for _, o := range args { + attrs, ok := o.([]string) + if !ok { + return &ErrInvalidTypeAssertion{ + v: o, + t: reflect.TypeOf("placeholder"), + } + } else if attrs == nil || len(attrs) == 0 { + return &ErrInvalidParameters{ + f: "gnmi.Set()", + r: "no parameters provided", + } + } + opts = append(opts, &gnmi.Operation{ + // Hardcoded TransportUpdate until multiple operations are supported + Type: opmap[TransportUpdate], + Origin: "", + Target: "", + Path: gnmi.SplitPath(attrs[0]), + Val: attrs[1], + }) + } + // 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 args { + for _, p := range opts { switch p.(type) { case *gnmi.Operation: op := p.(*gnmi.Operation) @@ -101,19 +144,66 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) (interface{}, error case *gnmi_ext.Extension: exts = append(exts, p.(*gnmi_ext.Extension)) default: - return nil, &ErrInvalidParameters{ + return &ErrInvalidParameters{ f: "gnmi.Set()", r: "args contain invalid type", } } } if len(ops) == 0 { - return nil, &ErrInvalidParameters{ + return &ErrInvalidParameters{ f: "gnmi.Set()", r: "no operations provided", } } - return g.set(ctx, ops, exts...) + resp, err := g.set(ctx, ops, exts...) + if err != nil { + return err + } + log.Info(resp) + return nil +} + +func (g *Gnmi) applyDiff(ctx context.Context, payload ...interface{}) error { + if len(payload) != 2 { + return &ErrInvalidParameters{} + } + op := ctx.Value(CtxKeyOperation) + oldstate, ok := payload[0].(ygot.GoStruct) + if !ok { + return &ErrInvalidTypeAssertion{ + v: payload[0], + t: reflect.TypeOf("ygot.GoStruct"), + } + } + newstate, ok := payload[1].(ygot.GoStruct) + if !ok { + return &ErrInvalidTypeAssertion{ + v: payload[1], + t: reflect.TypeOf("ygot.GoStruct"), + } + } + + diff, err := ygot.Diff(oldstate, newstate) + if err != nil { + return err + } + req := &gpb.SetRequest{} + if diff.Update != nil { + switch op { + case TransportUpdate: + req.Update = diff.Update + case TransportReplace: + req.Replace = diff.Update + default: + return &ErrOperationNotSupported{} + } + } else if diff.Delete != nil { + req.Delete = diff.Delete + } + resp, err := g.client.Set(ctx, req) + log.Info(resp) + return err } //Subscribe subscribes to a gNMI target @@ -141,7 +231,7 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch val, ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal) if ok { opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} - if err := g.Unmarshal(val.JsonIetfVal, extraxtPathElements(fullPath), root, opts...); err != nil { + if err := g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(fullPath), root, opts...); err != nil { return err } return nil @@ -157,14 +247,6 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch return nil } -func extraxtPathElements(path *gpb.Path) []string { - elems := make([]string, len(path.Elem)) - for i, e := range path.Elem { - elems[i] = strings.Title(e.Name) - } - return elems -} - // Capabilities calls GNMI capabilities func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) { log.WithFields(log.Fields{ diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index 38270c8da5fe3f31f030c5010de2b980a8d8d127..18627bd9349ab7d740a80bd5472c7eda045632bf 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -276,7 +276,6 @@ func TestGnmi_Set(t *testing.T) { name string fields fields args args - want interface{} wantErr bool }{ { @@ -285,20 +284,15 @@ func TestGnmi_Set(t *testing.T) { args: args{ params: nil, }, - want: nil, wantErr: true, }, // TODO: Positive test cases } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.fields.transport.Set(context.Background(), tt.args.params...) + err := tt.fields.transport.Set(context.Background(), tt.args.params...) if (err != nil) != tt.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Set() got = %v, want %v", got, tt.want) } }) } diff --git a/nucleus/http.go b/nucleus/http.go index 56ad4cb0af707662e72656e1a7b6a4fa0a88f046..ee0590e4e5645ff665417c9be7ec3d6b1542d220 100644 --- a/nucleus/http.go +++ b/nucleus/http.go @@ -7,11 +7,18 @@ import ( "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" log "github.com/sirupsen/logrus" + "io" "net/http" "net/url" "time" ) +var apiOpmap = map[string]Operation{ + "update": TransportUpdate, + "replace": TransportReplace, + "delete": TransportDelete, +} + func stopHttpServer() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -61,7 +68,6 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { return } - id, err := uuid.Parse(query.Get("uuid")) if err != nil { if err.Error() != "invalid UUID length: 0" { @@ -88,23 +94,18 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { if query.Get("q") != "init" && query.Get("q") != "getIDs" { pnd, err = c.pndc.get(pid) if err != nil { - log.Error(err) - writer.WriteHeader(http.StatusInternalServerError) + handleServerError(writer, err) return } sbic := pnd.GetSBIs() sbi, err = sbic.(*sbiStore).get(sid) if err != nil { - log.WithFields(log.Fields{ - "requested uuid": sid, - "available uuids": sbic.(*sbiStore).UUIDs(), - }).Error(err) - writer.WriteHeader(http.StatusInternalServerError) + handleServerError(writer, err) return } } - switch query.Get("q") { + switch q := query.Get("q"); q { case "addDevice": d, err := NewDevice(sbi, &GnmiTransportOptions{ Config: gnmi.Config{ @@ -167,42 +168,75 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") fmt.Fprintf(writer, "%v", device) case "getIDs": - writeIDs := func(typ string, ids []uuid.UUID) { - fmt.Fprintf(writer, "%v:\n", typ) - for i, id := range ids { - fmt.Fprintf(writer, "%v: %v\n", i+1, id) - } - } + pnds := c.pndc.UUIDs() - writeIDs("PNDs", pnds) - writeIDs("SBIs", c.sbic.UUIDs()) - for _,id := range pnds{ + writeIDs(writer, "PNDs", pnds) + writeIDs(writer, "SBIs", c.sbic.UUIDs()) + for _, id := range pnds { p, err := c.pndc.get(id) if err != nil { - writer.WriteHeader(http.StatusInternalServerError) - log.Error(err) + handleServerError(writer, err) return } - writeIDs("Devices", p.(*pndImplementation).devices.UUIDs()) + writeIDs(writer, "Devices", p.Devices()) } case "init": - writeIDs := func(typ string, ids []uuid.UUID) { - for _, id := range ids { - fmt.Fprintf(writer, "%v", id) - } + 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 { + handleServerError(writer, err) + return } - writeIDs("PNDs", c.pndc.UUIDs()) - writeIDs("SBIs", c.sbic.UUIDs()) - case "set": - resp, err := pnd.(*pndImplementation).Set(id, query.Get("path"), query.Get("value")) - if err != nil { - writer.WriteHeader(http.StatusInternalServerError) - log.Error(err) + writer.WriteHeader(http.StatusOK) + case "delete": + if err := pnd.ChangeOND(id, TransportDelete, query.Get("path")); err != nil { + handleServerError(writer, err) return } writer.WriteHeader(http.StatusOK) - fmt.Fprintln(writer, resp) + case "change-list": + changes := pnd.Committed() + writeIDs(writer, "Tentative changes", changes) + case "change-list-pending": + changes := pnd.Pending() + writeIDs(writer, "Pending changes", changes) + case "change-commit": + cuid, err := uuid.Parse(query.Get("cuid")) + if err != nil { + handleServerError(writer, err) + return + } + if err := pnd.Commit(cuid); err != nil { + handleServerError(writer, err) + return + } + writer.WriteHeader(http.StatusAccepted) + case "change-confirm": + cuid, err := uuid.Parse(query.Get("cuid")) + if err != nil { + handleServerError(writer, err) + return + } + if err := pnd.Confirm(cuid); err != nil { + handleServerError(writer, err) + return + } + writer.WriteHeader(http.StatusAccepted) default: writer.WriteHeader(http.StatusBadRequest) } } + +func writeIDs(w io.Writer, typ string, ids []uuid.UUID) { + fmt.Fprintf(w, "%v:\n", typ) + for i, id := range ids { + fmt.Fprintf(w, "%v: %v\n", i+1, id) + } +} + +func handleServerError(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "error: %v", err) + log.Error(err) +} diff --git a/nucleus/http_test.go b/nucleus/http_test.go index 9efa72110a1fe89cf1507424d147e7faac6fd71b..aad389b3ede8f378369b7fb59badc0aee26ac34a 100644 --- a/nucleus/http_test.go +++ b/nucleus/http_test.go @@ -14,18 +14,18 @@ func testSetupHTTP() { sbi = &OpenConfig{id: defaultSbiID} sbi.Schema() var err error - pnd, err = NewPND("test", "test pnd", defaultPndID, sbi) + httpTestPND, err = NewPND("test", "test pnd", defaultPndID, sbi) if err != nil { log.Fatal(err) } - d = mockDevice() - tr := d.Transport.(*mocks.Transport) + httpTestDevice = mockDevice() + tr := httpTestDevice.Transport.(*mocks.Transport) mockError := errors.New("mock error") tr.On("Get", mockContext, "/system/config/hostname").Return(mock.Anything, nil) tr.On("Get", mockContext, "error").Return(mock.Anything, mockError) tr.On("Set", mockContext, mock.Anything).Return(mock.Anything, nil) tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(nil) - if err := pnd.AddDevice(&d); err != nil { + if err := httpTestPND.AddDevice(&httpTestDevice); err != nil { log.Fatal(err) } args = "&uuid=" + mdid.String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String() @@ -33,15 +33,12 @@ func testSetupHTTP() { if err := c.sbic.add(sbi); err != nil { log.Fatal(err) } - if err := c.pndc.add(pnd); err != nil { + if err := c.pndc.add(httpTestPND); err != nil { log.Fatal(err) } } func Test_httpApi(t *testing.T) { - if testing.Short() { - t.Skip("this test is executed separately") - } tests := []struct { name string request string @@ -123,10 +120,54 @@ func Test_httpApi(t *testing.T) { }, { name: "set", - request: apiEndpoint + "/api?q=set" + args + "&path=/system/config/hostname&value=ceos3000", + request: apiEndpoint + "/api?q=update" + args + "&path=/system/config/hostname&value=ceos3000", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "replace", + request: apiEndpoint + "/api?q=replace" + args + "&path=/system/config/hostname&value=ceos3000", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "delete", + request: apiEndpoint + "/api?q=delete" + args + "&path=/system/config/hostname", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "change list", + request: apiEndpoint + "/api?q=change-list" + args + "&path=/system/config/hostname", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "change list pending", + request: apiEndpoint + "/api?q=change-list-pending" + args + "&path=/system/config/hostname", want: &http.Response{StatusCode: http.StatusOK}, wantErr: false, }, + { + name: "change commit", + request: apiEndpoint + "/api?q=change-commit" + args + "&cuid=" + cuid.String(), + // TODO: Mock Change for testing + want: &http.Response{StatusCode: http.StatusInternalServerError}, + wantErr: false, + }, + { + name: "change confirm", + request: apiEndpoint + "/api?q=change-confirm" + args + "&cuid=" + cuid.String(), + // TODO: Mock Change for testing + want: &http.Response{StatusCode: http.StatusInternalServerError}, + wantErr: false, + }, + { + name: "bad request", + request: apiEndpoint + "/api?q=bad-request" + args, + want: &http.Response{StatusCode: http.StatusBadRequest}, + wantErr: false, + }, { name: "internal server errror: wrong pnd", request: apiEndpoint + "/api?pnd=" + uuid.New().String(), @@ -157,9 +198,9 @@ 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 pnd.(*pndImplementation).devices.store { + for k := range httpTestPND.(*pndImplementation).devices.store { if k != mdid { - if err := pnd.RemoveDevice(k); err != nil { + if err := httpTestPND.RemoveDevice(k); err != nil { t.Error(err) return } diff --git a/nucleus/initialise_test.go b/nucleus/initialise_test.go index 6c90b054e44d009e03542821ced09f46cd65bb49..6d66325fe3ed7060b3ac780f272cbcb7015c4245 100644 --- a/nucleus/initialise_test.go +++ b/nucleus/initialise_test.go @@ -25,12 +25,13 @@ var defaultPndID uuid.UUID var ocUUID uuid.UUID var iid uuid.UUID var altIid uuid.UUID +var cuid uuid.UUID var sbi SouthboundInterface -var pnd PrincipalNetworkDomain +var httpTestPND PrincipalNetworkDomain var gnmiMessages map[string]pb.Message var gnmiConfig *gnmi.Config -var d Device +var httpTestDevice Device var startGnmiTarget chan string var stopGnmiTarget chan bool @@ -108,51 +109,38 @@ func newGnmiTransportOptions() *GnmiTransportOptions { func readTestUUIDs() { var err error did, err = uuid.Parse("4d8246f8-e884-41d6-87f5-c2c784df9e44") - if err != nil { - log.Fatal(err) - } - mdid, err = uuid.Parse("688a264e-5f85-40f8-bd13-afc42fcd5c7a") - if err != nil { - log.Fatal(err) - } - defaultSbiID, err = uuid.Parse("b70c8425-68c7-4d4b-bb5e-5586572bd64b") - if err != nil { - log.Fatal(err) - } - defaultPndID, err = uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad") - if err != nil { - log.Fatal(err) - } - ocUUID, err = uuid.Parse("5e252b70-38f2-4c99-a0bf-1b16af4d7e67") - if err != nil { - log.Fatal(err) - } iid, err = uuid.Parse("8495a8ac-a1e8-418e-b787-10f5878b2690") altIid, err = uuid.Parse("edc5de93-2d15-4586-b2a7-fb1bc770986b") + cuid, err = uuid.Parse("3e8219b0-e926-400d-8660-217f2a25a7c6") if err != nil { log.Fatal(err) } } func mockDevice() Device { + sbi := &OpenConfig{} return Device{ UUID: mdid, - GoStruct: nil, - SBI: &OpenConfig{}, + GoStruct: sbi.Schema().Root, + SBI: sbi, Transport: &mocks.Transport{}, } } func newPnd() pndImplementation { return pndImplementation{ - name: "default", - description: "default test pnd", - sbic: sbiStore{store{}}, - devices: deviceStore{store{}}, - id: defaultPndID, + name: "default", + description: "default test pnd", + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + pendingChanges: changeStore{store{}}, + committedChanges: changeStore{store{}}, + confirmedChanges: changeStore{store{}}, + id: defaultPndID, + errChans: make(map[uuid.UUID]chan error), } } diff --git a/nucleus/pnd/change.go b/nucleus/pnd/change.go new file mode 100644 index 0000000000000000000000000000000000000000..55c7fc539f515d4383a6e9b5783986ed6feac270 --- /dev/null +++ b/nucleus/pnd/change.go @@ -0,0 +1,133 @@ +package pnd + +import ( + "errors" + "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 + +func init() { + var err error + e := os.Getenv("GOSDN_CHANGE_TIMEOUT") + if e != "" { + changeTimeout, err = time.ParseDuration(e) + if err != nil { + log.Fatal(err) + } + } else { + changeTimeout, err = time.ParseDuration("10m") + if err != nil { + log.Fatal(err) + } + } + log.Debugf("change timeout set to %v", changeTimeout) +} + +// NewChange takes a Device UUID, a pair GoStructs (current and intended state) +// a callback function and a channel for errors and returns a *Change +// The callback function is used by the Commit() and Confirm() functions. It +// must define how the change is carried out. +func NewChange(device uuid.UUID, currentState ygot.GoStruct, change ygot.GoStruct, callback func(ygot.GoStruct, ygot.GoStruct) error, errChan chan error) *Change { + return &Change{ + cuid: uuid.New(), + duid: device, + timestamp: time.Now(), + previousState: currentState, + intendedState: change, + committed: false, + confirmed: false, + callback: callback, + errChan: errChan, + Done: make(chan int), + } +} + +// Change is an intended change to an OND. It is unique and immutable. +// It has a cuid, a timestamp, and holds both the previous and the new +// state. It keeps track if the state is committed and confirmed. A callback +// exists to acess the proper transport for the changed OND +type Change struct { + cuid uuid.UUID + duid uuid.UUID + timestamp time.Time + previousState ygot.GoStruct + intendedState ygot.GoStruct + committed bool + confirmed bool + inconsistent bool + callback func(ygot.GoStruct, ygot.GoStruct) error + lock sync.RWMutex + cancelFunc context.CancelFunc + errChan chan error + // TODO: Move nucleus.pndImplementation and Change to same package and unexport + Done chan int +} + +// ID returns the Change's UUID +func (c *Change) ID() uuid.UUID { + return c.cuid +} + +// Commit pushes the cange to the OND using the callback() function +// and starts the timeout-timer for the Change. If the timer expires +// the change is rolled back. +func (c *Change) Commit() error { + if err := c.callback(c.intendedState, c.previousState); err != nil { + return err + } + c.committed = true + log.WithFields(log.Fields{ + "change uuid": c.cuid, + "device uuid": c.duid, + }).Debug("change commited") + ctx, cancel := context.WithCancel(context.Background()) + c.cancelFunc = cancel + go c.rollbackHandler(ctx) + return nil +} + +func (c *Change) rollbackHandler(ctx context.Context) { + select { + case <-ctx.Done(): + return + case <-time.Tick(changeTimeout): + c.lock.RLock() + defer c.lock.RUnlock() + if !c.confirmed { + c.errChan <- c.callback(c.previousState, c.intendedState) + log.WithFields(log.Fields{ + "change uuid": c.cuid, + "device uuid": c.duid, + }).Info("change timed out") + } + } +} + +// Confirm confirms a committed Change and stops the rollback timer. +func (c *Change) Confirm() error { + c.lock.RLock() + if !c.committed { + defer c.lock.RUnlock() + return errors.New("cannot confirm uncommitted change") + } + c.lock.RUnlock() + c.lock.Lock() + defer c.lock.Unlock() + c.confirmed = true + c.cancelFunc() + close(c.errChan) + c.Done <- 0 + close(c.Done) + log.WithFields(log.Fields{ + "change uuid": c.cuid, + "device uuid": c.duid, + }).Info("change confirmed") + return nil +} diff --git a/nucleus/pnd/change_test.go b/nucleus/pnd/change_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7871a0db04a1527246df36a23b0ad83d264d10ef --- /dev/null +++ b/nucleus/pnd/change_test.go @@ -0,0 +1,244 @@ +package pnd + +import ( + "context" + "errors" + "github.com/google/uuid" + "github.com/openconfig/ygot/exampleoc" + "github.com/openconfig/ygot/ygot" + "reflect" + "sync" + "testing" + "time" +) + +var commit = "commit" +var rollback = "rollback" + +var commitDevice = &exampleoc.Device{ + System: &exampleoc.System{ + Hostname: &commit, + }, +} + +var rollbackDevice = &exampleoc.Device{ + System: &exampleoc.System{ + Hostname: &rollback, + }, +} + +func TestChange_CommitRollback(t *testing.T) { + wantErr := false + want := rollback + callback := make(chan string) + c := &Change{ + cuid: changeUUID, + duid: did, + timestamp: time.Now(), + previousState: rollbackDevice, + intendedState: commitDevice, + callback: func(first ygot.GoStruct, second ygot.GoStruct) error { + hostname := *first.(*exampleoc.Device).System.Hostname + t.Logf("hostname: %v", hostname) + switch hostname { + case rollback: + callback <- rollback + } + return nil + }, + lock: sync.RWMutex{}, + } + go func() { + time.Sleep(time.Millisecond * 10) + if err := c.Commit(); (err != nil) != wantErr { + t.Errorf("Commit() error = %v, wantErr %v", err, wantErr) + } + time.Sleep(changeTimeout) + }() + got := <-callback + if !reflect.DeepEqual(got, want) { + t.Errorf("Commit() = %v, want %v", got, want) + } + close(callback) +} + +func TestChange_CommitRollbackError(t *testing.T) { + wantErr := false + want := errors.New("this is an expected error") + c := &Change{ + cuid: changeUUID, + duid: did, + timestamp: time.Now(), + previousState: rollbackDevice, + intendedState: commitDevice, + callback: func(first ygot.GoStruct, second ygot.GoStruct) error { + hostname := *first.(*exampleoc.Device).System.Hostname + t.Logf("hostname: %v", hostname) + switch hostname { + case rollback: + return errors.New("this is an expected error") + } + return nil + }, + lock: sync.RWMutex{}, + errChan: make(chan error), + } + go func() { + time.Sleep(time.Millisecond * 10) + if err := c.Commit(); (err != nil) != wantErr { + t.Errorf("Commit() error = %v, wantErr %v", err, wantErr) + } + time.Sleep(changeTimeout) + }() + got := <-c.errChan + if !reflect.DeepEqual(got, want) { + t.Errorf("Commit() = %v, want %v", got, want) + } + close(c.errChan) +} + +func TestChange_CommitError(t *testing.T) { + wantErr := true + c := &Change{ + cuid: changeUUID, + duid: did, + timestamp: time.Now(), + previousState: rollbackDevice, + intendedState: commitDevice, + callback: func(first ygot.GoStruct, second ygot.GoStruct) error { + return errors.New("this is an expected error") + }, + lock: sync.RWMutex{}, + } + go func() { + time.Sleep(time.Millisecond * 10) + if err := c.Commit(); (err != nil) != wantErr { + t.Errorf("Commit() error = %v, wantErr %v", err, wantErr) + } + }() + got := c.committed + if !reflect.DeepEqual(got, false) { + t.Errorf("Commit() = %v, want %v", got, false) + } +} + +func TestChange_Commit(t *testing.T) { + wantErr := false + want := commit + callback := make(chan string) + + c := &Change{ + cuid: changeUUID, + duid: did, + timestamp: time.Now(), + previousState: rollbackDevice, + intendedState: commitDevice, + callback: func(first ygot.GoStruct, second ygot.GoStruct) error { + hostname := *first.(*exampleoc.Device).System.Hostname + t.Logf("hostname: %v", hostname) + callback <- hostname + return nil + }, + lock: sync.RWMutex{}, + errChan: make(chan error), + Done: make(chan int), + } + go func() { + time.Sleep(time.Millisecond * 10) + if err := c.Commit(); (err != nil) != wantErr { + t.Errorf("Commit() error = %v, wantErr %v", err, wantErr) + } + if err := c.Confirm(); err != nil { + t.Errorf("Commit() error = %v", err) + } + }() + got := <-callback + if !reflect.DeepEqual(got, want) { + t.Errorf("Commit() = %v, want %v", got, want) + } + close(callback) +} + +func TestChange_Confirm(t *testing.T) { + _, cancel := context.WithCancel(context.Background()) + type fields struct { + cuid uuid.UUID + duid uuid.UUID + timestamp time.Time + previousState ygot.GoStruct + intendedState ygot.GoStruct + callback func(ygot.GoStruct, ygot.GoStruct) error + committed bool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "committed", + fields: fields{ + committed: true, + }, + wantErr: false, + }, + { + name: "uncommitted", + fields: fields{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Change{ + committed: tt.fields.committed, + timestamp: tt.fields.timestamp, + previousState: &exampleoc.Device{ + System: &exampleoc.System{ + Hostname: &rollback, + }, + }, + intendedState: &exampleoc.Device{ + System: &exampleoc.System{ + Hostname: &commit, + }, + }, + cancelFunc: cancel, + lock: sync.RWMutex{}, + errChan: make(chan error), + Done: make(chan int, 1), + } + if err := c.Confirm(); (err != nil) != tt.wantErr { + t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } + cancel() +} + +func TestChange_ID(t *testing.T) { + type fields struct { + cuid uuid.UUID + } + tests := []struct { + name string + fields fields + want uuid.UUID + }{ + { + name: "default", + fields: fields{cuid: changeUUID}, + want: changeUUID, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Change{ + cuid: tt.fields.cuid, + } + if got := c.ID(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ID() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/nucleus/pnd/initialise_test.go b/nucleus/pnd/initialise_test.go new file mode 100644 index 0000000000000000000000000000000000000000..674656c8825327bac8c34ef84a8db6548ea2baaf --- /dev/null +++ b/nucleus/pnd/initialise_test.go @@ -0,0 +1,22 @@ +package pnd + +import ( + "github.com/google/uuid" + log "github.com/sirupsen/logrus" + "os" + "testing" +) + +// UUIDs for test cases +var changeUUID uuid.UUID +var did uuid.UUID + +func TestMain(m *testing.M) { + var err error + changeUUID, err = uuid.Parse("cfbb96cd-ecad-45d1-bebf-1851760f5087") + did, err = uuid.Parse("4d8246f8-e884-41d6-87f5-c2c784df9e44") + if err != nil { + log.Fatal(err) + } + os.Exit(m.Run()) +} diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go index 88c08172e2f4c859d1a8212328a20680953e3054..0a1e69788165da250a97bba5c2a36451ee49248b 100644 --- a/nucleus/principalNetworkDomain.go +++ b/nucleus/principalNetworkDomain.go @@ -2,7 +2,10 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" + . "code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd" //nolint "context" + "github.com/openconfig/ygot/ygot" + "github.com/openconfig/ygot/ytypes" log "github.com/sirupsen/logrus" "encoding/json" @@ -16,7 +19,10 @@ type PrincipalNetworkDomain interface { AddSbi(interface{}) error RemoveSbi(uuid.UUID) error AddDevice(interface{}) error + GetDevice(uuid uuid.UUID) (ygot.GoStruct, error) RemoveDevice(uuid.UUID) error + Devices() []uuid.UUID + ChangeOND(uuid uuid.UUID, operation interface{}, path string, value ...string) error Request(uuid.UUID, string) error RequestAll(string) error GetName() string @@ -25,24 +31,24 @@ type PrincipalNetworkDomain interface { ContainsDevice(uuid.UUID) bool GetSBIs() interface{} ID() uuid.UUID -} - -type pndImplementation struct { - name string - description string - sbic sbiStore - devices deviceStore - id uuid.UUID + Pending() []uuid.UUID + Committed() []uuid.UUID + Commit(uuid.UUID) error + Confirm(uuid.UUID) error } // NewPND creates a Principle Network Domain func NewPND(name, description string, id uuid.UUID, sbi SouthboundInterface) (PrincipalNetworkDomain, error) { pnd := &pndImplementation{ - name: name, - description: description, - sbic: sbiStore{store{}}, - devices: deviceStore{store{}}, - id: id, + name: name, + description: description, + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + pendingChanges: changeStore{store{}}, + committedChanges: changeStore{store{}}, + confirmedChanges: changeStore{store{}}, + id: id, + errChans: make(map[uuid.UUID]chan error), } if err := pnd.sbic.add(sbi); err != nil { return nil, err @@ -50,10 +56,73 @@ func NewPND(name, description string, id uuid.UUID, sbi SouthboundInterface) (Pr return pnd, nil } +type pndImplementation struct { + name string + description string + sbic sbiStore + devices deviceStore + pendingChanges changeStore + committedChanges changeStore + confirmedChanges changeStore + id uuid.UUID + errChans map[uuid.UUID]chan error +} + +func (pnd *pndImplementation) Pending() []uuid.UUID { + return pnd.pendingChanges.UUIDs() +} + +func (pnd *pndImplementation) Committed() []uuid.UUID { + return pnd.committedChanges.UUIDs() +} + +func (pnd *pndImplementation) Commit(u uuid.UUID) error { + change, err := pnd.pendingChanges.get(u) + if err != nil { + return err + } + if err := change.Commit(); err != nil { + return err + } + go func() { + for { + select { + case err := <-pnd.errChans[u]: + if err != nil { + handleRollbackError(change.ID(), err) + } + case <-change.Done: + } + } + }() + if err := pnd.committedChanges.add(change); err != nil { + return err + } + return pnd.pendingChanges.delete(u) +} + +func (pnd *pndImplementation) Confirm(u uuid.UUID) error { + change, err := pnd.committedChanges.get(u) + if err != nil { + return err + } + if err := change.Confirm(); err != nil { + return err + } + if err := pnd.confirmedChanges.add(change); err != nil { + return err + } + return pnd.committedChanges.delete(u) +} + func (pnd *pndImplementation) ID() uuid.UUID { return pnd.id } +func (pnd *pndImplementation) Devices() []uuid.UUID { + return pnd.devices.UUIDs() +} + // GetName returns the name of the PND func (pnd *pndImplementation) GetName() string { return pnd.name @@ -91,7 +160,7 @@ func (pnd *pndImplementation) AddSbi(sbi interface{}) error { return pnd.addSbi(s) } -// AddSbi removes a SBI from the PND +// RemoveSbi removes a SBI from the PND // TODO: this should to recursivly through // devices and remove the devices using // this SBI @@ -111,6 +180,14 @@ func (pnd *pndImplementation) AddDevice(device interface{}) error { return pnd.addDevice(d) } +func (pnd *pndImplementation) GetDevice(uuid uuid.UUID) (ygot.GoStruct, error) { + d, err := pnd.devices.get(uuid) + if err != nil { + return nil, err + } + return ygot.DeepCopy(d.GoStruct) +} + // RemoveDevice removes a device from the PND func (pnd *pndImplementation) RemoveDevice(uuid uuid.UUID) error { return pnd.removeDevice(uuid) @@ -190,24 +267,54 @@ func (pnd *pndImplementation) RequestAll(path string) error { 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) { +// ChangeOND creates a change from the provided Operation, path and value. The Change is pending and +func (pnd *pndImplementation) ChangeOND(uuid uuid.UUID, operation interface{}, path string, value ...string) error { d, err := pnd.getDevice(uuid) if err != nil { - return nil, err + return err } - ctx := context.Background() + cpy, err := ygot.DeepCopy(d.GoStruct) + ygot.BuildEmptyTree(cpy) + + p, err := ygot.StringToStructuredPath(path) + if err != nil { + return err + } + + if operation != TransportDelete && len(value) != 1 { + return &ErrInvalidParameters{ + f: pnd.ChangeOND, + r: value, + } + } + + switch operation { + case TransportUpdate, TransportReplace: + typedValue := gnmi.TypedValue(value[0]) + if err := ytypes.SetNode(d.SBI.Schema().RootSchema(), cpy, p, typedValue); err != nil { + return err + } + case TransportDelete: + if err := ytypes.DeleteNode(d.SBI.Schema().RootSchema(), cpy, p); err != nil { + return err + } + default: + return &ErrOperationNotSupported{o: operation} + } + + callback := func(state ygot.GoStruct, change ygot.GoStruct) error { + ctx := context.Background() + return d.Transport.Set(ctx, state, change) + } + + errChan := make(chan error) + change := NewChange(uuid, d.GoStruct, cpy, callback, errChan) + pnd.errChans[change.ID()] = errChan + + return pnd.pendingChanges.add(change) +} - // 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...) +func handleRollbackError(id uuid.UUID, err error) { + log.Error(err) + // TODO: Notion of invalid state needed. } diff --git a/nucleus/principalNetworkDomain_test.go b/nucleus/principalNetworkDomain_test.go index f02f840bc948d4887983a4beb532808d9e8dc2c7..6ff0f9327387933ef5dbfe92a2fb944182b7aa87 100644 --- a/nucleus/principalNetworkDomain_test.go +++ b/nucleus/principalNetworkDomain_test.go @@ -5,6 +5,7 @@ import ( "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "errors" "github.com/google/uuid" + "github.com/openconfig/ygot/ygot" "github.com/stretchr/testify/mock" "reflect" "testing" @@ -522,3 +523,223 @@ func Test_pndImplementation_RequestAll(t *testing.T) { }) } } + +func Test_pndImplementation_ChangeOND(t *testing.T) { + type fields struct { + } + type args struct { + uuid uuid.UUID + operation interface{} + path string + value []string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "update", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportUpdate, + path: "/system/config/hostname", + value: []string{"ceos3000"}, + }, + wantErr: false, + }, + { + name: "replace", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportReplace, + path: "/system/config/hostname", + value: []string{"ceos3000"}, + }, + wantErr: false, + }, + { + name: "delete", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportDelete, + path: "/system/config/hostname", + }, + wantErr: false, + }, + { + name: "delete w/args", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportDelete, + path: "/system/config/hostname", + value: []string{"ceos3000"}, + }, + wantErr: false, + }, + + // Negative test cases + { + name: "invalid operation", + fields: fields{}, + args: args{ + uuid: mdid, + operation: "INVALID", + }, + wantErr: true, + }, + { + name: "invalid arg count", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportUpdate, + path: "/system/config/hostname", + value: []string{"ceos3000", "ceos3001"}, + }, + wantErr: true, + }, + { + name: "invalid arg count - update, no args", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportUpdate, + path: "/system/config/hostname", + }, + wantErr: true, + }, + { + name: "invalid arg count - replace, no args", + fields: fields{}, + args: args{ + uuid: mdid, + operation: TransportUpdate, + path: "/system/config/hostname", + }, + wantErr: true, + }, + { + name: "device not found", + fields: fields{}, + args: args{ + uuid: did, + operation: TransportUpdate, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := newPnd() + d := mockDevice() + if err := p.AddDevice(&d); err != nil { + t.Error(err) + return + } + if err := p.ChangeOND(tt.args.uuid, tt.args.operation, tt.args.path, tt.args.value...); (err != nil) != tt.wantErr { + t.Errorf("ChangeOND() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if len(p.pendingChanges.store) != 1 { + t.Errorf("ChangeOND() unexpected change count. got %v, want 1", len(p.pendingChanges.store)) + } + } + }) + } +} + +func Test_pndImplementation_GetDevice(t *testing.T) { + p := newPnd() + d, err := NewDevice(sbi, &GnmiTransportOptions{}) + if err != nil { + t.Error(err) + return + } + if err = p.addDevice(d); err != nil { + t.Error(err) + return + } + type args struct { + uuid uuid.UUID + } + tests := []struct { + name string + args args + want ygot.GoStruct + wantErr bool + }{ + { + name: "default", + args: args{uuid: d.ID()}, + want: sbi.Schema().Root, + wantErr: false, + }, + { + name: "device not found", + args: args{uuid: mdid}, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := p.GetDevice(tt.args.uuid) + if (err != nil) != tt.wantErr { + t.Errorf("GetDevice() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetDevice() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_pndImplementation_Confirm(t *testing.T) { + tests := []struct { + name string + wantErr bool + }{ + { + name: "default", + wantErr: false, + }, + { + name: "uncommitted", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pnd := newPnd() + d := mockDevice() + tr := d.Transport.(*mocks.Transport) + tr.On("Set", mockContext, mock.Anything, mock.Anything).Return(nil) + if err := pnd.addDevice(&d); err != nil { + t.Error(err) + return + } + if err := pnd.ChangeOND(d.ID(), TransportUpdate, "system/config/hostname", "ceos3000"); err != nil { + t.Error(err) + return + } + u := pnd.Pending()[0] + if tt.name != "uncommitted" { + if err := pnd.Commit(u); (err != nil) != tt.wantErr { + t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + if err := pnd.Confirm(u); (err != nil) != tt.wantErr { + t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/nucleus/restconf_transport.go b/nucleus/restconf_transport.go index 0dd477196745a83fda3b8460e1c5755b377748a1..44411bf26586abbbbe7f499f122aa624d9d6e17d 100644 --- a/nucleus/restconf_transport.go +++ b/nucleus/restconf_transport.go @@ -7,30 +7,34 @@ import ( // Restconf implements the Transport interface and provides an SBI with the // possibility to access a Restconf endpoint. -type Restconf struct { -} +type Restconf struct{} -//Get not implemented yet +// Get not yet implemented func (r Restconf) Get(ctx context.Context, params ...string) (interface{}, error) { return nil, &ErrNotYetImplemented{} } -//Set not implemented yet -func (r Restconf) Set(ctx context.Context, params ...string) (interface{}, error) { - return nil, &ErrNotYetImplemented{} +// Set not yet implemented +func (r Restconf) Set(ctx context.Context, params ...interface{}) error { + return &ErrNotYetImplemented{} } -// Subscribe not implemented yet +// Subscribe not yet implemented func (r Restconf) Subscribe(ctx context.Context, params ...string) error { return &ErrNotYetImplemented{} } -// Type returns the RESTCONF transport type +// Type not yet implemented func (r Restconf) Type() string { return "restconf" } -// ProcessResponse not implemented yet +// GetOptions not yet implemented +func (r Restconf) GetOptions() interface{} { + return &ErrNotYetImplemented{} +} + +// ProcessResponse not yet implemented func (r Restconf) ProcessResponse(resp interface{}, root interface{}, models *ytypes.Schema) error { return &ErrNotYetImplemented{} } diff --git a/nucleus/restconf_transport_test.go b/nucleus/restconf_transport_test.go index 5f1eb1e352f2cb8214686c5215eea419c465dcd4..b31df33d665563cb12c64aac28f98788454e7272 100644 --- a/nucleus/restconf_transport_test.go +++ b/nucleus/restconf_transport_test.go @@ -61,26 +61,21 @@ func TestRestconf_ProcessResponse(t *testing.T) { func TestRestconf_Set(t *testing.T) { type args struct { ctx context.Context - params []string + params []interface{} } tests := []struct { name string args args - want interface{} wantErr bool }{ - {name: "not implemented", args: args{}, want: nil, wantErr: true}, + {name: "not implemented", args: args{}, wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := Restconf{} - got, err := r.Set(tt.args.ctx, tt.args.params...) + err := r.Set(tt.args.ctx, tt.args.params...) if (err != nil) != tt.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Set() got = %v, want %v", got, tt.want) } }) } diff --git a/nucleus/sbi-openconfig.go b/nucleus/sbi-openconfig.go deleted file mode 100644 index 0fa4ffa903a1f894325aaf5211341e19a2df754a..0000000000000000000000000000000000000000 --- a/nucleus/sbi-openconfig.go +++ /dev/null @@ -1 +0,0 @@ -package nucleus diff --git a/nucleus/southbound_test.go b/nucleus/southbound_test.go index ab46901446e0cac93464c4aa111436953a51436a..5df2efeab8351538bea042965fb384d0c8affe53 100644 --- a/nucleus/southbound_test.go +++ b/nucleus/southbound_test.go @@ -1,6 +1,7 @@ package nucleus import ( + "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path" "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto" "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "github.com/google/uuid" @@ -161,7 +162,7 @@ func Test_unmarshal(t *testing.T) { if err != nil { t.Error(err) } - fields := extraxtPathElements(resp.Notification[0].Update[0].Path) + fields := path.ToStrings(resp.Notification[0].Update[0].Path) bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal() if err := unmarshal(bytes, fields, tt.args.goStruct, tt.args.opt...); (err != nil) != tt.wantErr { t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr) diff --git a/nucleus/store.go b/nucleus/store.go index 0563a5a30533467809380ac25d949bfc4b578e68..7f6b98fed1ed15b2e23a4c3efb9ed2dce25d83e7 100644 --- a/nucleus/store.go +++ b/nucleus/store.go @@ -1,6 +1,7 @@ package nucleus import ( + p "code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd" "github.com/google/uuid" log "github.com/sirupsen/logrus" "reflect" @@ -131,7 +132,7 @@ func (s deviceStore) get(id uuid.UUID) (*Device, error) { if !ok { return nil, &ErrInvalidTypeAssertion{ v: device, - t: "Device", + t: reflect.TypeOf(&Device{}), } } log.WithFields(log.Fields{ @@ -139,3 +140,25 @@ func (s deviceStore) get(id uuid.UUID) (*Device, error) { }).Debug("device was accessed") return device, nil } + +type changeStore struct { + store +} + +func (s changeStore) get(id uuid.UUID) (*p.Change, error) { + item, err := s.store.get(id) + if err != nil { + return nil, err + } + change, ok := item.(*p.Change) + if !ok { + return nil, &ErrInvalidTypeAssertion{ + v: change, + t: reflect.TypeOf(&p.Change{}), + } + } + log.WithFields(log.Fields{ + "uuid": id, + }).Debug("change was accessed") + return change, nil +} diff --git a/nucleus/transport.go b/nucleus/transport.go index 36dda96300e226ac74de3682d86885e20332191a..6df6e6b7de23206a7b8e1c692d897cfe5fbb9e0b 100644 --- a/nucleus/transport.go +++ b/nucleus/transport.go @@ -7,12 +7,25 @@ import ( "io" ) -// Transport provides an interface for -// Transport implementations like RESTCONF -// or gnmi +// Operation codes numerous operations used to change the state of remote resources. +// It is used as a unified code. Each Transport implementation needs to map these +// accordingly to its specification. +type Operation int + +const ( + // TransportUpdate codes an update operation + TransportUpdate Operation = iota + // TransportReplace codes a replace operation + TransportReplace + // TransportDelete codes a delete operation + TransportDelete +) + +// Transport provides an interface for Transport implementations +// like RESTCONF or gnmi type Transport interface { Get(ctx context.Context, params ...string) (interface{}, error) - Set(ctx context.Context, params ...interface{}) (interface{}, error) + Set(ctx context.Context, params ...interface{}) error Subscribe(ctx context.Context, params ...string) error Type() string GetOptions() interface{} diff --git a/nucleus/util/path/translate.go b/nucleus/util/path/translate.go new file mode 100644 index 0000000000000000000000000000000000000000..e245344085d02130b5277334df9546aab98bc197 --- /dev/null +++ b/nucleus/util/path/translate.go @@ -0,0 +1,15 @@ +package path + +import ( + gpb "github.com/openconfig/gnmi/proto/gnmi" + "strings" +) + +// ToStrings translates a gNMI path to a slice of strings +func ToStrings(path *gpb.Path) []string { + elems := make([]string, len(path.Elem)) + for i, e := range path.Elem { + elems[i] = strings.Title(e.Name) + } + return elems +} diff --git a/nucleus/util/path/path_traversal.go b/nucleus/util/path/traverse.go similarity index 100% rename from nucleus/util/path/path_traversal.go rename to nucleus/util/path/traverse.go diff --git a/nucleus/util/path/path_traversal_test.go b/nucleus/util/path/traverse_test.go similarity index 100% rename from nucleus/util/path/path_traversal_test.go rename to nucleus/util/path/traverse_test.go diff --git a/test/integration/cmdIntegration_test.go b/test/integration/cmdIntegration_test.go index 91871180d5939dda833fa58f6ee5c3fc269bab16..d5596ab70dad335efd43246023c13a10859f5b6a 100644 --- a/test/integration/cmdIntegration_test.go +++ b/test/integration/cmdIntegration_test.go @@ -28,7 +28,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } - func testSetupIntegration() { if os.Getenv("GOSDN_LOG") == "nolog" { log.SetLevel(log.PanicLevel) @@ -86,7 +85,7 @@ func testSetupIntegration() { } } -func TestCliIntegration(t *testing.T) { +func TestCmdIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } @@ -154,7 +153,7 @@ func TestCliIntegration(t *testing.T) { hostname := guuid.New().String() if err := cli.HTTPGet( testAPIEndpoint, - "set", + "update", "address="+testAddress, "uuid="+did, "sbi="+cliSbi, diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go index 816888d6f77f857225f34d22b794017efe641629..ed6d78fd929ed1d8379ca0cc18bc11d2e65e5f83 100644 --- a/test/integration/nucleusIntegration_test.go +++ b/test/integration/nucleusIntegration_test.go @@ -23,13 +23,12 @@ func TestGnmi_SetIntegration(t *testing.T) { } type args struct { ctx context.Context - params []interface{} + params []string } tests := []struct { name string fields fields args args - want interface{} wantErr bool }{ { @@ -42,33 +41,28 @@ func TestGnmi_SetIntegration(t *testing.T) { }, args: args{ ctx: context.Background(), - params: []interface{}{&gnmi.Operation{}}, + params: []string{"/system/config/hostname", "ceos3000"}, }, - want: nil, wantErr: true, }, { name: "valid update", fields: fields{opt: opt}, args: args{ - ctx: context.Background(), - params: []interface{}{ - &gnmi.Operation{ - Type: "update", - Origin: "", - Target: "", - Path: []string{ - "system", - "config", - "hostname", - }, - Val: "ceos3000", - }, - }, + ctx: context.Background(), + params: []string{"/system/config/hostname", "ceos3000"}, }, - want: gnmiMessages["../proto/resp-set-system-config-hostname"], wantErr: false, }, + { + name: "invalid update", + fields: fields{opt: opt}, + args: args{ + ctx: context.Background(), + params: nil, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -77,21 +71,11 @@ func TestGnmi_SetIntegration(t *testing.T) { t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr) return } - resp, err := g.Set(tt.args.ctx, tt.args.params...) + err = g.Set(tt.args.ctx, tt.args.params) if (err != nil) != tt.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) return } - got, ok := resp.(*gpb.SetResponse) - if !ok { - t.Errorf("want: %v, got %v, error: %v", reflect.TypeOf(&gpb.SetResponse{}), reflect.TypeOf(resp), &nucleus.ErrInvalidTypeAssertion{}) - } - if err != nil && tt.wantErr { - return - } else if got.Prefix.Target != testAddress || - got.Response[0].Op != gpb.UpdateResult_UPDATE { - t.Errorf("Set() got = %v, want %v", got, tt.want) - } }) } }