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..a90be457ba4e72d9c1a9005fb81c6b9860410039 100644 --- a/cli/http.go +++ b/cli/http.go @@ -58,6 +58,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 9822234c73e01a5119746554b44198af1da233b9..c3595c3e3c9bf7cfe659b8fa65dff53fcf79a714 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module code.fbi.h-da.de/cocsn/gosdn go 1.14 require ( - code.fbi.h-da.de/cocsn/yang-models v0.0.4 + code.fbi.h-da.de/cocsn/yang-models v0.0.6 github.com/aristanetworks/goarista v0.0.0-20201120222254-94a892eb0c6a github.com/golang/protobuf v1.5.0 github.com/google/gnxi v0.0.0-20201221102247-c26672548161 @@ -11,8 +11,8 @@ require ( 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/openconfig/ygot v0.10.4 + github.com/sirupsen/logrus v1.8.0 github.com/spf13/cobra v1.1.1 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index df1bca3a02416ecdb2b8129aa30f24d7ceea3832..dfc84d46a711f66bbcaac6e485f4bdbc96f34d2b 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,10 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -code.fbi.h-da.de/cocsn/yang-models v0.0.4 h1:y/Ph5CaD2NJDPjoOuS59iKrMYf9bvpg2/hefC2RG6E4= -code.fbi.h-da.de/cocsn/yang-models v0.0.4/go.mod h1:7MnNmAQ9o84BpUepcaV6RB1mBGCNyXVJcdbKUl6rK0g= +code.fbi.h-da.de/cocsn/gosdn v0.0.1/go.mod h1:LK5EZWj/QPi+ueMptEB2MigvnS03Lky7p4UHoA+M7Ao= +code.fbi.h-da.de/cocsn/swagger/apis v0.0.0-20200924152423-61030cab7b88/go.mod h1:3jDZAOmepPl3kiukNGvPFkJEzgGczgrC3zJ2jcqVFSs= +code.fbi.h-da.de/cocsn/yang-models v0.0.6 h1:dqpxs/rx5shpQncmC4/Z97fkwRkRgoWBevzAPw2PYfo= +code.fbi.h-da.de/cocsn/yang-models v0.0.6/go.mod h1:VnKJrYR/CJrLhGfxFcZU2rpMQ6hosFC4q77/CJxpy+M= 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= @@ -35,17 +37,21 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/aristanetworks/fsnotify v1.4.2/go.mod h1:D/rtu7LpjYM8tRJphJ0hUBYpjai8SfX+aSNsWDTq/Ks= @@ -56,7 +62,10 @@ github.com/aristanetworks/splunk-hec-go v0.3.3/go.mod h1:1VHO9r17b0K7WmOlLb9nTk/ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -87,6 +96,7 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 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.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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= @@ -112,8 +122,12 @@ 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/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= 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/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= 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= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -124,14 +138,88 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V 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-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 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.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= +github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= +github.com/go-openapi/runtime v0.19.22/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= +github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= +github.com/go-openapi/validate v0.19.11/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= 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= @@ -190,6 +278,7 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf 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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -237,6 +326,7 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -247,10 +337,13 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= 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/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.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -261,6 +354,7 @@ github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -272,13 +366,25 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g= +github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -290,6 +396,7 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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= @@ -299,6 +406,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -333,10 +441,14 @@ github.com/openconfig/ygot v0.6.0/go.mod h1:o30svNf7O0xK+R35tlx95odkDmZWS9JyWWQS 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.4 h1:7rfvHEWFBYxFeVVK9UbMq6BJMAD7zeedidEUuxhcfaU= +github.com/openconfig/ygot v0.10.4/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= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -369,18 +481,24 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/tview v0.0.0-20200915114512-42866ecf6ca6/go.mod h1:xV4Aw4WIX8cmhg71U7MUHBdpIQ7zSEXdRruGHLaEAOc= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.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.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU= +github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= 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,6 +513,7 @@ 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 v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -421,18 +540,25 @@ 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= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 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= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -443,9 +569,13 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -487,6 +617,7 @@ golang.org/x/mod v0.2.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= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -494,6 +625,7 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -510,6 +642,8 @@ 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-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -523,6 +657,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -534,13 +669,18 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -558,6 +698,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20200817155316-9781c653f443/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= @@ -580,16 +721,22 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm 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-20190125232054-d66bd3c5d5a6/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= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -684,6 +831,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= @@ -712,6 +860,7 @@ 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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/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= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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..870af9eaeb787c8cbf6cb7de90bf2d9ea40e4870 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,50 @@ 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"), + } + } + 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 +139,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 +226,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 +242,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..bd1eeaf40a28bbc4a7edc2b1fea6bfbc4562519f 100644 --- a/nucleus/http_test.go +++ b/nucleus/http_test.go @@ -39,9 +39,6 @@ func testSetupHTTP() { } 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(), diff --git a/nucleus/initialise_test.go b/nucleus/initialise_test.go index 6c90b054e44d009e03542821ced09f46cd65bb49..9bcd8d270f625d05445c9a818e4eedbd086edf6a 100644 --- a/nucleus/initialise_test.go +++ b/nucleus/initialise_test.go @@ -25,6 +25,7 @@ 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 @@ -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..6a2da78f52ea9322b8290d5aa6d4d6261fe59def 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,72 @@ 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]: + 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 + } + close(pnd.errChans[u]) + 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 +159,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 +179,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 +266,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 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..8b4ff39d341034c9a38379884b29bfb50dfd5b04 100644 --- a/nucleus/principalNetworkDomain_test.go +++ b/nucleus/principalNetworkDomain_test.go @@ -522,3 +522,114 @@ 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: "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)) + } + } + }) + } +} 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 0aefff4db74566a9dc411cc76de8c4965766570a..bb57a774ce0a63ec970bbfa6c1e82f06159b81ec 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) diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go index 816888d6f77f857225f34d22b794017efe641629..54f310491d161b597762bc4028e5bbfebd07c8f1 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,31 +41,17 @@ func TestGnmi_SetIntegration(t *testing.T) { }, args: args{ ctx: context.Background(), - params: []interface{}{&gnmi.Operation{}}, + params: []string{}, }, - 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, }, } @@ -77,21 +62,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) - } }) } }