diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..2b83d667fb77b124088bc27b8727bc8007ac96e4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/gnmi"] + path = deps/gnmi + url = https://github.com/openconfig/gnmi.git diff --git a/README.md b/README.md index 4c1793d1791af28c539996fce3932ff96c1976cf..4ab492ad0b14584f7fc83dffc982492526d6b8a8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # CoCSN API -A simple RESTFUL API specification for goSDN RESTCONF operations. \ No newline at end of file +Protocol buffer definitions for goSDN gRPC API. To generate run the following command inside the repo root. + +´´´sh +protoc --proto_path=./proto --proto_path=./deps --go_out=./go --go-grpc_out=./go --go-grpc_opt=paths=source_relative --go_opt=paths=source_relative $(find proto | grep .proto) +´´´ diff --git a/deps/github.com/openconfig/gnmi/.github/workflows/ci-cpp-build-gnmi.yml b/deps/github.com/openconfig/gnmi/.github/workflows/ci-cpp-build-gnmi.yml new file mode 100644 index 0000000000000000000000000000000000000000..341e5ddc0077155334bac44e9124e19eabeff464 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/.github/workflows/ci-cpp-build-gnmi.yml @@ -0,0 +1,38 @@ +name: "bazel build" + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + env: + BAZEL: bazelisk-linux-amd64 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Mount bazel cache + uses: actions/cache@v2 + with: + # See https://docs.bazel.build/versions/master/output_directories.html + path: "~/.cache/bazel" + # Create a new cache entry whenever Bazel files change. + # See https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows + key: bazel-${{ runner.os }}-build-${{ hashFiles('**/*.bzl', '**/*.bazel') }} + restore-keys: | + bazel-${{ runner.os }}-build- + - name: Install bazelisk + run: | + curl -LO "https://github.com/bazelbuild/bazelisk/releases/download/v1.8.1/$BAZEL" + chmod +x $BAZEL + sudo mv $BAZEL /usr/local/bin/bazel + - name: Build + run: bazel build //... + diff --git a/deps/github.com/openconfig/gnmi/BUILD.bazel b/deps/github.com/openconfig/gnmi/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..ca5484e5365a2b49d22d0b0f23b624f8bcf3d880 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/BUILD.bazel @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Supporting infrastructure for implementing and testing PINS. + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +exports_files(["LICENSE"]) diff --git a/deps/github.com/openconfig/gnmi/CONTRIBUTING.md b/deps/github.com/openconfig/gnmi/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..d2f20ac744a61569982bec931cd8d2562b991c69 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution, +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to <https://cla.developers.google.com/> to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +accept GitHub pull requests, however all commits are made internally and +mirrored out. This is done so that our internal verification steps run before +any code is submitted. diff --git a/deps/github.com/openconfig/gnmi/LICENSE b/deps/github.com/openconfig/gnmi/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/deps/github.com/openconfig/gnmi/README.md b/deps/github.com/openconfig/gnmi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0e2ef19ac0b7c00501c0bd0d29adfd6ead95edd7 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/README.md @@ -0,0 +1,26 @@ +# gNMI - gRPC Network Management Interface + +This repository contains reference Go implementations for gNMI. + +**Note:** This is not an official Google product. + +The implementations include: + +- client library implementation using `gnmi.proto` +- CLI for interacting with a gNMI service +- Caching collector that connects to multiple gNMI targets and makes the data + available over a single gNMI Subscribe RPC to clients + +## Installing + +To install the CLI, run + + go get -u github.com/openconfig/gnmi/cmd/gnmi_cli + +## Client libraries + +The main entry point for using the client libraries is in +`github.com/openconfig/gnmi/client`. + +See [godoc pages](https://godoc.org/github.com/openconfig/gnmi/client) for +documentation and usage examples. diff --git a/deps/github.com/openconfig/gnmi/WORKSPACE.bazel b/deps/github.com/openconfig/gnmi/WORKSPACE.bazel new file mode 100644 index 0000000000000000000000000000000000000000..8d46c70992727e702ba2dde3042806fe17481b1e --- /dev/null +++ b/deps/github.com/openconfig/gnmi/WORKSPACE.bazel @@ -0,0 +1,45 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +workspace(name = "gnmi") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "io_bazel_rules_go", + sha256 = "69de5c704a05ff37862f7e0f5534d4f479418afc21806c887db544a316f3cb6b", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz", + "https://github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz", + ], +) + +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") + +go_rules_dependencies() + +go_register_toolchains(version = "1.16") + +# -- Load Dependencies --------------------------------------------------------- +load("gnmi_deps.bzl", "gnmi_deps") + +gnmi_deps() + +load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") + +grpc_deps() + +load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") + +grpc_extra_deps() diff --git a/deps/github.com/openconfig/gnmi/cache/cache.go b/deps/github.com/openconfig/gnmi/cache/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..c00c5ccb7ac623d8ae9fd42931b42add0afbea61 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cache/cache.go @@ -0,0 +1,665 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package cache is a tree-based cache of timestamped state provided from +// one or more gNMI targets. It accepts updates from the target(s) to +// refresh internal values that are made available to clients via subscriptions. +package cache + +import ( + "encoding/json" + "errors" + "fmt" + "sync" + "time" + + log "github.com/golang/glog" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/ctree" + "github.com/openconfig/gnmi/errlist" + "github.com/openconfig/gnmi/latency" + "github.com/openconfig/gnmi/metadata" + "github.com/openconfig/gnmi/path" + "github.com/openconfig/gnmi/value" + + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +// T provides a shorthand function to reference a timestamp with an +// int64 (nanoseconds since epoch). +func T(n int64) time.Time { return time.Unix(0, n) } + +// A Target hosts an indexed cache of state for a single target. +type Target struct { + name string // name of the target + t *ctree.Tree // actual cache of target data + client func(*ctree.Leaf) // Function to pass all cache updates to. + sync bool // denotes whether this cache is in sync with target + meta *metadata.Metadata // metadata associated with target + lat *latency.Latency // latency measurements + tsmu sync.Mutex // protects latest timestamp + ts time.Time // latest timestamp for an update +} + +// options contains options for creating a Cache. +type options struct { + // latencyWindows is a list of time windows for which latency stats will be + // calculated and exported as metadata. + latencyWindows []time.Duration + avgLatencyPrecision time.Duration +} + +// Option defines the function prototype to set options for creating a Cache. +type Option func(*options) + +// WithLatencyWindows returns an Option to set latency windows for which +// latency stats are calculated and exported for each target in Cache. +// metaUpdatePeriod is the period for updating target metadata. The latency +// windows need to be multiples of this period. +func WithLatencyWindows(ws []string, metaUpdatePeriod time.Duration) (Option, error) { + if metaUpdatePeriod.Nanoseconds() == 0 { + return nil, nil // disable latency stats if updatePeriod is 0 + } + windows, err := latency.ParseWindows(ws, metaUpdatePeriod) + if err != nil { + return nil, err + } + return func(o *options) { + o.latencyWindows = windows + }, nil +} + +// WithAvgLatencyPrecision returns an Option to set the precision of average +// latency stats calculated in Cache. +func WithAvgLatencyPrecision(avgLatencyPrecision time.Duration) Option { + return func(o *options) { + o.avgLatencyPrecision = avgLatencyPrecision + } +} + +// Cache is a structure holding state information for multiple targets. +type Cache struct { + opts options + mu sync.RWMutex + targets map[string]*Target // Map of per target caches. + client func(*ctree.Leaf) // Function to pass all cache updates to. +} + +// New creates a new instance of Cache that receives target updates from the +// translator and provides an interface to service client queries. +func New(targets []string, opts ...Option) *Cache { + c := &Cache{ + targets: make(map[string]*Target, len(targets)), + client: func(*ctree.Leaf) {}, + } + for _, opt := range opts { + if opt != nil { + opt(&c.opts) + } + } + latency.RegisterMetadata(c.opts.latencyWindows) + + for _, t := range targets { + c.Add(t) + } + return c +} + +// LatencyWindows returns the latency windows supported by the cache. +func (c *Cache) LatencyWindows() []time.Duration { + return c.opts.latencyWindows +} + +// SetClient registers a callback function to receive calls for each update +// accepted by the cache. This call should be made prior to sending any updates +// into the cache, just after initialization. +func (c *Cache) SetClient(client func(*ctree.Leaf)) { + c.mu.Lock() + defer c.mu.Unlock() + c.client = client + for _, t := range c.targets { + t.client = client + } +} + +// Metadata returns the per-target metadata structures. +func (c *Cache) Metadata() map[string]*metadata.Metadata { + md := map[string]*metadata.Metadata{} + defer c.mu.RUnlock() + c.mu.RLock() + for target, cache := range c.targets { + md[target] = cache.meta + } + return md +} + +// UpdateMetadata copies the current metadata for each target cache to the +// metadata path within each target cache. +func (c *Cache) UpdateMetadata() { + c.updateCache((*Target).updateMeta) +} + +// ConnectError updates the target's metadata with the provided error. +func (c *Cache) ConnectError(name string, err error) { + if target := c.GetTarget(name); target != nil { + target.connectError(err) + } +} + +// connectError updates the ConnectError in the cached metadata. +func (t *Target) connectError(err error) { + if err := t.GnmiUpdate(metaNotiStr(t.name, metadata.ConnectError, err.Error())); err != nil { + log.Errorf("target %q got error during meta connect error update, %v", t.name, err) + } +} + +// UpdateSize computes the size of each target cache and updates the size +// metadata reported within the each target cache. +func (c *Cache) UpdateSize() { + c.updateCache((*Target).updateSize) +} + +// GetTarget returns the Target from the cache corresponding to the target name. +func (c *Cache) GetTarget(target string) *Target { + defer c.mu.RUnlock() + c.mu.RLock() + return c.targets[target] +} + +// HasTarget reports whether the specified target exists in the cache or a glob +// (*) is passed which will match any target (even if no targets yet exist). +func (c *Cache) HasTarget(target string) bool { + switch target { + case "": + return false + case "*": + return true + default: + defer c.mu.RUnlock() + c.mu.RLock() + return c.targets[target] != nil + } +} + +// Query calls the specified callback for all results matching the query. All +// values passed to fn are client.Notification. +func (c *Cache) Query(target string, query []string, fn ctree.VisitFunc) error { + switch { + case target == "": + return errors.New("no target specified in query") + case target == "*": + defer c.mu.RUnlock() + c.mu.RLock() + // Run the query sequentially for each target cache. + for _, target := range c.targets { + if err := target.t.Query(query, fn); err != nil { + return err + } + } + default: + dc := c.GetTarget(target) + if dc == nil { + return fmt.Errorf("target %q not found in cache", target) + } + return dc.t.Query(query, fn) + } + return nil +} + +// Add reserves space in c to receive updates for the specified target. +func (c *Cache) Add(target string) *Target { + defer c.mu.Unlock() + c.mu.Lock() + var latOpts *latency.Options + if c.opts.avgLatencyPrecision.Nanoseconds() != 0 { + latOpts = &latency.Options{AvgPrecision: c.opts.avgLatencyPrecision} + } + t := &Target{ + t: &ctree.Tree{}, + name: target, + meta: metadata.New(), + client: c.client, + lat: latency.New(c.opts.latencyWindows, latOpts), + } + c.targets[target] = t + return t +} + +// Reset clears the cache for a target once a connection is resumed after +// having been lost. +func (c *Cache) Reset(target string) { + defer c.mu.RUnlock() + c.mu.RLock() + if t := c.targets[target]; t != nil { + t.Reset() + } +} + +// Remove removes the space in c corresponding to the specified target. +func (c *Cache) Remove(target string) { + defer c.mu.Unlock() + c.mu.Lock() + delete(c.targets, target) + // Notify clients that the target is removed. + c.client(ctree.DetachedLeaf(deleteNoti(target, "", []string{"*"}))) +} + +// Sync creates an internal gnmi.Notification with metadata/sync path +// to set the state to true for the specified target. +func (c *Cache) Sync(name string) { + if target := c.GetTarget(name); target != nil { + target.Sync() + } +} + +// Sync creates an internal gnmi.Notification with metadata/sync path +// to set the state to true for the specified target. +func (t *Target) Sync() { + if err := t.GnmiUpdate(metaNotiBool(t.name, metadata.Sync, true)); err != nil { + log.Errorf("target %q got error during meta sync update, %v", t.name, err) + } +} + +// Connect creates an internal gnmi.Notification for metadata/connected path +// to set the state to true for the specified target. +func (c *Cache) Connect(name string) { + if target := c.GetTarget(name); target != nil { + target.Connect() + } +} + +// Connect creates an internal gnmi.Notification for metadata/connected path +// to set the state to true for the specified target, and clear connectErr. +func (t *Target) Connect() { + if err := t.GnmiUpdate(metaNotiBool(t.name, metadata.Connected, true)); err != nil { + log.Errorf("target %q got error during meta connected update, %v", t.name, err) + } + + if err := t.GnmiUpdate(deleteNoti(t.name, "", metadata.Path(metadata.ConnectError))); err != nil { + log.Errorf("target %q got error during meta connectError update, %v", t.name, err) + } +} + +// GnmiUpdate sends a pb.Notification into the cache. +// If the notification has multiple Updates/Deletes, +// each individual Update/Delete is sent to cache as +// a separate gnmi.Notification. +func (c *Cache) GnmiUpdate(n *pb.Notification) error { + if n == nil { + return errors.New("gnmi.Notification is nil") + } + if n.GetPrefix() == nil { + return errors.New("gnmi.Notification prefix is nil") + } + target := c.GetTarget(n.GetPrefix().GetTarget()) + if target == nil { + return fmt.Errorf("target %q not found in cache", n.GetPrefix().GetTarget()) + } + return target.GnmiUpdate(n) +} + +// GnmiUpdate sends a pb.Notification into the target cache. +// If the notification has multiple Updates/Deletes, +// each individual Update/Delete is sent to cache as +// a separate gnmi.Notification. +func (t *Target) GnmiUpdate(n *pb.Notification) error { + t.checkTimestamp(T(n.GetTimestamp())) + switch { + // Store atomic notifications as a single leaf in the tree. + case n.Atomic: + if len(n.GetDelete()) > 0 { + return errors.New("atomic deletes unsupported") + } + l := len(n.GetUpdate()) + if l == 0 { + t.meta.AddInt(metadata.EmptyCount, 1) + return nil + } + nd, err := t.gnmiUpdate(n) + if err != nil { + return err + } + if nd != nil { + t.meta.AddInt(metadata.UpdateCount, int64(l)) + t.client(nd) + } + + // Break non-atomic complex notifications into individual leaves per update. + case len(n.GetUpdate())+len(n.GetDelete()) > 1: + updates := n.GetUpdate() + deletes := n.GetDelete() + n.Update, n.Delete = nil, nil + // restore back the notification updates and deletes + defer func() { + n.Update = updates + n.Delete = deletes + }() + errs := &errlist.List{} + for _, u := range updates { + noti := proto.Clone(n).(*pb.Notification) + noti.Update = []*pb.Update{u} + nd, err := t.gnmiUpdate(noti) + if err != nil { + errs.Add(err) + continue + } + if nd != nil { + t.meta.AddInt(metadata.UpdateCount, 1) + t.client(nd) + } + } + + for _, d := range deletes { + noti := proto.Clone(n).(*pb.Notification) + noti.Delete = []*pb.Path{d} + t.meta.AddInt(metadata.UpdateCount, 1) + for _, nd := range t.gnmiRemove(noti) { + t.client(nd) + } + } + return errs.Err() + + // Single update notification could be handled by the above code but is + // handled separately to avoid the unnecessary proto.Clone call. + case len(n.GetUpdate()) == 1: + nd, err := t.gnmiUpdate(n) + if err != nil { + return err + } + if nd != nil { + t.meta.AddInt(metadata.UpdateCount, 1) + t.client(nd) + } + + // Single delete notification also avoids proto.Clone above. + case len(n.GetDelete()) == 1: + t.meta.AddInt(metadata.UpdateCount, 1) + for _, nd := range t.gnmiRemove(n) { + t.client(nd) + } + + // Empty notification. + default: + t.meta.AddInt(metadata.EmptyCount, 1) + } + return nil +} + +func (t *Target) checkTimestamp(ts time.Time) { + // Locking ensures that d.ts is always increasing regardless of the order in + // which updates are processed in parallel by multiple goroutines. + defer t.tsmu.Unlock() + t.tsmu.Lock() + // Track latest timestamp for a target. + if ts.After(t.ts) { + t.ts = ts + } +} + +func (t *Target) gnmiUpdate(n *pb.Notification) (*ctree.Leaf, error) { + realData := true + suffix := n.Update[0].Path + // If the notification is an atomic group of updates, store them under the prefix only. + if n.Atomic { + suffix = nil + } + path := joinPrefixAndPath(n.Prefix, suffix) + if path[0] == metadata.Root { + realData = false + u := n.Update[0] + switch path[1] { + case metadata.Sync: + var ok bool + tv, ok := u.Val.Value.(*pb.TypedValue_BoolVal) + if !ok { + return nil, fmt.Errorf("%v : has value %v of type %T, expected boolean", metadata.Path(metadata.Sync), u.Val, u.Val) + } + t.sync = tv.BoolVal + t.meta.SetBool(metadata.Sync, t.sync) + case metadata.Connected: + tv, ok := u.Val.Value.(*pb.TypedValue_BoolVal) + if !ok { + return nil, fmt.Errorf("%v : has value %v of type %T, expected boolean", metadata.Path(metadata.Connected), u.Val, u.Val) + } + t.meta.SetBool(metadata.Connected, tv.BoolVal) + case metadata.ConnectedAddr, metadata.ConnectError: + tv, ok := u.Val.Value.(*pb.TypedValue_StringVal) + if !ok { + return nil, fmt.Errorf("%v : has value %v of type %T, expected string", metadata.Path(path[1]), u.Val, u.Val) + } + t.meta.SetStr(path[1], tv.StringVal) + } + } + // Update an existing leaf. + if oldval := t.t.GetLeaf(path); oldval != nil { + // An update with corrupt data is possible to visit a node that does not + // contain *pb.Notification. Thus, need type assertion here. + old, ok := oldval.Value().(*pb.Notification) + if !ok { + return nil, fmt.Errorf("corrupt schema with collision for path %q, got %T", path, oldval.Value()) + } + if !T(old.GetTimestamp()).Before(T(n.GetTimestamp())) { + // Update rejected. Timestamp <= previous recorded timestamp. + t.meta.AddInt(metadata.StaleCount, 1) + return nil, errors.New("update is stale") + } + oldval.Update(n) + // Simulate event-driven for all non-atomic updates. + if !n.Atomic && value.Equal(old.Update[0].Val, n.Update[0].Val) { + t.meta.AddInt(metadata.SuppressedCount, 1) + return nil, nil + } + // Compute latency for updated leaves. + if t.sync && realData { + // Record latency for post-sync target updates. Exclude metadata updates. + t.lat.Compute(T(n.GetTimestamp())) + } + return oldval, nil + } + // Add a new leaf. + if err := t.t.Add(path, n); err != nil { + return nil, err + } + if realData { + t.meta.AddInt(metadata.LeafCount, 1) + t.meta.AddInt(metadata.AddCount, 1) + // Compute latency for new leaves. + if t.sync { + // Record latency for post-sync target updates. Exclude metadata updates. + t.lat.Compute(T(n.GetTimestamp())) + } + } + return t.t.GetLeaf(path), nil +} + +func (t *Target) gnmiRemove(n *pb.Notification) []*ctree.Leaf { + path := joinPrefixAndPath(n.Prefix, n.Delete[0]) + if path[0] == metadata.Root { + t.meta.ResetEntry(path[1]) + } + leaves := t.t.DeleteConditional(path, func(v interface{}) bool { return v.(*pb.Notification).GetTimestamp() < n.GetTimestamp() }) + if len(leaves) == 0 { + return nil + } + deleted := int64(len(leaves)) + t.meta.AddInt(metadata.LeafCount, -deleted) + t.meta.AddInt(metadata.DelCount, deleted) + var ls []*ctree.Leaf + for _, l := range leaves { + noti := &pb.Notification{ + Timestamp: n.GetTimestamp(), + Prefix: &pb.Path{Target: n.GetPrefix().GetTarget()}, + Delete: []*pb.Path{{Element: l}}, + } + ls = append(ls, ctree.DetachedLeaf(noti)) + } + return ls +} + +// updateCache calls fn for each Target. +func (c *Cache) updateCache(fn func(*Target, func(*ctree.Leaf))) { + defer c.mu.RUnlock() + c.mu.RLock() + for _, target := range c.targets { + fn(target, c.client) + } +} + +// updateSize walks the entire tree of the target, sums up marshaled sizes of +// all leaves and writes the sum in metadata. +func (t *Target) updateSize(func(*ctree.Leaf)) { + var s int64 + size := func(n interface{}) int64 { + buf, err := json.Marshal(n) + if err != nil { + return 0 + } + return int64(len(buf)) + } + t.t.Query([]string{"*"}, + func(_ []string, _ *ctree.Leaf, v interface{}) error { + s += size(v) + return nil + }) + t.meta.SetInt(metadata.Size, s) +} + +// updateMeta updates the metadata values in the cache. +func (t *Target) updateMeta(clients func(*ctree.Leaf)) { + t.tsmu.Lock() + latest := t.ts + t.tsmu.Unlock() + t.meta.SetInt(metadata.LatestTimestamp, latest.UnixNano()) + + t.lat.UpdateReset(t.meta) + for value := range metadata.TargetBoolValues { + v, err := t.meta.GetBool(value) + if err != nil { + continue + } + path := metadata.Path(value) + prev := t.t.GetLeafValue(path) + if prev == nil || prev.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_BoolVal).BoolVal != v { + noti := metaNotiBool(t.name, value, v) + if n, _ := t.gnmiUpdate(noti); n != nil { + if clients != nil { + clients(n) + } + } + } + } + + for value := range metadata.TargetIntValues { + v, err := t.meta.GetInt(value) + if err != nil { + continue + } + path := metadata.Path(value) + prev := t.t.GetLeafValue(path) + if prev == nil || prev.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_IntVal).IntVal != v { + noti := metaNotiInt(t.name, value, v) + if n, _ := t.gnmiUpdate(noti); n != nil { + if clients != nil { + clients(n) + } + } + } + } + + for value := range metadata.TargetStrValues { + v, err := t.meta.GetStr(value) + if err != nil { + continue + } + path := metadata.Path(value) + prev := t.t.GetLeafValue(path) + if prev == nil || prev.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_StringVal).StringVal != v { + noti := metaNotiStr(t.name, value, v) + if n, _ := t.gnmiUpdate(noti); n != nil { + if clients != nil { + clients(n) + } + } + } + } +} + +// Reset clears the Target of stale data upon a reconnection and notifies +// cache client of the removal. +func (t *Target) Reset() { + // Reset metadata to zero values (e.g. connected = false) and notify clients. + t.meta.Clear() + t.updateMeta(t.client) + for root := range t.t.Children() { + if root == metadata.Root { + continue + } + t.t.Delete([]string{root}) + t.client(ctree.DetachedLeaf(deleteNoti(t.name, root, []string{"*"}))) + } +} + +func joinPrefixAndPath(pr, ph *pb.Path) []string { + // <target> and <origin> are only valid as prefix gnmi.Path + // https://github.com/openconfig/reference/blob/master/rpc/gnmi-specification.md#222-paths + p := path.ToStrings(pr, true) + p = append(p, path.ToStrings(ph, false)...) + // remove the prepended target name + p = p[1:] + return p +} + +func deleteNoti(t, o string, p []string) *pb.Notification { + pe := make([]*pb.PathElem, 0, len(p)) + for _, e := range p { + pe = append(pe, &pb.PathElem{Name: e}) + } + return &pb.Notification{ + Timestamp: time.Now().UnixNano(), + Prefix: &pb.Path{Target: t, Origin: o}, + Delete: []*pb.Path{&pb.Path{Elem: pe}}, + } +} + +func metaNoti(t, m string, v *pb.TypedValue) *pb.Notification { + mp := metadata.Path(m) + pe := make([]*pb.PathElem, 0, len(mp)) + for _, p := range mp { + pe = append(pe, &pb.PathElem{Name: p}) + } + return &pb.Notification{ + Timestamp: time.Now().UnixNano(), + Prefix: &pb.Path{Target: t}, + Update: []*pb.Update{ + &pb.Update{ + Path: &pb.Path{Elem: pe}, + Val: v, + }, + }, + } +} + +func metaNotiBool(t, m string, v bool) *pb.Notification { + return metaNoti(t, m, &pb.TypedValue{Value: &pb.TypedValue_BoolVal{v}}) +} + +func metaNotiInt(t, m string, v int64) *pb.Notification { + return metaNoti(t, m, &pb.TypedValue{Value: &pb.TypedValue_IntVal{v}}) +} + +func metaNotiStr(t, m string, v string) *pb.Notification { + return metaNoti(t, m, &pb.TypedValue{Value: &pb.TypedValue_StringVal{v}}) +} diff --git a/deps/github.com/openconfig/gnmi/cache/cache_test.go b/deps/github.com/openconfig/gnmi/cache/cache_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a96eb8724444e269345f72aff70646bc38978780 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cache/cache_test.go @@ -0,0 +1,1268 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "errors" + "fmt" + "math/rand" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/ctree" + "github.com/openconfig/gnmi/errdiff" + "github.com/openconfig/gnmi/latency" + "github.com/openconfig/gnmi/metadata" + "github.com/openconfig/gnmi/value" + + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +func TestHasTarget(t *testing.T) { + c := New(nil) + for _, tt := range []string{"", "dev1", "dev2", "dev3"} { + if c.HasTarget(tt) { + t.Errorf("HasTarget(%q) got true for empty cache, want false", tt) + } + } + if !c.HasTarget("*") { + t.Error("HasTarget(*) got false for empty cache, want true") + } + c = New([]string{"dev1", "dev2"}) + for tt, want := range map[string]bool{"": false, "*": true, "dev1": true, "dev2": true, "dev3": false} { + if got := c.HasTarget(tt); got != want { + t.Errorf("HasTarget(%q) got %t, want %t", tt, got, want) + } + } +} + +func TestAdd(t *testing.T) { + c := New(nil) + c.Add("dev1") + if !c.HasTarget("dev1") { + t.Error("dev1 not added") + } +} + +func TestRemove(t *testing.T) { + tg := "dev1" + c := New([]string{tg}) + var got interface{} + c.SetClient(func(l *ctree.Leaf) { + got = l.Value() + }) + c.Remove(tg) + if got == nil { + t.Fatalf("no update was received") + } + if c.HasTarget("dev1") { + t.Errorf("dev1 not deleted") + } + noti, ok := got.(*pb.Notification) + if !ok { + t.Fatalf("got %T, want *pb.Notification type", got) + } + if noti.Prefix.GetTarget() != tg { + t.Errorf("got %q, want %q", noti.Prefix.GetTarget(), tg) + } + if len(noti.Delete) != 1 { + t.Fatalf("got %d, want 1 delete update in notification", len(noti.Delete)) + } + d := noti.Delete[0] + if len(d.Elem) != 1 { + t.Fatalf("got %d, want 1 elems in delete notificatin path", len(d.Elem)) + } + if p := d.Elem[0].Name; p != "*" { + t.Errorf("got %q, want %q in target delete path", p, "*") + } +} + +func TestResetUnknown(t *testing.T) { + c := New([]string{}) + c.Reset("dev1") + if c.HasTarget("dev1") { + t.Error("c.Reset created a target that didn't exist before") + } +} + +func TestMetadata(t *testing.T) { + targets := []string{"dev1", "dev2", "dev3"} + c := New(targets) + md := c.Metadata() + if got, want := len(md), len(targets); got != want { + t.Errorf("Metadata got %d targets, want %d", got, want) + } +} + +func TestMetadataStale(t *testing.T) { + c := New([]string{"dev1"}) + for i := 0; i < 10; i++ { + n := gnmiNotification("dev1", []string{"prefix", "path"}, []string{"update", "a", "b", "c"}, 0, "", false) + c.GnmiUpdate(n) + c.GetTarget("dev1").updateMeta(nil) + path := metadata.Path(metadata.StaleCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + staleCount := v.(*pb.Notification).Update[0].Val.GetIntVal() + if staleCount != int64(i) { + t.Errorf("got staleCount = %d, want %d", staleCount, i) + } + return nil + }) + path = metadata.Path(metadata.UpdateCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + updates := v.(*pb.Notification).Update[0].Val.GetIntVal() + if updates != 1 { + t.Errorf("got updates %d, want 1", updates) + } + return nil + }) + } +} + +func TestGNMIUpdateIntermediateUpdate(t *testing.T) { + c := New([]string{"dev1"}) + // Initialize a cache tree for next GnmiUpdate test. + n := gnmiNotification("dev1", []string{"prefix", "path"}, []string{"update", "a", "b", "c"}, 0, "", false) + if err := c.GnmiUpdate(n); err != nil { + t.Fatalf("GnmiUpdate(%+v): got %v, want nil error", n, err) + } + // This is a negative test case for invalid path in an update. + // For a cache tree initialized above with path "a"/"b"/"c", "a" is a non-leaf node. + // Because non-leaf node "a" does not contain notification, error should be returned in following update. + n = gnmiNotification("dev1", []string{"prefix", "path"}, []string{"update", "a"}, 0, "", false) + err := c.GnmiUpdate(n) + if diff := errdiff.Substring(err, "corrupt schema with collision"); diff != "" { + t.Errorf("GnmiUpdate(%+v): %v", n, diff) + } +} + +func TestMetadataSuppressed(t *testing.T) { + c := New([]string{"dev1"}) + // Unique values not suppressed. + for i := 0; i < 10; i++ { + c.GnmiUpdate(gnmiNotification("dev1", []string{"prefix", "path"}, []string{"update", "path"}, int64(i), strconv.Itoa(i), false)) + c.GetTarget("dev1").updateMeta(nil) + path := metadata.Path(metadata.SuppressedCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + suppressedCount := v.(*pb.Notification).Update[0].Val.GetIntVal() + if suppressedCount != 0 { + t.Errorf("got suppressedCount = %d, want 0", suppressedCount) + } + return nil + }) + path = metadata.Path(metadata.UpdateCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + updates := v.(*pb.Notification).Update[0].Val.GetIntVal() + if updates != int64(i+1) { + t.Errorf("got updates %d, want %d", updates, i) + } + return nil + }) + } + c.Reset("dev1") + // Duplicate values suppressed. + for i := 0; i < 10; i++ { + c.GnmiUpdate(gnmiNotification("dev1", []string{"prefix", "path"}, []string{"update", "path"}, int64(i), "same value", false)) + c.GetTarget("dev1").updateMeta(nil) + path := metadata.Path(metadata.SuppressedCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + suppressedCount := v.(*pb.Notification).Update[0].Val.GetIntVal() + if suppressedCount != int64(i) { + t.Errorf("got suppressedCount = %d, want %d", suppressedCount, i) + } + return nil + }) + path = metadata.Path(metadata.UpdateCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + updates := v.(*pb.Notification).Update[0].Val.GetIntVal() + if updates != 1 { + t.Errorf("got updates %d, want 1", updates) + } + return nil + }) + } +} + +func TestMetadataLatency(t *testing.T) { + window := 2 * time.Second + opt, _ := WithLatencyWindows([]string{"2s"}, 2*time.Second) + c := New([]string{"dev1"}, opt) + for _, path := range [][]string{ + latency.Path(window, latency.Avg), + latency.Path(window, latency.Max), + latency.Path(window, latency.Min), + } { + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + t.Errorf("%s exists when device not in sync", strings.Join(path, "/")) + return nil + }) + } + timestamp := time.Now().Add(-time.Minute).UnixNano() + c.GnmiUpdate(gnmiNotification("dev1", nil, metadata.Path(metadata.Sync), timestamp, true, false)) + c.GnmiUpdate(gnmiNotification("dev1", nil, []string{"a", "1"}, timestamp, "b", false)) + c.GetTarget("dev1").updateMeta(nil) + for _, path := range [][]string{ + latency.Path(window, latency.Avg), + latency.Path(window, latency.Max), + latency.Path(window, latency.Min), + } { + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + l := v.(*pb.Notification).Update[0].Val.GetIntVal() + if want := time.Minute.Nanoseconds(); l < want { + t.Errorf("%s got value %d, want greater than %d", + strings.Join(path, "/"), l, want) + } + return nil + }) + } +} + +func TestUpdateMetadata(t *testing.T) { + c := New([]string{"dev1"}) + c.UpdateMetadata() + want := [][]string{ + {metadata.Root, metadata.LatestTimestamp}, + {metadata.Root, metadata.LeafCount}, + {metadata.Root, metadata.AddCount}, + {metadata.Root, metadata.UpdateCount}, + {metadata.Root, metadata.DelCount}, + {metadata.Root, metadata.EmptyCount}, + {metadata.Root, metadata.StaleCount}, + {metadata.Root, metadata.SuppressedCount}, + {metadata.Root, metadata.Connected}, + {metadata.Root, metadata.ConnectedAddr}, + {metadata.Root, metadata.Sync}, + {metadata.Root, metadata.Size}, + } + var got [][]string + c.Query("dev1", []string{metadata.Root}, func(path []string, _ *ctree.Leaf, _ interface{}) error { + got = append(got, path) + return nil + }) + sort.Slice(got, func(i, j int) bool { return less(got[i], got[j]) }) + sort.Slice(want, func(i, j int) bool { return less(want[i], want[j]) }) + if !reflect.DeepEqual(got, want) { + t.Errorf("got update paths: %q\n want: %q", got, want) + } +} + +func TestUpdateSize(t *testing.T) { + c := New([]string{"dev1"}) + c.GnmiUpdate(gnmiNotification("dev1", nil, []string{"a", "1"}, 0, string(make([]byte, 1000)), false)) + c.UpdateSize() + c.UpdateMetadata() + var val int64 + c.Query("dev1", []string{metadata.Root, metadata.Size}, func(_ []string, _ *ctree.Leaf, v interface{}) error { + t.Logf("%v", v) + val = v.(*pb.Notification).Update[0].Val.GetIntVal() + return nil + }) + if val <= 1000 { + t.Errorf("got size of %d want > 1000", val) + } +} + +func TestConnectError(t *testing.T) { + want := "test error" + c := New([]string{"dev1"}) + c.ConnectError("dev1", fmt.Errorf(want)) + c.UpdateMetadata() + var got string + c.Query("dev1", []string{metadata.Root, metadata.ConnectError}, func(_ []string, _ *ctree.Leaf, v interface{}) error { + got = v.(*pb.Notification).Update[0].Val.GetStringVal() + return nil + }) + if got != want { + t.Errorf("got connect error %q; want %q", got, want) + } +} + +type updateQueryData struct { + targets []string + paths [][]string + values []*string +} + +func sendUpdates(u updateQueryData, c *Cache, n int, wg *sync.WaitGroup) { + r := rand.New(rand.NewSource(5)) + for i := 0; i < n; i++ { + target := u.targets[r.Intn(len(u.targets))] + path := append([]string{target}, u.paths[r.Intn(len(u.paths))]...) + val, _ := value.FromScalar(u.values[r.Intn(len(u.values))]) + c.GnmiUpdate(&pb.Notification{ + Update: []*pb.Update{{ + Path: &pb.Path{Element: path}, + Val: val, + }}, + Timestamp: int64(n), + }) + } +} + +func makeQueries(u updateQueryData, c *Cache, n int, wg *sync.WaitGroup) { + r := rand.New(rand.NewSource(30)) + for i := 0; i < n; i++ { + target := u.targets[r.Intn(len(u.targets))] + path := u.paths[r.Intn(len(u.paths))] + c.Query(target, path, func([]string, *ctree.Leaf, interface{}) error { return nil }) + } + wg.Done() +} + +func createPaths(l, n int) [][]string { + var paths [][]string + if l == 0 { + for i := 0; i < n; i++ { + paths = append(paths, []string{fmt.Sprintf("level_%d_node_%d", l, i)}) + } + return paths + } + subpaths := createPaths(l-1, n) + for i := 0; i < n; i++ { + for _, p := range subpaths { + paths = append(paths, append([]string{fmt.Sprintf("level_%d_node_%d", l, i)}, p...)) + } + } + return paths +} + +func BenchmarkParallelUpdateQuery(b *testing.B) { + u := updateQueryData{ + targets: []string{"dev1", "dev2", "dev3"}, + paths: createPaths(5, 10), + values: []*string{nil}, + } + for v := 0; v < 100; v++ { + s := fmt.Sprintf("value_%d", v) + u.values = append(u.values, &s) + } + procs := runtime.GOMAXPROCS(0) + c := New(u.targets) + var wg sync.WaitGroup + b.ResetTimer() + for p := 0; p < procs; p++ { + wg.Add(2) + go sendUpdates(u, c, b.N, &wg) + go makeQueries(u, c, b.N, &wg) + } + wg.Wait() +} + +func gnmiNotification(dev string, prefix []string, path []string, ts int64, val interface{}, delete bool) *pb.Notification { + return notificationBundle(dev, prefix, ts, []update{ + { + delete: delete, + path: path, + val: val, + }}) +} + +type update struct { + delete bool + path []string + val interface{} +} + +func notificationBundle(dev string, prefix []string, ts int64, updates []update) *pb.Notification { + n := &pb.Notification{ + Prefix: &pb.Path{Element: prefix, Target: dev}, + Timestamp: ts, + } + for _, u := range updates { + if u.delete { + n.Delete = append(n.Delete, &pb.Path{Element: u.path}) + } else { + val, err := value.FromScalar(u.val) + if err != nil { + panic(fmt.Sprintf("notificationBundle cannot convert val - dev: %q, prefix: %q, ts: %d, update: %+v : %v", dev, prefix, ts, u, err)) + } + n.Update = append(n.Update, &pb.Update{ + Path: &pb.Path{ + Element: u.path, + }, + Val: val, + }) + } + } + return n +} + +func TestGNMIQuery(t *testing.T) { + c := New([]string{"dev1"}) + c.Query("", nil, func([]string, *ctree.Leaf, interface{}) error { + t.Error("querying without a target invoked callback") + return nil + }) + updates := []struct { + t *pb.Notification + q bool + v string + }{ + // This update is inserted here, but deleted below. + {gnmiNotification("dev1", []string{}, []string{"a", "e"}, 0, "value1", false), true, ""}, + // This update is ovewritten below. + {gnmiNotification("dev1", []string{}, []string{"a", "b"}, 0, "value1", false), true, "value3"}, + // This update is inserted and not modified. + {gnmiNotification("dev1", []string{}, []string{"a", "c"}, 0, "value4", false), true, "value4"}, + // This update overwrites a previous above. + {gnmiNotification("dev1", []string{}, []string{"a", "b"}, 1, "value3", false), true, "value3"}, + // These two targets don't exist in the cache and the updates are rejected. + {gnmiNotification("dev2", []string{}, []string{"a", "b"}, 0, "value1", false), false, ""}, + {gnmiNotification("dev3", []string{}, []string{"a", "b"}, 0, "value2", false), false, ""}, + // This is a delete that removes the first update, above. + {gnmiNotification("dev1", []string{}, []string{"a", "e"}, 1, "", true), true, ""}, + } + // Add updates to cache. + for _, tt := range updates { + c.GnmiUpdate(tt.t) + } + // Run queries over the inserted updates. + for x, tt := range updates { + target := tt.t.GetPrefix().GetTarget() + var gp *pb.Path + if tt.t.Update != nil { + gp = tt.t.Update[0].GetPath() + } else { + gp = tt.t.Delete[0] + } + p := joinPrefixAndPath(tt.t.GetPrefix(), gp) + if r := c.HasTarget(target); r != tt.q { + t.Errorf("#%d: got %v, want %v", x, r, tt.q) + } + var results []interface{} + appendResults := func(_ []string, _ *ctree.Leaf, val interface{}) error { results = append(results, val); return nil } + c.Query(target, p, appendResults) + if len(results) != 1 { + if tt.v != "" { + t.Errorf("Query(%s, %v, ): got %d results, want 1", target, p, len(results)) + } + continue + } + val := results[0].(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_StringVal).StringVal + if val != tt.v { + t.Errorf("#%d: got %q, want %q", x, val, tt.v) + } + } +} + +func TestGNMIQueryAll(t *testing.T) { + c := New([]string{"dev1", "dev2", "dev3"}) + updates := map[string]*pb.Notification{ + "value1": gnmiNotification("dev1", []string{}, []string{"a", "b"}, 0, "value1", false), + "value2": gnmiNotification("dev2", []string{}, []string{"a", "c"}, 0, "value2", false), + "value3": gnmiNotification("dev3", []string{}, []string{"a", "d"}, 0, "value3", false), + } + // Add updates to cache. + for _, u := range updates { + c.GnmiUpdate(u) + } + target, path := "*", []string{"a"} + if r := c.HasTarget(target); !r { + t.Error("Query not executed against cache for target * and path a") + } + var results []interface{} + appendResults := func(_ []string, _ *ctree.Leaf, val interface{}) error { results = append(results, val); return nil } + c.Query(target, path, appendResults) + for _, v := range results { + val := v.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_StringVal).StringVal + if _, ok := updates[val]; !ok { + t.Errorf("got unexpected update value %#v, want one of %v", v, updates) + } + delete(updates, val) + } + if len(updates) > 0 { + t.Errorf("the following updates were not received for query of target * with path a: %v", updates) + } +} + +func TestGNMIQueryAllError(t *testing.T) { + c := New([]string{"dev1", "dev2", "dev3"}) + updates := map[string]*pb.Notification{ + "value1": gnmiNotification("dev1", []string{}, []string{"a", "b"}, 0, "value1", false), + "value2": gnmiNotification("dev2", []string{}, []string{"a", "c"}, 0, "value2", false), + "value3": gnmiNotification("dev3", []string{}, []string{"a", "d"}, 0, "value3", false), + } + // Add updates to cache. + for _, u := range updates { + c.GnmiUpdate(u) + } + errVisit := func(_ []string, _ *ctree.Leaf, val interface{}) error { return errors.New("some error") } + if err := c.Query("*", []string{"a"}, errVisit); err == nil { + t.Fatalf("Query returned no error, want error") + } + defer c.mu.Unlock() + c.mu.Lock() // Cache is left unlocked. +} + +func TestGNMIAtomic(t *testing.T) { + c := New([]string{"dev1"}) + type query struct { + path []string + expect bool + } + tests := []struct { + desc string + noti *pb.Notification + wantErr bool + queries []query + }{ + { + desc: "normal atomic update", + noti: &pb.Notification{ + Atomic: true, + Timestamp: time.Now().UnixNano(), + Prefix: &pb.Path{ + Target: "dev1", + Origin: "openconfig", + Elem: []*pb.PathElem{{Name: "a"}, {Name: "b", Key: map[string]string{"key": "value"}}}, + }, + Update: []*pb.Update{ + {Path: &pb.Path{Elem: []*pb.PathElem{{Name: "x"}}}, Val: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"x val"}}}, + {Path: &pb.Path{Elem: []*pb.PathElem{{Name: "y"}}}, Val: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"y val"}}}, + {Path: &pb.Path{Elem: []*pb.PathElem{{Name: "z"}}}, Val: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"z val"}}}, + }, + }, + queries: []query{ + // Query paths that return the atomic update. + {path: []string{"*"}, expect: true}, + {path: []string{"openconfig"}, expect: true}, + {path: []string{"openconfig", "a"}, expect: true}, + {path: []string{"openconfig", "a", "b"}, expect: true}, + {path: []string{"openconfig", "a", "b", "value"}, expect: true}, + // Query paths that do not. + {path: []string{"foo"}}, + {path: []string{"openconfig", "a", "b", "value", "x"}}, + }, + }, { + desc: "empty atomic update", + noti: &pb.Notification{ + Atomic: true, + Timestamp: time.Now().UnixNano(), + Prefix: &pb.Path{ + Target: "dev1", + Origin: "openconfig", + Elem: []*pb.PathElem{{Name: "a"}, {Name: "b", Key: map[string]string{"key": "value"}}}, + }, + }, + }, { + desc: "atomic delete error", + noti: &pb.Notification{ + Atomic: true, + Timestamp: time.Now().UnixNano(), + Prefix: &pb.Path{ + Target: "dev1", + Origin: "openconfig", + Elem: []*pb.PathElem{{Name: "a"}, {Name: "b", Key: map[string]string{"key": "value"}}}, + }, + Delete: []*pb.Path{ + {Elem: []*pb.PathElem{{Name: "x"}}}, + {Elem: []*pb.PathElem{{Name: "y"}}}, + {Elem: []*pb.PathElem{{Name: "z"}}}, + }, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + err := c.GnmiUpdate(tt.noti) + if err != nil && !tt.wantErr { + t.Errorf("%v: unexpected error: %v", tt.desc, err) + } else if err == nil && tt.wantErr { + t.Errorf("%v: expected err, got nil", tt.desc) + } + for _, q := range tt.queries { + c.Query("dev1", q.path, func(_ []string, _ *ctree.Leaf, val interface{}) error { + if !q.expect { + t.Errorf("Query(%p): got notification %v, want none", q.path, val) + } else { + if v, ok := val.(*pb.Notification); !ok || !proto.Equal(v, tt.noti) { + t.Errorf("got:\n%s want\n%s", proto.MarshalTextString(v), proto.MarshalTextString(tt.noti)) + } + } + return nil + }) + } + } +} + +func TestGNMIUpdateMeta(t *testing.T) { + c := New([]string{"dev1"}) + + var lastSize, lastCount, lastAdds, lastUpds int64 + for i := 0; i < 10; i++ { + c.GnmiUpdate(gnmiNotification("dev1", []string{}, []string{"a", fmt.Sprint(i)}, int64(i), "b", false)) + + c.GetTarget("dev1").updateSize(nil) + c.GetTarget("dev1").updateMeta(nil) + + var path []string + path = metadata.Path(metadata.Size) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + newSize := v.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_IntVal).IntVal + if newSize <= lastSize { + t.Errorf("%s didn't increase after adding leaf #%d", + strings.Join(path, "/"), i+1) + } + lastSize = newSize + return nil + }) + path = metadata.Path(metadata.LeafCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + newCount := v.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_IntVal).IntVal + if newCount <= lastCount { + t.Errorf("%s didn't increase after adding leaf #%d", + strings.Join(path, "/"), i+1) + } + lastCount = newCount + return nil + }) + path = metadata.Path(metadata.AddCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + newAdds := v.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_IntVal).IntVal + if newAdds <= lastAdds { + t.Errorf("%s didn't increase after adding leaf #%d", + strings.Join(path, "/"), i+1) + } + lastAdds = newAdds + return nil + }) + path = metadata.Path(metadata.UpdateCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + newUpds := v.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_IntVal).IntVal + if newUpds <= lastUpds { + t.Errorf("%s didn't increase after adding leaf #%d", + strings.Join(path, "/"), i+1) + } + lastUpds = newUpds + return nil + }) + path = metadata.Path(metadata.DelCount) + c.Query("dev1", path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + dels := v.(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_IntVal).IntVal + if dels > 0 { + t.Errorf("%s is %d after adding leaf #%d, even though no leaves were removed", + strings.Join(path, "/"), dels, i+1) + } + return nil + }) + } + + pathGen := func(ph []string) *pb.Path { + pe := make([]*pb.PathElem, 0, len(ph)) + for _, p := range ph { + pe = append(pe, &pb.PathElem{Name: p}) + } + return &pb.Path{Elem: pe} + } + timestamp := time.Now().Add(-time.Minute) + + c.Sync("dev1") + + c.GnmiUpdate( + &pb.Notification{ + Timestamp: timestamp.UnixNano(), + Prefix: &pb.Path{Target: "dev1"}, + Update: []*pb.Update{ + &pb.Update{ + Path: pathGen([]string{"a", "1"}), + Val: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"c"}}, + }, + }, + }) + c.GetTarget("dev1").updateMeta(nil) +} + +func TestGNMIQueryWithPathElem(t *testing.T) { + c := New([]string{"dev1"}) + c.Query("", nil, func([]string, *ctree.Leaf, interface{}) error { + t.Error("querying without a target invoked callback") + return nil + }) + ns := []struct { + n *pb.Notification + q []string + e string + }{ + { + // add value1 by sending update notification + n: &pb.Notification{ + Prefix: &pb.Path{ + Target: "dev1", + Elem: []*pb.PathElem{&pb.PathElem{Name: "a"}, &pb.PathElem{Name: "b", Key: map[string]string{"bb": "x", "aa": "y"}}, &pb.PathElem{Name: "c"}}, + }, + Update: []*pb.Update{ + &pb.Update{ + Path: &pb.Path{Elem: []*pb.PathElem{&pb.PathElem{Name: "d", Key: map[string]string{"kk": "1", "ff": "2"}}, &pb.PathElem{Name: "e"}}}, + Val: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"value1"}}, + }, + }, + Timestamp: 0, + }, + q: []string{"a", "b", "y", "x", "c", "d", "2", "1", "e"}, + e: "", + }, + { + // add value2 by sending update notification + n: &pb.Notification{ + Prefix: &pb.Path{ + Target: "dev1", + Elem: []*pb.PathElem{&pb.PathElem{Name: "a"}, &pb.PathElem{Name: "b", Key: map[string]string{"bb": "x", "aa": "y"}}, &pb.PathElem{Name: "c"}}, + }, + Update: []*pb.Update{ + &pb.Update{ + Path: &pb.Path{Elem: []*pb.PathElem{&pb.PathElem{Name: "d", Key: map[string]string{"kk": "1", "ff": "3"}}, &pb.PathElem{Name: "e"}}}, + Val: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"value2"}}, + }, + }, + Timestamp: 1, + }, + q: []string{"a", "b", "y", "x", "c", "d", "3", "1", "e"}, + e: "value2", + }, + { + // delete the value1 by sending a delete notification + n: &pb.Notification{ + Prefix: &pb.Path{ + Target: "dev1", + Elem: []*pb.PathElem{&pb.PathElem{Name: "a"}, &pb.PathElem{Name: "b", Key: map[string]string{"bb": "x", "aa": "y"}}, &pb.PathElem{Name: "c"}}, + }, + Delete: []*pb.Path{ + &pb.Path{Elem: []*pb.PathElem{&pb.PathElem{Name: "d", Key: map[string]string{"kk": "1", "ff": "2"}}, &pb.PathElem{Name: "e"}}}, + }, + Timestamp: 2, + }, + q: []string{"a", "b", "y", "x", "c", "d", "2", "1", "e"}, + e: "", + }, + } + + for _, t := range ns { + c.GnmiUpdate(t.n) + } + + // Run queries over the inserted updates. + for x, tt := range ns { + target := tt.n.GetPrefix().GetTarget() + var gp *pb.Path + if tt.n.Update != nil { + gp = tt.n.Update[0].GetPath() + } else { + gp = tt.n.Delete[0] + } + p := joinPrefixAndPath(tt.n.GetPrefix(), gp) + var results []interface{} + appendResults := func(_ []string, _ *ctree.Leaf, val interface{}) error { results = append(results, val); return nil } + c.Query(target, p, appendResults) + if len(results) != 1 { + if tt.e != "" { + t.Errorf("Query(%s, %v, ): got %d results, want 1", target, p, len(results)) + } + continue + } + val := results[0].(*pb.Notification).Update[0].Val.Value.(*pb.TypedValue_StringVal).StringVal + if val != tt.e { + t.Errorf("#%d: got %q, want %q", x, val, tt.e) + } + } +} + +func TestGNMIClient(t *testing.T) { + c := New([]string{"dev1"}) + var got []interface{} + c.SetClient(func(n *ctree.Leaf) { + got = append(got, n.Value()) + }) + sortGot := func() { + path := func(i int) []string { + switch l := got[i].(type) { + case *pb.Notification: + if len(l.Update) > 0 { + return joinPrefixAndPath(l.Prefix, l.Update[0].Path) + } + return joinPrefixAndPath(l.Prefix, l.Delete[0]) + default: + return nil + } + } + sort.Slice(got, func(i, j int) bool { return less(path(i), path(j)) }) + } + + tests := []struct { + desc string + updates []*pb.Notification + want []interface{} + }{ + { + desc: "add new nodes", + updates: []*pb.Notification{ + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 1, "value1", false), + gnmiNotification("dev1", []string{}, []string{"a", "c"}, 2, "value2", false), + gnmiNotification("dev1", []string{}, []string{"a", "d"}, 3, "value3", false), + }, + want: []interface{}{ + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 1, "value1", false), + gnmiNotification("dev1", []string{}, []string{"a", "c"}, 2, "value2", false), + gnmiNotification("dev1", []string{}, []string{"a", "d"}, 3, "value3", false), + }, + }, + { + desc: "update nodes", + updates: []*pb.Notification{ + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 0, "value1", false), + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 1, "value1", false), + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 2, "value11", false), + }, + want: []interface{}{ + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 2, "value11", false), + }, + }, + { + desc: "delete nodes", + updates: []*pb.Notification{ + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 1, "", true), + gnmiNotification("dev1", []string{}, []string{"a", "b"}, 3, "", true), + gnmiNotification("dev1", []string{}, []string{"a"}, 4, "", true), + }, + want: []interface{}{ + gnmiNotification("dev1", nil, []string{"a", "b"}, 3, "", true), + gnmiNotification("dev1", nil, []string{"a", "c"}, 4, "", true), + gnmiNotification("dev1", nil, []string{"a", "d"}, 4, "", true), + }, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got = nil + for _, u := range tt.updates { + c.GnmiUpdate(u) + } + sortGot() + if diff := cmp.Diff(tt.want, got, cmp.Comparer(proto.Equal)); diff != "" { + t.Errorf("sent updates: %v\ndiff in received updates:\n%s", tt.updates, diff) + } + }) + } + t.Run("remove target", func(t *testing.T) { + got = nil + want := deleteNoti("dev1", "", []string{"*"}) + want.Timestamp = 0 + c.Remove("dev1") + if len(got) != 1 { + t.Fatalf("Remove didn't produce correct client update, got: %+v, want: %+v", got, []interface{}{want}) + } + gotVal := got[0].(*pb.Notification) + // Clear timestamp before comparison. + gotVal.Timestamp = 0 + if diff := cmp.Diff(want, gotVal, cmp.Comparer(proto.Equal)); diff != "" { + t.Errorf("diff in received update:\n%s", diff) + } + }) +} + +func TestGNMIReset(t *testing.T) { + targets := []string{"dev1", "dev2", "dev3"} + c := New(targets) + updates := map[string]*pb.Notification{ + "value1": gnmiNotification("dev1", []string{}, []string{"a", "b"}, 0, "value1", false), + "value2": gnmiNotification("dev2", []string{}, []string{"a", "c"}, 0, "value2", false), + "value3": gnmiNotification("dev3", []string{}, []string{"a", "d"}, 0, "value3", false), + "invalid": gnmiNotification("", []string{}, []string{}, 0, "", false), // Should have no effect on test. + } + // Add updates to cache. + for _, u := range updates { + c.GnmiUpdate(u) + } + var results []interface{} + var hasMeta bool + appendResults := func(path []string, _ *ctree.Leaf, val interface{}) error { + if path[0] == metadata.Root { + hasMeta = true + } else { + results = append(results, val) + } + return nil + } + for _, target := range targets { + results = nil + hasMeta = false + c.Query(target, []string{"*"}, appendResults) + if got := len(results); got != 1 { + t.Errorf("Target %q got %d results, want 1\n\t%v", target, got, results) + } + if hasMeta { + t.Errorf("Target %q got metadata, want none", target) + } + c.Reset(target) + results = nil + hasMeta = false + c.Query(target, []string{"*"}, appendResults) + if got := len(results); got != 0 { + t.Errorf("Target %q got %d results, want 0\n\t%v", target, got, results) + } + if !hasMeta { + t.Errorf("Target %q got no metadata, want metadata", target) + } + } +} + +func TestGNMISyncConnectUpdates(t *testing.T) { + c := New([]string{"dev1"}) + var got []interface{} + c.SetClient(func(l *ctree.Leaf) { + got = append(got, l.Value()) + }) + tests := []struct { + metadata string + helper func(string) + want []*pb.Notification + }{ + {metadata: metadata.Sync, helper: c.Sync, want: []*pb.Notification{metaNotiBool("dev1", metadata.Sync, true)}}, + {metadata: metadata.Connected, helper: c.Connect, want: []*pb.Notification{metaNotiBool("dev1", metadata.Connected, true)}}, + {metadata: metadata.ConnectError, helper: func(t string) { c.ConnectError(t, fmt.Errorf("testErr")); c.Connect(t) }, + want: []*pb.Notification{metaNotiStr("dev1", metadata.ConnectError, "testErr"), + gnmiNotification("dev1", []string{}, metadata.Path(metadata.ConnectError), 1, "", true)}}, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("Test %v", tt.metadata), func(t *testing.T) { + tt.helper("dev1") + if len(got) != len(tt.want) { + t.Fatalf("got %d updates, want %d. got %v, want %v", len(got), len(tt.want), got, tt.want) + } + for i := 0; i < len(tt.want); i++ { + got[i].(*pb.Notification).Timestamp = 0 + tt.want[i].Timestamp = 0 + if diff := cmp.Diff(tt.want[i], got[i], cmp.Comparer(proto.Equal)); diff != "" { + t.Errorf("diff in received update:\n%s", diff) + } + } + got = nil + }) + } +} + +func TestGNMIUpdate(t *testing.T) { + type state struct { + deleted bool + path []string + timestamp int + val string + } + + dev := "dev1" + prefix := []string{"prefix1"} + path1 := []string{"path1"} + path2 := []string{"path2"} + path3 := []string{"path3"} + tests := []struct { + desc string + initial *pb.Notification + notification *pb.Notification + want []state + wantConnectedAddr string + wantUpdates int + wantSuppressed int + wantStale int + wantErr bool + }{ + { + desc: "duplicate update", + initial: notificationBundle(dev, prefix, 0, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "2", + }, + }), + notification: notificationBundle(dev, prefix, 1, []update{ + { + path: path1, + val: "11", + }, { + path: path2, + val: "2", + }, + }), + want: []state{ + { + path: path1, + val: "11", + timestamp: 1, + }, { + path: path2, + val: "2", + timestamp: 1, + }, + }, + wantUpdates: 1, + wantSuppressed: 1, + wantErr: false, + }, { + desc: "no duplicates", + initial: notificationBundle(dev, prefix, 0, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "2", + }, + }), + notification: notificationBundle(dev, prefix, 1, []update{ + { + path: path1, + val: "11", + }, { + path: path2, + val: "22", + }, + }), + want: []state{ + { + path: path1, + val: "11", + timestamp: 1, + }, { + path: path2, + val: "22", + timestamp: 1, + }, + }, + wantUpdates: 2, + }, { + desc: "duplicate update with deletes", + initial: notificationBundle(dev, prefix, 0, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "2", + }, { + path: path3, + val: "3", + }, + }), + notification: notificationBundle(dev, prefix, 1, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + delete: true, + }, { + path: path3, + delete: true, + }, + }), + want: []state{ + { + path: path1, + val: "1", + timestamp: 1, + }, { + path: path2, + deleted: true, + }, { + path: path3, + deleted: true, + }, + }, + wantUpdates: 2, + wantSuppressed: 1, + wantErr: false, + }, { + desc: "stale updates - same timestamp", + initial: notificationBundle(dev, prefix, 0, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "2", + }, + }), + notification: notificationBundle(dev, prefix, 0, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "22", + }, + }), + want: []state{ + { + path: path1, + val: "1", + timestamp: 0, + }, { + path: path2, + val: "2", + timestamp: 0, + }, + }, + wantStale: 2, + wantErr: true, + }, { + desc: "stale updates - later timestamp", + initial: notificationBundle(dev, prefix, 1, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "2", + }, + }), + notification: notificationBundle(dev, prefix, 0, []update{ + { + path: path1, + val: "1", + }, { + path: path2, + val: "22", + }, + }), + want: []state{ + { + path: path1, + val: "1", + timestamp: 1, + }, { + path: path2, + val: "2", + timestamp: 1, + }, + }, + wantStale: 2, + wantErr: true, + }, { + desc: "meta connectedAddress reset", + initial: notificationBundle(dev, nil, 0, []update{ + { + path: []string{metadata.Root, metadata.ConnectedAddr}, + val: "127.1.1.1:8080", + }, + }), + notification: notificationBundle(dev, prefix, 1, []update{}), + want: []state{}, + wantConnectedAddr: "", // Value is cleared and copied to the cache. + }, { + desc: "meta connectedAddress update", + initial: notificationBundle(dev, prefix, 0, []update{}), + notification: notificationBundle(dev, nil, 1, []update{ + { + path: []string{metadata.Root, metadata.ConnectedAddr}, + val: "127.1.1.1:8080", + }, + }), + wantConnectedAddr: "127.1.1.1:8080", + wantUpdates: 1, + }, + } + + suppressedPath := metadata.Path(metadata.SuppressedCount) + updatePath := metadata.Path(metadata.UpdateCount) + stalePath := metadata.Path(metadata.StaleCount) + connectedAddrPath := metadata.Path(metadata.ConnectedAddr) + + for _, tt := range tests { + c := New([]string{dev}) + if err := c.GnmiUpdate(tt.initial); err != nil { + t.Fatalf("%v: Could not initialize cache: %v ", tt.desc, err) + } + c.GetTarget(dev).meta.Clear() + + err := c.GnmiUpdate(tt.notification) + c.UpdateMetadata() + + if err != nil && !tt.wantErr { + t.Errorf("%v: GnmiUpdate(%v) = %v, want no error", tt.desc, tt.notification, err) + continue + } + if err == nil && tt.wantErr { + t.Errorf("%v: GnmiUpdate(%v) = nil, want error", tt.desc, tt.notification) + continue + } + + checkMetaInt := func(desc string, path []string, want int) { + c.Query(dev, path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + got := v.(*pb.Notification).Update[0].Val.GetIntVal() + if got != int64(want) { + t.Errorf("%v: got %v = %d, want %d", desc, path, got, want) + } + return nil + }) + } + + checkMetaString := func(desc string, path []string, want string) { + c.Query(dev, path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + got := v.(*pb.Notification).Update[0].Val.GetStringVal() + if got != string(want) { + t.Errorf("%v: got %v = %v, want %v", desc, path, got, want) + } + return nil + }) + } + + checkState := func(desc string, states []state) { + for _, s := range states { + c.Query(dev, s.path, func(_ []string, _ *ctree.Leaf, v interface{}) error { + if s.deleted { + t.Errorf("%v: Query(%p): got %v, want none", desc, s.path, v) + } else { + want := gnmiNotification(dev, prefix, s.path, int64(s.timestamp), s.val, false) + if got, ok := v.(*pb.Notification); !ok || !proto.Equal(got, want) { + t.Errorf("%v: got:\n%s want\n%s", desc, proto.MarshalTextString(got), proto.MarshalTextString(want)) + } + } + return nil + }) + } + } + + checkState(tt.desc, tt.want) + checkMetaInt(tt.desc, suppressedPath, tt.wantSuppressed) + checkMetaInt(tt.desc, updatePath, tt.wantUpdates) + checkMetaInt(tt.desc, stalePath, tt.wantStale) + checkMetaString(tt.desc, connectedAddrPath, tt.wantConnectedAddr) + } +} + +func less(p, p2 []string) bool { + for x := 0; x < len(p) && x < len(p2); x++ { + if p[x] < p2[x] { + return true + } + if p[x] > p2[x] { + return false + } + } + return len(p) < len(p2) +} diff --git a/deps/github.com/openconfig/gnmi/cli/cli.go b/deps/github.com/openconfig/gnmi/cli/cli.go new file mode 100644 index 0000000000000000000000000000000000000000..1549d2a65ddf4ac5d64281c0f25904013b5e0c6f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cli/cli.go @@ -0,0 +1,417 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package cli provides the query capabilities for streaming telemetry. +package cli + +import ( + "bytes" + "context" + "fmt" + "sort" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/client" + "github.com/openconfig/gnmi/ctree" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +const layout = "2006-01-02-15:04:05.000000000" + +var ( + queryTypeMap = map[string]client.Type{ + "o": client.Once, "once": client.Once, "ONCE": client.Once, + "p": client.Poll, "polling": client.Poll, "POLLING": client.Poll, + "s": client.Stream, "streaming": client.Stream, "STREAMING": client.Stream, + } + displayTypeMap = map[string]string{ + "g": "GROUP", "group": "GROUP", "GROUP": "GROUP", + "s": "SINGLE", "single": "SINGLE", "SINGLE": "SINGLE", + "p": "PROTO", "proto": "PROTO", "PROTO": "PROTO", + "sp": "SHORTPROTO", "shortproto": "SHORTPROTO", "SHORTPROTO": "SHORTPROTO", + } +) + +// Config is a type to hold parameters that affect how the cli sends and +// displays results. +type Config struct { + PollingInterval time.Duration // Duration between polling events. + StreamingDuration time.Duration // Duration to collect updates, 0 is forever. + Count uint // Number of polling/streaming events, 0 is infinite. + countExhausted bool // Trigger to indicate termination. + Delimiter string // Delimiter between path elements when converted to string. + Display func([]byte) // Function called to display each result. + DisplayPrefix string // Prefix for each line of result output. + DisplayIndent string // Indent per nesting level of result output. + DisplayType string // Display results in selected format, grouped, single, proto. + DisplayPeer bool // Display the immediate connected peer. + // <empty string> - disable timestamp + // on - human readable timestamp according to layout + // raw - int64 nanos since epoch + // <FORMAT> - human readable timestamp according to <FORMAT> + Timestamp string // Formatting of timestamp in result output. + DisplaySize bool + Latency bool // Show latency to client + ClientTypes []string // List of client types to try. +} + +// QueryType returns a client query type for t after trying aliases for the +// type. +func QueryType(t string) client.Type { + return queryTypeMap[t] +} + +// QueryDisplay constructs a query from the supplied arguments (target, queries, +// queryType), sends as an RPC to the specified destination address and displays +// results with the supplied display function. +func QueryDisplay(ctx context.Context, query client.Query, cfg *Config) error { + if err := sendQueryAndDisplay(ctx, query, cfg); err != nil { + return fmt.Errorf("sendQueryAndDisplay(ctx, %+v, %+v):\n\t%v", query, cfg, err) + } + return nil +} + +// ParseSubscribeProto parses given gNMI SubscribeRequest text proto +// into client.Query. +func ParseSubscribeProto(p string) (client.Query, error) { + var tq client.Query + sr := &gpb.SubscribeRequest{} + if err := proto.UnmarshalText(p, sr); err != nil { + return tq, err + } + return client.NewQuery(sr) +} + +// sendQueryAndDisplay directs a query to the specified target. The returned +// results are formatted as a human readable string and passed to +// Config.Display(). +func sendQueryAndDisplay(ctx context.Context, query client.Query, cfg *Config) error { + cancel := func() {} + if cfg.StreamingDuration > 0 { + ctx, cancel = context.WithTimeout(ctx, cfg.StreamingDuration) + defer cancel() + } + switch displayTypeMap[cfg.DisplayType] { + default: + return fmt.Errorf("unknown display type %q", cfg.DisplayType) + case "GROUP": + case "SINGLE": + return displaySingleResults(ctx, query, cfg) + case "PROTO": + return displayProtoResults(ctx, query, cfg, func(r proto.Message) []byte { + return []byte(proto.MarshalTextString(r)) + }) + case "SHORTPROTO": + return displayProtoResults(ctx, query, cfg, func(r proto.Message) []byte { + // r.String() will add extra whitespace at the end for some reason. + // Trim it down. + return bytes.TrimSpace([]byte(proto.CompactTextString(r))) + }) + } + switch query.Type { + default: + return fmt.Errorf("unknown query type %v", query.Type) + case client.Once: + return displayOnceResults(ctx, query, cfg) + case client.Poll: + return displayPollingResults(ctx, query, cfg) + case client.Stream: + return displayStreamingResults(ctx, query, cfg) + } +} + +// genHandler takes a provided query and cfg and will build a display function +// that is custom for the client and display options configured. It really +// looks more scary than it is. For each client type and display method you +// need to build a custom handler for display the raw values rather than +// the normal "Leaf" value that client normally works with. +func genHandler(cfg *Config) client.NotificationHandler { + var buf bytes.Buffer // Reuse the same buffer in either case. + iDisplay := func(p client.Path, v interface{}, ts time.Time) { + buf.Reset() + buf.WriteString(strings.Join(p, cfg.Delimiter)) + buf.WriteString(fmt.Sprintf(", %v", v)) + var t interface{} + switch cfg.Timestamp { + default: // Assume user has passed a valid layout for time.Format + t = ts.Format(cfg.Timestamp) + case "": // Timestamp disabled. + case "on": + t = ts.Format(layout) + case "raw": + t = ts.UnixNano() + } + if t != nil { + buf.WriteString(fmt.Sprintf(", %v", t)) + } + if cfg.Latency { + buf.WriteString(fmt.Sprintf(", %s", time.Since(ts))) + } + cfg.Display(buf.Bytes()) + } + return func(n client.Notification) error { + switch v := n.(type) { + default: + return fmt.Errorf("invalid type: %#v", v) + case client.Update: + iDisplay(v.Path, v.Val, v.TS) + case client.Delete: + iDisplay(v.Path, v.Val, v.TS) + case client.Sync, client.Connected: + case client.Error: + return v + } + return nil + } +} + +// displaySingleResults displays each key/value pair returned on a single line. +func displaySingleResults(ctx context.Context, query client.Query, cfg *Config) error { + query.NotificationHandler = genHandler(cfg) + c := &client.BaseClient{} + if err := c.Subscribe(ctx, query); err != nil { + return fmt.Errorf("client had error while displaying results:\n\t%v", err) + } + return nil +} + +// displayProtoResults displays the raw protos returned for the supplied query. +func displayProtoResults(ctx context.Context, query client.Query, cfg *Config, formatter func(proto.Message) []byte) error { + var sum int64 + query.ProtoHandler = func(msg proto.Message) error { + if cfg.DisplaySize { + sum += int64(proto.Size(msg)) + } + cfg.Display(formatter(msg)) + return nil + } + c := &client.BaseClient{} + if err := c.Subscribe(ctx, query, cfg.ClientTypes...); err != nil { + return fmt.Errorf("client had error while displaying results:\n\t%v", err) + } + if cfg.DisplaySize { + cfg.Display([]byte(fmt.Sprintf("// total response size: %d", sum))) + } + return nil +} + +func displayPeer(c client.Client, cfg *Config) { + if !cfg.DisplayPeer { + return + } + var peer string + impl, err := c.Impl() + if err != nil { + return + } + if v, ok := impl.(interface { + Peer() string + }); ok { + peer = v.Peer() + } + if peer == "" { + cfg.Display([]byte("// No peer found for client")) + return + } + cfg.Display([]byte(fmt.Sprintf("// CLI peer: %s", peer))) +} + +// displayOnceResults builds all the results returned for for one application of +// query to the OpenConfig data tree and displays the resulting tree in a human +// readable form. +func displayOnceResults(ctx context.Context, query client.Query, cfg *Config) error { + c := client.New() + if err := c.Subscribe(ctx, query, cfg.ClientTypes...); err != nil { + return fmt.Errorf("client had error while displaying results:\n\t%v", err) + } + displayPeer(c, cfg) + displayWalk(query.Target, c, cfg) + return nil +} + +func countComplete(cfg *Config) bool { + switch { + case cfg.countExhausted: + return true + case cfg.Count == 0: + default: + cfg.Count-- + if cfg.Count == 0 { + cfg.countExhausted = true + } + } + return false +} + +// displayPollingResults repeatedly calls displayOnceResults at the requested +// interval. +func displayPollingResults(ctx context.Context, query client.Query, cfg *Config) error { + c := client.New() + if err := c.Subscribe(ctx, query, cfg.ClientTypes...); err != nil { + return fmt.Errorf("client had error while displaying results:\n\t%v", err) + } + defer c.Close() + header := false + for !countComplete(cfg) { + if err := c.Poll(); err != nil { + return fmt.Errorf("client.Poll(): %v", err) + } + if !header { + displayPeer(c, cfg) + header = true + } + displayWalk(query.Target, c, cfg) + if !cfg.countExhausted { + time.Sleep(cfg.PollingInterval) + } + } + return nil +} + +// displayStreamingResults calls displayOnceResults one time, followed by +// subsequent individual updates as they arrive. +func displayStreamingResults(ctx context.Context, query client.Query, cfg *Config) error { + c := client.New() + complete := false + display := func(path []string, ts time.Time, val interface{}) { + if !complete { + return + } + b := make(pathmap) + if cfg.Timestamp != "" { + b.add(append(path, "timestamp"), ts) + b.add(append(path, "value"), val) + } else { + b.add(path, val) + } + cfg.Display(b.display(cfg.DisplayPrefix, cfg.DisplayIndent)) + } + query.NotificationHandler = func(n client.Notification) error { + switch v := n.(type) { + case client.Update: + display(v.Path, v.TS, v.Val) + case client.Delete: + display(v.Path, v.TS, v.Val) + case client.Sync: + displayWalk(query.Target, c, cfg) + complete = true + case client.Error: + cfg.Display([]byte(fmt.Sprintf("Error: %v", v))) + } + return nil + } + return c.Subscribe(ctx, query, cfg.ClientTypes...) +} + +func displayWalk(target string, c *client.CacheClient, cfg *Config) { + b := make(pathmap) + var addFunc func(path []string, v client.TreeVal) + switch cfg.Timestamp { + default: + addFunc = func(path []string, v client.TreeVal) { + b.add(path, pathmap{ + "value": v.Val, + "timestamp": v.TS.Format(cfg.Timestamp), + }) + } + case "on": + addFunc = func(path []string, v client.TreeVal) { + b.add(path, pathmap{ + "value": v.Val, + "timestamp": v.TS.Format(layout), + }) + } + case "raw": + addFunc = func(path []string, v client.TreeVal) { + b.add(path, pathmap{ + "value": v.Val, + "timestamp": v.TS.UnixNano(), + }) + } + case "off", "": + addFunc = func(path []string, v client.TreeVal) { + b.add(path, v.Val) + } + } + c.WalkSorted(func(path []string, _ *ctree.Leaf, value interface{}) error { + switch v := value.(type) { + default: + b.add(path, fmt.Sprintf("INVALID NODE %#v", value)) + case *ctree.Tree: + case client.TreeVal: + addFunc(path, v) + } + return nil + }) + result := b.display(cfg.DisplayPrefix, cfg.DisplayIndent) + cfg.Display(result) + if cfg.DisplaySize { + cfg.Display([]byte(fmt.Sprintf("// total response size: %d", len(result)))) + } +} + +type pathmap map[string]interface{} + +func (m pathmap) display(prefix, indent string) []byte { + return []byte(prefix + m.str(prefix, indent, "")) +} + +func valStr(val interface{}, prefix, indent, curindent string) string { + switch v := val.(type) { + case pathmap: + return v.str(prefix, indent, curindent+indent) + case string: + return fmt.Sprintf("%q", v) + case []interface{}: + vals := make([]string, len(v)) + for i := 0; i < len(v); i++ { + vals[i] = valStr(v[i], prefix, indent, curindent) + } + return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) + default: + return fmt.Sprintf("%v", v) + } +} + +func (m pathmap) str(prefix, indent, curindent string) string { + var keys []string + for key := range m { + keys = append(keys, key) + } + sort.Strings(keys) + var vals []string + for _, key := range keys { + vals = append(vals, prefix+curindent+indent+fmt.Sprintf("%q: %s", key, valStr(m[key], prefix, indent, curindent))) + } + return "{\n" + strings.Join(vals, ",\n") + "\n" + prefix + curindent + "}" +} + +func (m pathmap) add(path []string, v interface{}) { + if len(path) == 1 { + m[path[0]] = v + return + } + + mm, ok := m[path[0]] + if !ok { + mm = make(pathmap) + } + mm.(pathmap).add(path[1:], v) + m[path[0]] = mm +} diff --git a/deps/github.com/openconfig/gnmi/cli/cli_test.go b/deps/github.com/openconfig/gnmi/cli/cli_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2851145dd98b3bc5482dd2c683dea73f0b94ff9e --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cli/cli_test.go @@ -0,0 +1,886 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cli + +import ( + "context" + "crypto/tls" + "math" + "regexp" + "sort" + "strings" + "testing" + "time" + + "google.golang.org/grpc" + "github.com/openconfig/gnmi/client" + _ "github.com/openconfig/gnmi/client/gnmi" + "github.com/openconfig/gnmi/testing/fake/gnmi" + "github.com/openconfig/gnmi/testing/fake/testing/grpc/config" + + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +func TestSendQueryAndDisplayFail(t *testing.T) { + tests := []struct { + desc string + q client.Query + cfg *Config + want string + }{{ + desc: "addr not set", + want: "Destination.Addrs is empty", + q: client.Query{Queries: []client.Path{{"a"}}, Type: client.Once}, + cfg: &Config{DisplayType: "group"}, + }, { + desc: "invalid display", + want: `unknown display type "invalid display"`, + q: client.Query{Queries: []client.Path{{"a"}}, Type: client.Once}, + cfg: &Config{DisplayType: "invalid display"}, + }, { + desc: "unknown query type", + want: `unknown query type`, + q: client.Query{Queries: []client.Path{{"a"}}, Type: client.Unknown}, + cfg: &Config{DisplayType: "group"}, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if err := sendQueryAndDisplay(context.Background(), tt.q, tt.cfg); err == nil || !strings.Contains(err.Error(), tt.want) { + t.Errorf("sendQueryAndDisplay(ctx, %v, %v): unexpected error, got %v want error contains %q", tt.q, tt.cfg, err, tt.want) + } + }) + } +} + +func TestSendQueryAndDisplay(t *testing.T) { + var displayOut string + display := func(b []byte) { + displayOut += string(b) + "\n" + } + tests := []struct { + desc string + updates []*fpb.Value + query client.Query + cfg Config + want string + sort bool + }{{ + desc: "single target single output with provided layout", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + Timestamp: "2006-01-02-15:04:05", + }, + want: `dev1/a/b, 5, 1969-12-31-16:00:00 +`, + }, { + desc: "single target single output with timestamp on", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + Timestamp: "on", + }, + want: `dev1/a/b, 5, 1969-12-31-16:00:00.000000100 +`, + }, { + desc: "single target single output", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + }, + want: `dev1/a/b, 5 +`, + }, { + desc: "single target group output with comment prefix", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_DoubleValue{DoubleValue: &fpb.DoubleValue{Value: math.Inf(0)}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "# ", + DisplayIndent: " ", + DisplayType: "group", + }, + want: `# { +# "dev1": { +# "a": { +# "b": +Inf +# } +# } +# } +`, + }, { + desc: "single target group output with list value", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_StringListValue{StringListValue: &fpb.StringListValue{Value: []string{"c", "d or e"}}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + }, + want: `{ + "dev1": { + "a": { + "b": ["c", "d or e"] + } + } +} +`, + }, { + desc: "single target single output with peer", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + DisplayPeer: true, + }, + want: `// CLI peer: 127.0.0.1:port +{ + "dev1": { + "a": { + "b": 5 + } + } +} +`, + }, { + desc: "single target single output with latency", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + Latency: true, + }, + want: `dev1/a/b, 5, <h>h<m>m<s>.<ns>s +`, + }, { + desc: "single target single output with delete", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Path: []string{"a", "c"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Path: []string{"a", "c"}, Value: &fpb.Value_Delete{Delete: &fpb.DeleteValue{}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 200}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + }, + want: `dev1/a/b, 5 +dev1/a/c, 5 +dev1/a/c, <nil> +`, + }, { + desc: "single target multiple paths", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + // The following is disallowed because the tree already has a branch where + // this leaf requests to be, thus dropped. + {Path: []string{"a"}, Value: &fpb.Value_StringValue{StringValue: &fpb.StringValue{Value: "foo"}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 105}}, + // The following is disallowed because the tree already has a leaf where + // this branch requests to be, thus dropped. + {Path: []string{"a", "b", "c"}, Value: &fpb.Value_DoubleValue{DoubleValue: &fpb.DoubleValue{Value: 3.14}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + }, + want: `{ + "dev1": { + "a": { + "b": 5 + } + } +} +`, + }, { + desc: "single target single paths (proto)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 1440807212000000000}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayType: "proto", + }, + want: `sync_response: true + +update: < + timestamp: 1440807212000000000 + prefix: < + target: "dev1" + > + update: < + path: < + element: "a" + element: "b" + > + val: < + int_val: 5 + > + > +> + +`, + }, { + desc: "single target single paths (proto) with size", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 1440807212000000000}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayType: "proto", + DisplaySize: true, + }, + want: `sync_response: true + +update: < + timestamp: 1440807212000000000 + prefix: < + target: "dev1" + > + update: < + path: < + element: "a" + element: "b" + > + val: < + int_val: 5 + > + > +> + +// total response size: 36 +`, + }, { + desc: "single target multiple paths (with timestamp)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 1440807212000000000}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 1440807212000000001}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + Timestamp: "on", + }, + want: `{ + "dev1": { + "a": { + "b": { + "timestamp": "2015-08-28-17:13:32.000000000", + "value": 5 + } + } + } +} +`, + }, { + desc: "single target multiple paths (with raw timestamp)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + Timestamp: "raw", + }, + want: `{ + "dev1": { + "a": { + "b": { + "timestamp": 100, + "value": 5 + } + } + } +} +`, + }, { + desc: "single target multiple paths (with DoW timestamp)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 1440807212000000000}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 1440807212000000001}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + Timestamp: "Monday", + }, + want: `{ + "dev1": { + "a": { + "b": { + "timestamp": "Friday", + "value": 5 + } + } + } +} +`, + }, { + desc: "single target multiple root paths", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Path: []string{"a", "c"}, Value: &fpb.Value_DoubleValue{DoubleValue: &fpb.DoubleValue{Value: 3.25}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 101}}, + {Path: []string{"d", "e", "f"}, Value: &fpb.Value_StringValue{StringValue: &fpb.StringValue{Value: "foo"}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 102}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"*"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + }, + want: `{ + "dev1": { + "a": { + "b": 5, + "c": 3.25 + }, + "d": { + "e": { + "f": "foo" + } + } + } +} +`, + }, { + desc: "single target multiple paths (Pollingx2)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Poll, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + Count: 2, + PollingInterval: 100 * time.Millisecond, + }, + want: `{ + "dev1": { + "a": { + "b": 5 + } + } +} +{ + "dev1": { + "a": { + "b": 5 + } + } +} +`, + }, { + desc: "single target multiple paths (streaming)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + // StreamingDuration will expire before Count updates are received because + // no updates are being streamed in the test. + Count: 2, + StreamingDuration: 100 * time.Millisecond, + }, + want: `{ + "dev1": { + "a": { + "b": 5 + } + } +} +`, + }, { + desc: "single target multiple paths (single line)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Path: []string{"a", "c"}, Value: &fpb.Value_DoubleValue{DoubleValue: &fpb.DoubleValue{Value: 3.25}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 101}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + }, + want: `dev1/a/b, 5 +dev1/a/c, 3.25 +`, + sort: true, + }, { + desc: "single target multiple paths (single line raw)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Path: []string{"a", "c"}, Value: &fpb.Value_DoubleValue{DoubleValue: &fpb.DoubleValue{Value: 3.25}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 101}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: ".", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + Timestamp: "raw", + }, + want: `dev1.a.b, 5, 100 +dev1.a.c, 3.25, 101 +`, + sort: true, + }, { + desc: "single target multiple paths (proto short)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayType: "shortproto", + }, + want: `update:<timestamp:100 prefix:<target:"dev1" > update:<path:<element:"a" element:"b" > val:<int_val:5 > > > +sync_response:true +`, + }, { + desc: "single target multiple paths (with display size)", + updates: []*fpb.Value{ + {Path: []string{"a", "b"}, Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 100}}, + {Value: &fpb.Value_Sync{Sync: 1}, Repeat: 1, Timestamp: &fpb.Timestamp{Timestamp: 300}}, + }, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayIndent: " ", + DisplayType: "group", + DisplaySize: true, + }, + want: `{ + "dev1": { + "a": { + "b": 5 + } + } +} +// total response size: 49 +`, + }} + + opt, err := config.WithSelfTLSCert() + if err != nil { + t.Fatalf("failed to generate cert: %v", err) + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + displayOut = "" + s, err := gnmi.New( + &fpb.Config{ + Target: "dev1", + DisableSync: true, + Values: tt.updates, + }, + []grpc.ServerOption{opt}, + ) + if err != nil { + t.Fatal("failed to start test server") + } + defer s.Close() + + tt.query.Addrs = []string{s.Address()} + + if err := QueryDisplay(context.Background(), tt.query, &tt.cfg); err != nil { + // This is fine if we use cfg.StreamingDuration. + t.Logf("sendQueryAndDisplay returned error: %v", err) + } + + // The test server comes up on an arbitrary port. Remove the port number + // from the output before comparison. + re := regexp.MustCompile("localhost:[0-9]*") + got := re.ReplaceAllLiteralString(displayOut, "127.0.0.1:port") + reLat := regexp.MustCompile(`\d+h\d+m\d+.\d+s`) + got = reLat.ReplaceAllLiteralString(got, "<h>h<m>m<s>.<ns>s") + if tt.sort { + lines := strings.Split(got, "\n") + sort.Strings(lines) + // move blank line to end + lines = append(lines[1:], lines[0]) + got = strings.Join(lines, "\n") + } + + if got != tt.want { + t.Errorf("sendQueryAndDisplay(ctx, address, %v, %v):\ngot(%d):\n%s\nwant(%d):\n%s", tt.query, tt.cfg, len(got), got, len(tt.want), tt.want) + } + }) + } +} + +func TestGNMIClient(t *testing.T) { + var displayOut string + display := func(b []byte) { + displayOut += string(b) + "\n" + } + tests := []struct { + desc string + updates []*fpb.Value + query client.Query + cfg Config + want string + sort bool + }{{ + desc: "single target single output with provided layout", + updates: []*fpb.Value{{ + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_Delete{Delete: &fpb.DeleteValue{}}, + }, { + Timestamp: &fpb.Timestamp{Timestamp: 300}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }}, + query: client.Query{ + Target: "dev", + Queries: []client.Path{{"a"}}, + Type: client.Once, + Timeout: 3 * time.Second, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "single", + Timestamp: "2006-01-02-15:04:05", + }, + want: `dev/a, 5, 1969-12-31-16:00:00 +dev/a/b, 5, 1969-12-31-16:00:00 +dev/a/b, <nil>, 1969-12-31-16:00:00 +`, + }, { + desc: "single target group output with provided layout", + updates: []*fpb.Value{{ + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_Delete{Delete: &fpb.DeleteValue{}}, + }, { + Timestamp: &fpb.Timestamp{Timestamp: 300}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }}, + query: client.Query{ + Target: "dev", + Queries: []client.Path{{"a"}}, + Type: client.Once, + Timeout: 3 * time.Second, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Delimiter: "/", + Display: display, + DisplayPrefix: "", + DisplayIndent: " ", + DisplayType: "group", + Timestamp: "2006-01-02-15:04:05", + }, + want: `{ + "dev": { + "a": { + "timestamp": "1969-12-31-16:00:00", + "value": 5 + } + } +} +`, + }, { + desc: "single target multiple paths (proto short)", + updates: []*fpb.Value{{ + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_Delete{Delete: &fpb.DeleteValue{}}, + }, { + Timestamp: &fpb.Timestamp{Timestamp: 300}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }}, + query: client.Query{ + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + Timeout: 3 * time.Second, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + cfg: Config{ + Display: display, + DisplayType: "shortproto", + }, + want: `update:<timestamp:100 prefix:<target:"dev1" > update:<path:<element:"a" > val:<int_val:5 > > > +update:<timestamp:100 prefix:<target:"dev1" > update:<path:<element:"a" element:"b" > val:<int_val:5 > > > +update:<timestamp:200 prefix:<target:"dev1" > delete:<element:"a" element:"b" > > +sync_response:true +`, + }} + opt, err := config.WithSelfTLSCert() + if err != nil { + t.Fatalf("failed to generate cert: %v", err) + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + displayOut = "" + s, err := gnmi.New( + &fpb.Config{ + Target: "dev1", + DisableSync: true, + Values: tt.updates, + }, + []grpc.ServerOption{opt}, + ) + if err != nil { + t.Fatal("failed to start test server") + } + defer s.Close() + + tt.query.Addrs = []string{s.Address()} + if err := QueryDisplay(context.Background(), tt.query, &tt.cfg); err != nil { + // This is fine if we use cfg.StreamingDuration. + t.Logf("sendQueryAndDisplay returned error: %v", err) + } + + // The test server comes up on an arbitrary port. Remove the port number + // from the output before comparison. + re := regexp.MustCompile("127.0.0.1:[0-9]*") + got := re.ReplaceAllLiteralString(displayOut, "127.0.0.1:port") + reLat := regexp.MustCompile(`\d+h\d+m\d+.\d+s`) + got = reLat.ReplaceAllLiteralString(got, "<h>h<m>m<s>.<ns>s") + if got != tt.want { + t.Errorf("sendQueryAndDisplay(ctx, address, %v, %v):\ngot(%d):\n%s\nwant(%d):\n%s", tt.query, tt.cfg, len(got), got, len(tt.want), tt.want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/client/cache.go b/deps/github.com/openconfig/gnmi/client/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..cd2efbd5c33f0716de5b30b4aa6f22f230fce47a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/cache.go @@ -0,0 +1,115 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + "fmt" + + "github.com/openconfig/gnmi/ctree" +) + +// Client adds a caching layer on top of a simple query client. +// +// It works similarly to BaseClient and adds the Leaves method to return +// current tree state. +type CacheClient struct { + *BaseClient + *ctree.Tree + synced chan struct{} + clientHandler NotificationHandler +} + +var _ Client = &CacheClient{} + +// New returns an initialized caching client. +func New() *CacheClient { + c := &CacheClient{ + BaseClient: &BaseClient{}, + Tree: &ctree.Tree{}, + synced: make(chan struct{}), + } + return c +} + +// Subscribe implements the Client interface. +func (c *CacheClient) Subscribe(ctx context.Context, q Query, clientType ...string) error { + q.ProtoHandler = nil + if q.NotificationHandler != nil { + c.clientHandler = q.NotificationHandler + } + q.NotificationHandler = c.defaultHandler + return c.BaseClient.Subscribe(ctx, q, clientType...) +} + +// defaultHandler is passed into the client specific implementations. It will +// be called for each leaf notification generated by the client. +func (c *CacheClient) defaultHandler(n Notification) error { + switch v := n.(type) { + default: + return fmt.Errorf("invalid type %#v", v) + case Connected: // Ignore. + case Error: + return fmt.Errorf("received error: %v", v) + case Update: + c.Add(v.Path, TreeVal{TS: v.TS, Val: v.Val}) + case Delete: + c.Delete(v.Path) + case Sync: + select { + default: + close(c.synced) + case <-c.synced: + } + } + if c.clientHandler != nil { + return c.clientHandler(n) + } + return nil +} + +// Poll implements the Client interface. +// Poll also closes the channel returned by Synced and resets it. +func (c *CacheClient) Poll() error { + select { + default: + close(c.synced) + case <-c.synced: + } + return c.BaseClient.Poll() +} + +// Synced will close when a sync is recieved from the query. +func (c *CacheClient) Synced() <-chan struct{} { + return c.synced +} + +// Leaves returns the current state of the received tree. It's safe to call at +// any point after New. +func (c *CacheClient) Leaves() Leaves { + // Convert node items into Leaf (expand TreeVal leaves). + var pvs Leaves + c.WalkSorted(func(path []string, _ *ctree.Leaf, value interface{}) error { + tv, ok := value.(TreeVal) + if !ok { + return fmt.Errorf("Invalid value in tree: %s=%#v", path, value) + } + pvs = append(pvs, Leaf{Path: path, Val: tv.Val, TS: tv.TS}) + return nil + }) + return pvs +} diff --git a/deps/github.com/openconfig/gnmi/client/cache_test.go b/deps/github.com/openconfig/gnmi/client/cache_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c4dff8e8184620915e928733892b66717a1dcf38 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/cache_test.go @@ -0,0 +1,244 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client_test + +import ( + "context" + "errors" + "fmt" + "reflect" + "testing" + "time" + + "github.com/openconfig/gnmi/client" + fake "github.com/openconfig/gnmi/client/fake" +) + +const ( + cacheTest = "cacheTest" + cacheFail = "cacheFail" +) + +func TestPollCache(t *testing.T) { + tests := []struct { + desc string + q client.Query + u [][]interface{} + want []client.Leaves + err bool + }{{ + desc: "invalid query type", + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + NotificationHandler: func(client.Notification) error { return nil }, + }, + u: [][]interface{}{{ + client.Update{TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + client.Sync{}, + }, {}}, + want: []client.Leaves{{ + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + }, {}}, + err: true, + }, { + desc: "Poll With Entry Test", + q: client.Query{ + Type: client.Poll, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + NotificationHandler: func(client.Notification) error { return nil }, + }, + u: [][]interface{}{{ + client.Update{TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + client.Sync{}, + }, { + client.Update{TS: time.Unix(3, 0), Path: client.Path{"a", "b"}, Val: 1}, + client.Sync{}, + }}, + want: []client.Leaves{{ + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + }, { + {TS: time.Unix(3, 0), Path: client.Path{"a", "b"}, Val: 1}, + }}, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + fake.Mock(cacheTest, tt.u[0]) + c := client.New() + defer c.Close() + if err := c.Subscribe(context.Background(), tt.q, cacheTest); err != nil { + t.Errorf("Subscribe() failed: %v", err) + } + l := c.Leaves() + if !reflect.DeepEqual(l, tt.want[0]) { + t.Fatalf("Unexpected updates: got:\n%v\nwant:\n%v", l, tt.want[0]) + } + impl, err := c.Impl() + if err != nil { + t.Fatalf("c.Impl: %v", err) + } + impl.(*fake.Client).Reset(tt.u[1]) + err = c.Poll() + switch { + case err != nil && tt.err: + return + case err != nil && !tt.err: + t.Errorf("Poll() failed: %v", err) + return + case err == nil && tt.err: + t.Errorf("Poll() expected error.") + return + } + l = c.Leaves() + if !reflect.DeepEqual(l, tt.want[1]) { + t.Fatalf("Unexpected updates: got:\n%v\nwant:\n%v", l, tt.want[1]) + } + }) + } +} + +func TestCache(t *testing.T) { + fake.Mock(cacheFail, []interface{}{errors.New("client failed")}) + + nTest := false + tests := []struct { + desc string + q client.Query + u []interface{} + clientType []string + want client.Leaves + err bool + }{{ + desc: "Error New", + clientType: []string{cacheFail}, + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + }, + want: nil, + err: true, + }, { + desc: "Once Test", + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + }, + u: []interface{}{ + client.Update{TS: time.Unix(1, 0), Path: client.Path{"a", "b"}, Val: 1}, + client.Delete{TS: time.Unix(2, 0), Path: client.Path{"a", "b"}}, + client.Sync{}, + }, + want: nil, + }, { + desc: "Once With Entry Test", + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + }, + u: []interface{}{ + client.Update{TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + client.Sync{}, + }, + want: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + }, + }, { + desc: "Custom handler with Sync test", + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + NotificationHandler: func(n client.Notification) error { + if _, ok := n.(client.Sync); ok { + nTest = true + } + return nil + }, + }, + u: []interface{}{ + client.Update{TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + client.Sync{}, + }, + want: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + }, + }, { + desc: "Error on notification", + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + }, + u: []interface{}{ + client.Error{}, + }, + err: true, + }, { + desc: "Error on Recv", + q: client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + }, + u: []interface{}{ + fmt.Errorf("Recv() error"), + }, + err: true, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + fake.Mock(cacheTest, tt.u) + + c := client.New() + defer c.Close() + if tt.q.NotificationHandler != nil { + go func() { + <-c.Synced() + if !nTest { + t.Errorf("Synced() failed: got %v, want true", nTest) + } + }() + } else { + tt.q.NotificationHandler = func(client.Notification) error { return nil } + } + clientType := []string{cacheTest} + if tt.clientType != nil { + clientType = tt.clientType + } + err := c.Subscribe(context.Background(), tt.q, clientType...) + switch { + case err != nil && tt.err: + return + case err != nil && !tt.err: + t.Errorf("Subscribe() failed: %v", err) + return + case err == nil && tt.err: + t.Errorf("Subscribe() expected error.") + return + } + l := c.Leaves() + if !reflect.DeepEqual(l, tt.want) { + t.Fatalf("Unexpected updates: got:\n%v\nwant:\n%v", l, tt.want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/client/client.go b/deps/github.com/openconfig/gnmi/client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..007bc68ef53c7d4eb86cd03a7b5027df6e479982 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/client.go @@ -0,0 +1,215 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package client provides a generic access layer for streaming telemetry +// providers. +// +// The Client interface is implemented by 3 types in this package: +// +// - BaseClient simply forwards all messages from the underlying connection to +// NotificationHandler or ProtoHandler (see type Query). +// +// - CacheClient wraps around BaseClient and adds a persistence layer for all +// notifications. The notifications build up an internal tree which can be +// queried and walked using CacheClient's methods. +// +// - ReconnectClient wraps around any Client implementation (BaseClient, +// CacheClient or a user-provided one) and adds transparent reconnection loop +// in Subscribe. Reconnection attempts are done with exponential backoff. +// +// This package uses pluggable transport implementations. For example, for gNMI +// targets you need to add this blank import: +// import _ "github.com/openconfig/gnmi/client/gnmi" +// +// That import will automatically register itself as available ClientType in +// this package (using func init). +// +// If you want to write a custom implementation, implement Impl interface and +// register it with unique name via func Register. +// +// Take a look at package examples in godoc for typical use cases. +package client + +import ( + "context" + "errors" + "fmt" + "io" + "sync" + + log "github.com/golang/glog" +) + +// Client defines a set of methods which every client must implement. +// This package provides a few implementations: BaseClient, CacheClient, +// ReconnectClient. +// +// Do not confuse this with Impl. +type Client interface { + // Subscribe will perform the provided query against the requested + // clientType. clientType is the name of a specific Impl specified in + // Register (most implementations will call Register in init()). + // + // It will try each clientType listed in order until one succeeds. If + // clientType is nil, it will try each registered clientType in random + // order. + Subscribe(ctx context.Context, q Query, clientType ...string) error + // Poll will send a poll request to the server and process all + // notifications. It is up the caller to identify the sync and realize the + // Poll is complete. + Poll() error + // Close terminates the underlying Impl, which usually terminates the + // connection right away. + // Close must be called to release any resources that Impl could have + // allocated. + Close() error + // Impl will return the underlying client implementation. Most users + // shouldn't use this. + Impl() (Impl, error) +} + +var ( + // ErrStopReading is the common error defined to have the client stop a read + // loop. + ErrStopReading = errors.New("stop the result reading loop") + // ErrClientInit is the common error for when making calls before the client + // has been started via Subscribe. + ErrClientInit = errors.New("Subscribe() must be called before any operations on client") + // ErrUnsupported is returned by Impl's methods when the underlying + // implementation doesn't support it. + ErrUnsupported = errors.New("operation not supported by client implementation") +) + +// BaseClient is a streaming telemetry client with minimal footprint. The +// caller must call Subscribe to perform the actual query. BaseClient stores no +// state. All updates must be handled by the provided handlers inside of +// Query. +// +// The zero value of BaseClient is ready for use (there is no constructor). +type BaseClient struct { + mu sync.RWMutex + closed bool + clientImpl Impl + + query Query +} + +var _ Client = &BaseClient{} + +// Subscribe implements the Client interface. +func (c *BaseClient) Subscribe(ctx context.Context, q Query, clientType ...string) error { + if err := q.Validate(); err != nil { + return err + } + if len(clientType) == 0 { + clientType = RegisteredImpls() + } + + // TODO: concurrent subscribes can be removed after we enforce reflection + // at client Impl level. + fn := func(ctx context.Context, typ string, input interface{}) (Impl, error) { + q := input.(Query) + impl, err := NewImpl(ctx, q.Destination(), typ) + if err != nil { + return nil, err + } + if err := impl.Subscribe(ctx, q); err != nil { + impl.Close() + return nil, err + } + return impl, nil + } + impl, err := getFirst(ctx, clientType, q, fn) + if err != nil { + return err + } + + c.mu.Lock() + c.query = q + if c.clientImpl != nil { + c.clientImpl.Close() + } + c.clientImpl = impl + c.closed = false + c.mu.Unlock() + + return c.run(impl) +} + +// Poll implements the Client interface. +func (c *BaseClient) Poll() error { + impl, err := c.Impl() + if err != nil { + return ErrClientInit + } + if c.query.Type != Poll { + return fmt.Errorf("Poll() can only be used on Poll query type: %v", c.query.Type) + } + if err := impl.Poll(); err != nil { + return err + } + return c.run(impl) +} + +// Close implements the Client interface. +func (c *BaseClient) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + if c.clientImpl == nil { + return ErrClientInit + } + c.closed = true + return c.clientImpl.Close() +} + +// Impl implements the Client interface. +func (c *BaseClient) Impl() (Impl, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.clientImpl == nil { + return nil, ErrClientInit + } + return c.clientImpl, nil +} + +func (c *BaseClient) run(impl Impl) error { + for { + err := impl.Recv() + switch err { + default: + log.V(1).Infof("impl.Recv() received unknown error: %v", err) + impl.Close() + return err + case io.EOF, ErrStopReading: + log.V(1).Infof("impl.Recv() stop marker: %v", err) + return nil + case nil: + } + + // Close fast, so that we don't deliver any buffered updates. + // + // Note: this approach still allows at most 1 update through after + // Close. A more thorough solution would be to do the check at + // Notification/ProtoHandler or Impl level, but that would involve much + // more work. + c.mu.RLock() + closed := c.closed + c.mu.RUnlock() + if closed { + return nil + } + } +} diff --git a/deps/github.com/openconfig/gnmi/client/client_test.go b/deps/github.com/openconfig/gnmi/client/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f4dd41aa779376156ead0e08d3ee7cb1d94a8010 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/client_test.go @@ -0,0 +1,566 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client_test + +import ( + "context" + "fmt" + "reflect" + "sort" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/client" + fclient "github.com/openconfig/gnmi/client/fake" +) + +const ( + defaultQuery = "*" +) + +func testImpl(ctx context.Context, d client.Destination) (client.Impl, error) { + if len(d.Addrs) > 0 && d.Addrs[0] == "error" { + return nil, fmt.Errorf("error") + } + return fclient.New(ctx, d) +} + +func TestRegister(t *testing.T) { + // In case some other test forgot, clean out registered impls. + client.ResetRegisteredImpls() + // Verify Reset + if got := client.RegisteredImpls(); got != nil { + t.Fatalf("client.ResetRegisteredImpls() failed: got %v want nil", got) + } + // Clean out what we registered. + defer client.ResetRegisteredImpls() + + // Registered names must not be reused unless you expect an error in duplicate + tests := []struct { + desc string + name string + f client.InitImpl + clientType []string + rErr bool + nErr bool + }{{ + desc: "Missing Impl", + name: "foo", + rErr: true, + }, { + desc: "No registration, unspecified client", + nErr: true, + }, { + desc: "Name only", + name: "foo", + rErr: true, + }, { + desc: "Valid Client", + name: "bar", + f: testImpl, + clientType: []string{"bar"}, + nErr: false, + }, { + desc: "Unspecified client with prior registeration", + nErr: false, + }, { + desc: "Duplicate Registration", + name: "bar", + f: testImpl, + clientType: []string{"bar"}, + rErr: true, + }, { + desc: "Unknown Registration", + name: "foobar", + f: testImpl, + clientType: []string{"zbaz"}, + nErr: true, + }, { + desc: "Multiple clients, one valid", + clientType: []string{"zbaz", "bar"}, + nErr: false, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if tt.name != "" { + err := client.Register(tt.name, tt.f) + switch { + case tt.rErr && err == nil: + t.Fatalf("Register(%q, %v) unexpected success", tt.name, tt.f) + case !tt.rErr && err != nil: + t.Fatalf("Register(%q, %v) failed: %v", tt.name, tt.f, err) + case tt.rErr && err != nil: + return + } + } + err := client.New().Subscribe(context.Background(), client.Query{ + Type: client.Once, + Addrs: []string{"fake"}, + Queries: []client.Path{{"*"}}, + NotificationHandler: func(client.Notification) error { return nil }, + }, tt.clientType...) + switch { + case tt.nErr && err == nil: + t.Fatalf("Subscribe() unexpected success") + case !tt.nErr && err != nil: + t.Fatalf("Subscribe() failed: %v", err) + case tt.nErr && err != nil: + return + } + }) + } +} + +func TestRegisterHangingImpl(t *testing.T) { + // This test makes sure that a hanging client.NewImpl (due to blocked + // InitImpl, e.g. waiting for timeout) doesn't prevent other client.NewImpl + // calls from blocking too. This may happen due to a global mutex in + // register.go + + // In case some other test forgot, clean out registered impls. + client.ResetRegisteredImpls() + // Clean out what we registered. + defer client.ResetRegisteredImpls() + + blocked := make(chan struct{}) + client.Register("blocking", func(ctx context.Context, _ client.Destination) (client.Impl, error) { + close(blocked) + // Block until test returns. + <-ctx.Done() + return nil, nil + }) + client.Register("regular", func(context.Context, client.Destination) (client.Impl, error) { + return nil, nil + }) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + connected := make(chan string, 1) + go func() { + client.NewImpl(ctx, client.Destination{}, "blocking") + connected <- "blocking" + }() + go func() { + // Wait for blocking Impl to start blocking. + <-blocked + client.NewImpl(ctx, client.Destination{}, "regular") + connected <- "regular" + }() + + select { + case got := <-connected: + if got != "regular" { + t.Errorf(`connected Impl %q, want "regular"`, got) + } + case <-time.After(5 * time.Second): + t.Error("blocking InitImpl prevents regular InitImpl from connecting (waiter 5s)") + } +} + +func TestQuery(t *testing.T) { + tests := []struct { + desc string + in client.Query + wantPath []client.Path + err bool + client []string + }{{ + desc: "Empty Query", + in: client.Query{}, + err: true, + wantPath: []client.Path{{defaultQuery}}, + }, { + desc: "No Addr", + wantPath: []client.Path{{defaultQuery}}, + in: client.Query{ + Queries: []client.Path{{"foo", "bar"}, {"a", "b"}}, + Type: client.Once, + }, + err: true, + }, { + desc: "No Target", + wantPath: []client.Path{{defaultQuery}}, + in: client.Query{ + Addrs: []string{"fake addr"}, + Queries: []client.Path{{"foo", "bar"}, {"a", "b"}}, + Type: client.Once, + }, + err: true, + }, { + desc: "No Type", + wantPath: []client.Path{{defaultQuery}}, + in: client.Query{ + Addrs: []string{"fake addr"}, + Target: "", + Queries: []client.Path{{"foo", "bar"}, {"a", "b"}}, + }, + err: true, + }, { + desc: "No Queries", + wantPath: []client.Path{{defaultQuery}}, + in: client.Query{ + Addrs: []string{"fake addr"}, + Target: "", + Type: client.Once, + }, + err: true, + }, { + desc: "Both handlers set", + wantPath: []client.Path{{"foo", "bar"}, {"a", "b"}}, + in: client.Query{ + Addrs: []string{"fake addr"}, + Target: "", + Queries: []client.Path{{"foo", "bar"}, {"a", "b"}}, + Type: client.Once, + NotificationHandler: func(_ client.Notification) error { return nil }, + ProtoHandler: func(_ proto.Message) error { return nil }, + }, + err: true, + }, { + desc: "Valid Query", + wantPath: []client.Path{{"foo", "bar"}, {"a", "b"}}, + in: client.Query{ + Addrs: []string{"fake addr"}, + Target: "", + Queries: []client.Path{{"foo", "bar"}, {"a", "b"}}, + Type: client.Once, + NotificationHandler: func(_ client.Notification) error { return nil }, + }, + }, { + desc: "Password contains forbidden characters", + in: client.Query{ + Credentials: &client.Credentials{Password: "\n"}, + }, + err: true, + }, { + desc: "Username contains forbidden characters", + in: client.Query{ + Credentials: &client.Credentials{Username: "\n"}, + }, + err: true, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + err := tt.in.Validate() + switch { + case err != nil && tt.err: + return + case err != nil && !tt.err: + t.Errorf("Validate() failed: %v", err) + return + case err == nil && tt.err: + t.Errorf("Validate() expected error.") + return + } + if !reflect.DeepEqual(tt.wantPath, tt.in.Queries) { + t.Errorf("Validate() failed: got %v, want %v", tt.in.Queries, tt.wantPath) + } + }) + } +} + +func TestLeaves(t *testing.T) { + tests := []struct { + desc string + in client.Leaves + want client.Leaves + }{{ + desc: "sorted", + in: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"a"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b", "c"}, Val: 1}, + }, + want: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"a"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b", "c"}, Val: 1}, + }, + }, { + desc: "unsorted", + in: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1}, + }, + want: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1}, + }, + }, { + desc: "stable", + in: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 2}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3}, + }, + want: client.Leaves{ + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 2}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3}, + }, + }, { + desc: "nil path", + in: client.Leaves{ + {TS: time.Unix(0, 0), Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3}, + }, + want: client.Leaves{ + {TS: time.Unix(0, 0), Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2}, + {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3}, + }, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got := make(client.Leaves, len(tt.in)) + copy(got, tt.in) + sort.Sort(got) + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("sort.Sort(%v) failed: got %v, want %v", tt.in, got, tt.want) + } + }) + } +} + +func TestPath(t *testing.T) { + tests := []struct { + desc string + in client.Path + cmp client.Path + want bool + }{{ + desc: "same", + in: client.Path{"a", "b", "c"}, + cmp: client.Path{"a", "b", "c"}, + want: true, + }, { + desc: "different length", + in: client.Path{"a", "b", "c"}, + cmp: client.Path{"a", "b"}, + want: false, + }, { + desc: "different", + in: client.Path{"a", "b", "c"}, + cmp: client.Path{"a", "b", "d"}, + want: false, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := tt.in.Equal(tt.cmp); got != tt.want { + t.Fatalf("%+v.Equal(%+v) failed: got %v, want %v", tt.in, tt.cmp, got, tt.want) + } + }) + } +} + +func TestNewType(t *testing.T) { + tests := []struct { + desc string + in string + want client.Type + }{{ + desc: "Unknown", + in: "foo", + want: client.Unknown, + }, { + desc: "Once", + in: "once", + want: client.Once, + }, { + desc: "Stream", + in: "stream", + want: client.Stream, + }, { + desc: "Poll", + in: "poll", + want: client.Poll, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := client.NewType(tt.in); got != tt.want { + t.Fatalf("client.NewType(%+v) failed: got %v, want %v", tt.in, got, tt.want) + } + }) + } +} + +func TestError(t *testing.T) { + want := "foo" + e := client.NewError(want) + if got := e.Error(); got != want { + t.Errorf("client.NewError(%q) failed: got %v, want %v", want, got, want) + } +} + +func TestClientUpdatesAfterClose(t *testing.T) { + client.ResetRegisteredImpls() + + fake := fakeStreamingClient{ch: make(chan struct{})} + client.Register("fake", func(context.Context, client.Destination) (client.Impl, error) { + return fake, nil + }) + + c := &client.BaseClient{} + done := make(chan struct{}) + go func() { + defer close(done) + err := c.Subscribe(context.Background(), client.Query{ + Addrs: []string{"fake"}, + Type: client.Stream, + Queries: []client.Path{{"*"}}, + NotificationHandler: func(client.Notification) error { return nil }, + }, "fake") + if err != nil { + t.Errorf("Subscribe(): %v", err) + } + }() + + for i := 0; i < 10; i++ { + select { + case fake.ch <- struct{}{}: + case <-done: + t.Fatal("Subscribe returned before close") + } + } + c.Close() + + var updatesAfterClose int +loop: + for { + select { + case fake.ch <- struct{}{}: + updatesAfterClose++ + case <-done: + break loop + } + } + if updatesAfterClose > 1 { + t.Errorf("got %d updates after Close, expect at most 1", updatesAfterClose) + } +} + +type fakeStreamingClient struct { + client.Impl + ch chan struct{} +} + +func (f fakeStreamingClient) Subscribe(context.Context, client.Query) error { + return nil +} + +func (f fakeStreamingClient) Recv() error { + <-f.ch + return nil +} + +func (f fakeStreamingClient) Close() error { return nil } + +// Once client will run the query and once complete you can act on the +// returned tree. +func ExampleClient_Once() { + q := client.Query{ + Addrs: []string{"127.0.0.1:1234"}, + Target: "dev", + Queries: []client.Path{{"*"}}, + Type: client.Once, + } + c := client.New() + defer c.Close() + err := c.Subscribe(context.Background(), q) + if err != nil { + fmt.Println(err) + return + } + for _, v := range c.Leaves() { + fmt.Printf("%v: %v\n", v.Path, v.Val) + } +} + +// Poll client is like Once client, but can be re-triggered via Poll to +// re-execute the query. +func ExampleClient_Poll() { + q := client.Query{ + Addrs: []string{"127.0.0.1:1234"}, + Target: "dev", + Queries: []client.Path{{"*"}}, + Type: client.Poll, + } + c := client.New() + defer c.Close() + err := c.Subscribe(context.Background(), q) + if err != nil { + fmt.Println(err) + return + } + for _, v := range c.Leaves() { + fmt.Printf("%v: %v\n", v.Path, v.Val) + } + err = c.Poll() // Poll allows the underyling Query to keep running + if err != nil { + fmt.Println(err) + return + } + for _, v := range c.Leaves() { + fmt.Printf("%v: %v\n", v.Path, v.Val) + } +} + +// Stream client returns the current state for the query and keeps running +// until closed or the underlying connection breaks. +func ExampleClient_Stream() { + q := client.Query{ + Addrs: []string{"127.0.0.1:1234"}, + Target: "dev", + Queries: []client.Path{{"*"}}, + Type: client.Stream, + NotificationHandler: func(n client.Notification) error { + switch nn := n.(type) { + case client.Connected: + fmt.Println("client is connected") + case client.Sync: + fmt.Println("client is synced") + case client.Update, client.Delete: + fmt.Printf("update: %+v\n", nn) + case client.Error: + fmt.Printf("error: %v\n", nn) + } + return nil + }, + } + c := client.New() + defer c.Close() + // Note that Subscribe will block. + err := c.Subscribe(context.Background(), q) + if err != nil { + fmt.Println(err) + } +} diff --git a/deps/github.com/openconfig/gnmi/client/fake/fake.go b/deps/github.com/openconfig/gnmi/client/fake/fake.go new file mode 100644 index 0000000000000000000000000000000000000000..9fe4124fbc5518423e1ce6cb7cc7105d2b4db57f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/fake/fake.go @@ -0,0 +1,165 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package client implements a fake client implementation to be used with +// streaming telemetry collection. It provides a simple Updates queue of data +// to send it should be used to provide an RPC free test infra for user facing +// libraries. +package client + +import ( + "context" + "fmt" + + log "github.com/golang/glog" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/client" +) + +// New can be replaced for any negative testing you would like to do as well. +// +// New exists for compatibility reasons. Most new clients should use Mock. +// Mock ensures that q.NotificationHandler and ctx aren't forgotten. +var New = func(ctx context.Context, _ client.Destination) (client.Impl, error) { + return &Client{Context: ctx}, nil +} + +// Mock overrides a client implementation named typ (most implementation +// libraries have Type constant containing that name) with a fake client +// sending given updates. +// +// See Client documentation about updates slice contents. +func Mock(typ string, updates []interface{}) { + client.RegisterTest(typ, func(ctx context.Context, _ client.Destination) (client.Impl, error) { + c := &Client{ + Context: ctx, + Updates: updates, + } + return c, nil + }) +} + +// Client is the fake of a client implementation. It will provide a simple +// list of updates to send to the generic client. +// +// The Updates slice can contain: +// - client.Notification: passed to query.NotificationHandler +// - proto.Message: passed to query.ProtoHandler +// - error: returned from Recv, interrupts the update stream +// - Block: pauses Recv, proceeds to next update on Unblock +// +// See ExampleClient for sample use case. +type Client struct { + currUpdate int + Updates []interface{} + Handler client.NotificationHandler + ProtoHandler client.ProtoHandler + // BlockAfterSync is deprecated: use Block update as last Updates slice + // element instead. + // + // When BlockAfterSync is set, Client will read from it in Recv after + // sending all Updates before returning ErrStopReading. + // BlockAfterSync is closed when Close is called. + BlockAfterSync chan struct{} + connected bool + Context context.Context +} + +// Subscribe implements the client.Impl interface. +func (c *Client) Subscribe(ctx context.Context, q client.Query) error { + c.Handler = q.NotificationHandler + c.ProtoHandler = q.ProtoHandler + return nil +} + +// Reset will reset the client to start playing new updates. +func (c *Client) Reset(u []interface{}) { + c.currUpdate = 0 + c.Updates = u +} + +// Recv will be called for each update the generic client wants to receive. +func (c *Client) Recv() error { + if c.Context == nil { + c.Context = context.Background() + } + if !c.connected && c.Handler != nil { + c.Handler(client.Connected{}) + c.connected = true + } + + for c.currUpdate < len(c.Updates) { + u := c.Updates[c.currUpdate] + c.currUpdate++ + log.V(1).Infof("fake client update: %v", u) + switch v := u.(type) { + case client.Notification: + if c.Handler == nil { + return fmt.Errorf("update %+v is client.Notification but query.NotificationHandler wasn't set", v) + } + return c.Handler(v) + case proto.Message: + if c.ProtoHandler == nil { + return fmt.Errorf("update %+v is proto.Message but query.ProtoHandler wasn't set", v) + } + return c.ProtoHandler(v) + case error: + return v + case Block: + select { + case <-c.Context.Done(): + return c.Context.Err() + case <-v: + } + } + } + + if c.Handler != nil { + c.Handler(client.Sync{}) + } + // We went through all c.Update items. + if c.BlockAfterSync != nil { + log.Info("No more updates, blocking on BlockAfterSync") + select { + case <-c.Context.Done(): + return c.Context.Err() + case <-c.BlockAfterSync: + } + } + log.Infof("Recv() returning %v", client.ErrStopReading) + return client.ErrStopReading +} + +// Close is a noop in the fake. +func (c *Client) Close() error { + if c.BlockAfterSync != nil { + close(c.BlockAfterSync) + } + return nil +} + +// Poll is a noop in the fake. +func (c *Client) Poll() error { + return nil +} + +// Block is a special update that lets the stream of updates to be paused. +// See Client docs for usage example. +type Block chan struct{} + +// Unblock unpauses the update stream following the Block. Can only be called +// once. +func (b Block) Unblock() { close(b) } diff --git a/deps/github.com/openconfig/gnmi/client/fake/fake_test.go b/deps/github.com/openconfig/gnmi/client/fake/fake_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f2b33c9d80f9df1414e869898dd3ef08319354a7 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/fake/fake_test.go @@ -0,0 +1,69 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/openconfig/gnmi/client" +) + +func ExampleClient() { + block := make(Block) + Mock("fake", []interface{}{ + client.Update{Path: []string{"target", "a", "b"}, Val: 1, TS: time.Now()}, + client.Update{Path: []string{"target", "a", "c"}, Val: 2, TS: time.Now()}, + block, + client.Delete{Path: []string{"target", "a", "b"}, TS: time.Now()}, + errors.New("unexpected error"), + }) + + // Unblock the stream after a second. + go func() { + time.Sleep(time.Second) + block.Unblock() + }() + + err := client.New().Subscribe(context.Background(), client.Query{ + Addrs: []string{""}, + Queries: []client.Path{{"*"}}, + Type: client.Once, + NotificationHandler: func(n client.Notification) error { + switch nn := n.(type) { + case client.Connected: + fmt.Println("connected") + case client.Sync: + fmt.Println("sync") + case client.Update: + fmt.Printf("%q: %v\n", nn.Path, nn.Val) + case client.Delete: + fmt.Printf("%q deleted\n", nn.Path) + } + return nil + }, + }) + fmt.Println("got error:", err) + // Output: + // connected + // ["target" "a" "b"]: 1 + // ["target" "a" "c"]: 2 + // ["target" "a" "b"] deleted + // got error: unexpected error +} diff --git a/deps/github.com/openconfig/gnmi/client/flags/flags_test.go b/deps/github.com/openconfig/gnmi/client/flags/flags_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7b4d5f3ec93d988a49a600d40b73d92614dd6586 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/flags/flags_test.go @@ -0,0 +1,246 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flags + +import ( + "flag" + "reflect" + "strings" + "testing" +) + +var tests = []struct { + desc string + in []string + wantStringList StringList + wantStringMap StringMap + wantIntMap IntMap + wantErr string +}{{ + desc: "empty args", + in: []string{}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, +}, { + desc: "Stringlist args", + in: []string{"-test_stringList=asdf,foo"}, + wantStringList: []string{"asdf", "foo"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, +}, { + desc: "StringMap args", + in: []string{"-test_stringMap=a=10,b=20"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "10", "b": "20"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, +}, { + desc: "StringMap args (missing default key)", + in: []string{"-test_stringMap=a=10,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "10", "c": "30"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, +}, { + desc: "StringMap args (invalid key/value)", + in: []string{"-test_stringMap=a10,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "10", "c": "30"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, + wantErr: "invalid value", +}, { + desc: "StringMap args (nil key)", + in: []string{"-test_stringMap==10,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "10", "c": "30"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, + wantErr: "invalid value", +}, { + desc: "StringMap args (empty)", + in: []string{"-test_stringMap="}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, + wantErr: "invalid value", +}, { + desc: "IntMap args", + in: []string{"-test_intMap=a=10,b=20"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "b": 20}, +}, { + desc: "IntMap args (additional)", + in: []string{"-test_intMap=a=10,b=20,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "b": 20, "c": 30}, +}, { + desc: "IntMap args (additional spaces)", + in: []string{"-test_intMap=a= 10, b=20, c= 30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "b": 20, "c": 30}, +}, { + desc: "IntMap args (parse error)", + in: []string{"-test_intMap=a= 10, b=20, c=asdf"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "b": 20, "c": 30}, + wantErr: "invalid value", +}, { + desc: "IntMap args (missing default key)", + in: []string{"-test_intMap=a=10,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "c": 30}, + wantErr: "invalid value", +}, { + desc: "IntMap args (invalid key/value)", + in: []string{"-test_intMap=a10,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "c": 30}, + wantErr: "invalid value", +}, { + desc: "IntMap args (nil key)", + in: []string{"-test_intMap==10,c=30"}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{"a": 10, "c": 30}, + wantErr: "invalid value", +}, { + desc: "IntMap args (empty)", + in: []string{"-test_intMap="}, + wantStringList: []string{"0"}, + wantStringMap: map[string]string{"a": "1", "b": "2"}, + wantIntMap: map[string]int64{}, + wantErr: "invalid value", +}, { + desc: "All Set", + in: []string{"-test_intMap=a=1,b=2", "-test_stringMap=foo=bar,baz=faz", "-test_stringList=asdf,foo,bar"}, + wantStringList: []string{"asdf", "foo", "bar"}, + wantStringMap: map[string]string{"foo": "bar", "baz": "faz"}, + wantIntMap: map[string]int64{"a": 1, "b": 2}, + wantErr: "invalid value", +}} + +func TestFlags(t *testing.T) { + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + stringList := StringList{"0"} + stringMap := StringMap{"a": "1", "b": "2"} + intMap := IntMap{"a": 1, "b": 2} + f := &flag.FlagSet{} + f.Var(&stringList, "test_stringList", "[]string value") + f.Var(&stringMap, "test_stringMap", "map[string]string") + f.Var(&intMap, "test_intMap", "map[string]int64") + err := f.Parse(tt.in) + if err != nil { + if strings.Contains(err.Error(), tt.wantErr) { + return + } + t.Errorf("flag.CommandLine.Parse(%v) err: %v", tt.in, err) + } + if !reflect.DeepEqual(stringList, tt.wantStringList) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if !reflect.DeepEqual(stringList.Get().(StringList), tt.wantStringList) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList.Get() got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if stringList.String() != tt.wantStringList.String() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %q, want %q", tt.in, stringList.String(), tt.wantStringList.String()) + } + if !reflect.DeepEqual(stringMap, tt.wantStringMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringMap got %v, want %v", tt.in, stringMap, tt.wantStringMap) + } + if !reflect.DeepEqual(stringMap.Get().(StringMap), tt.wantStringMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringMap.Get() got %v, want %v", tt.in, stringMap, tt.wantStringMap) + } + if stringMap.String() != tt.wantStringMap.String() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %q, want %q", tt.in, stringMap.String(), tt.wantStringMap.String()) + } + if !reflect.DeepEqual(intMap, tt.wantIntMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: intMap got %v, want %v", tt.in, intMap, tt.wantIntMap) + } + if !reflect.DeepEqual(intMap.Get().(IntMap), tt.wantIntMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: intMap.Get() got %v, want %v", tt.in, intMap, tt.wantIntMap) + } + if intMap.String() != tt.wantIntMap.String() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %q, want %q", tt.in, intMap.String(), tt.wantIntMap.String()) + } + }) + } +} + +func TestNewFlags(t *testing.T) { + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + listVar := []string{} + stringMapVar := map[string]string{} + intMapVar := map[string]int64{} + stringList := NewStringList(&listVar, []string{"0"}) + stringMap := NewStringMap(&stringMapVar, map[string]string{"a": "1", "b": "2"}) + intMap := NewIntMap(&intMapVar, map[string]int64{"a": 1, "b": 2}) + f := &flag.FlagSet{} + f.Var(stringList, "test_stringList", "[]string value") + f.Var(stringMap, "test_stringMap", "map[string]string") + f.Var(intMap, "test_intMap", "map[string]int64") + err := f.Parse(tt.in) + if err != nil { + if strings.Contains(err.Error(), tt.wantErr) { + return + } + t.Errorf("flag.CommandLine.Parse(%v) err: %v", tt.in, err) + } + if reflect.ValueOf(listVar).Pointer() != reflect.ValueOf(*stringList).Pointer() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList Pointer() got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if !reflect.DeepEqual(*stringList, tt.wantStringList) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if !reflect.DeepEqual(stringList.Get().(StringList), tt.wantStringList) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList.Get() got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if stringList.String() != tt.wantStringList.String() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %q, want %q", tt.in, stringList.String(), tt.wantStringList.String()) + } + if reflect.ValueOf(stringMapVar).Pointer() != reflect.ValueOf(*stringMap).Pointer() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringMap Pointer() got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if !reflect.DeepEqual(*stringMap, tt.wantStringMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringMap got %v, want %v", tt.in, stringMap, tt.wantStringMap) + } + if !reflect.DeepEqual(stringMap.Get().(StringMap), tt.wantStringMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringMap.Get() got %v, want %v", tt.in, stringMap, tt.wantStringMap) + } + if stringMap.String() != tt.wantStringMap.String() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %q, want %q", tt.in, stringMap.String(), tt.wantStringMap.String()) + } + if reflect.ValueOf(intMapVar).Pointer() != reflect.ValueOf(*intMap).Pointer() { + t.Errorf("flag.CommandLine.Parse(%v) failed: intMap Pointer() got %v, want %v", tt.in, stringList, tt.wantStringList) + } + if !reflect.DeepEqual(*intMap, tt.wantIntMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: intMap got %v, want %v", tt.in, intMap, tt.wantIntMap) + } + if !reflect.DeepEqual(intMap.Get().(IntMap), tt.wantIntMap) { + t.Errorf("flag.CommandLine.Parse(%v) failed: intMap.Get() got %v, want %v", tt.in, intMap, tt.wantIntMap) + } + if intMap.String() != tt.wantIntMap.String() { + t.Errorf("flag.CommandLine.Parse(%v) failed: stringList got %q, want %q", tt.in, intMap.String(), tt.wantIntMap.String()) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/client/flags/intmap.go b/deps/github.com/openconfig/gnmi/client/flags/intmap.go new file mode 100644 index 0000000000000000000000000000000000000000..e532a13aea874021d88961eff16bb09b8f9bc411 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/flags/intmap.go @@ -0,0 +1,79 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flags + +import ( + "fmt" + "sort" + "strconv" + "strings" +) + +// IntMap is a map[string]int64 container for command line flags. The default +// initialization will create an empty map. If you need default backing map use +// NewIntMap. +type IntMap map[string]int64 + +func (m *IntMap) String() string { + s := make([]string, 0, len(*m)) + for k, v := range *m { + s = append(s, fmt.Sprintf("%s=%d", k, v)) + } + sort.Strings(s) + return strings.Join(s, ",") +} + +// Get returns the values of m. The interface will need to be type asserted to +// IntMap for use. +func (m *IntMap) Get() interface{} { + return *m +} + +// Set will take a string in the format <key1>=<value1>,<key2>=<value2> and +// parse the resulting value into a map[string]int64. Values may contain "=", +// keys may not. +func (m *IntMap) Set(v string) error { + *m = IntMap{} + for _, entry := range strings.Split(v, ",") { + data := strings.SplitN(entry, "=", 2) + if len(data) != 2 { + return fmt.Errorf("invalid key=value pair: %s", entry) + } + k := strings.TrimSpace(data[0]) + vString := strings.TrimSpace(data[1]) + if len(k) == 0 { + return fmt.Errorf("invalid key=value pair: %s", entry) + } + var err error + v := 0 + if len(vString) != 0 { + v, err = strconv.Atoi(vString) + if err != nil { + return err + } + } + (*m)[k] = int64(v) + } + return nil +} + +// NewIntMap will wrap the pointer to the map in a IntMap and set the +// underlying map to val. +func NewIntMap(p *map[string]int64, val map[string]int64) *IntMap { + *p = val + return (*IntMap)(p) +} diff --git a/deps/github.com/openconfig/gnmi/client/flags/stringlist.go b/deps/github.com/openconfig/gnmi/client/flags/stringlist.go new file mode 100644 index 0000000000000000000000000000000000000000..6edcc91d5c5f84a9e98da1d053acaeb1d8a2aada --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/flags/stringlist.go @@ -0,0 +1,50 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package flags defines extra flag types for use in command line flag parsing. +package flags + +import ( + "strings" +) + +// StringList is a []string container for command line flags. The default +// initialization will create an empty slice. If you need default backing slice +// map use NewStringList. +type StringList []string + +func (ss *StringList) String() string { + return strings.Join(*ss, ",") +} + +// Get returns the values of ss. The interface will need to be type asserted to +// StringList for use. +func (ss *StringList) Get() interface{} { + return *ss +} + +// Set sets the value of ss to the comma separated values in s. +func (ss *StringList) Set(s string) error { + *ss = StringList(strings.Split(s, ",")) + return nil +} + +// NewStringList will wrap the pointer to the slice in a StringList and set the +// underlying slice to val. +func NewStringList(p *[]string, val []string) *StringList { + *p = val + return (*StringList)(p) +} diff --git a/deps/github.com/openconfig/gnmi/client/flags/stringmap.go b/deps/github.com/openconfig/gnmi/client/flags/stringmap.go new file mode 100644 index 0000000000000000000000000000000000000000..5c9c8c40e022537157f119013e85702e1cb5214b --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/flags/stringmap.go @@ -0,0 +1,71 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flags + +import ( + "fmt" + "sort" + "strings" +) + +// StringMap is a map[string]string container for command line flags. The +// default initialization will create an empty map. If you need default backing +// map use NewStringMap. +type StringMap map[string]string + +func (m *StringMap) String() string { + s := make([]string, len(*m)) + i := 0 + for k, v := range *m { + s[i] = fmt.Sprintf("%s=%s", k, v) + i++ + } + sort.Strings(s) + return strings.Join(s, ",") +} + +// Get returns the values of m. The interface will need to be type asserted to +// StringMap for use. +func (m *StringMap) Get() interface{} { + return *m +} + +// Set will take a string in the format <key1>=<value1>,<key2>=<value2> and +// parse the resulting value into a map[string]string. +func (m *StringMap) Set(v string) error { + *m = StringMap{} + for _, entry := range strings.Split(v, ",") { + data := strings.SplitN(entry, "=", 2) + if len(data) != 2 { + return fmt.Errorf("invalid key=value pair: %s", entry) + } + k := strings.TrimSpace(data[0]) + v := strings.TrimSpace(data[1]) + if len(k) == 0 { + return fmt.Errorf("invalid key=value pair: %s", entry) + } + (*m)[k] = v + } + return nil +} + +// NewStringMap will wrap the pointer to the map in a StringMap and set the +// underlying map to val. +func NewStringMap(p *map[string]string, val map[string]string) *StringMap { + *p = val + return (*StringMap)(p) +} diff --git a/deps/github.com/openconfig/gnmi/client/gnmi/client.go b/deps/github.com/openconfig/gnmi/client/gnmi/client.go new file mode 100644 index 0000000000000000000000000000000000000000..92c3106c4703f20868422265a2de32cc7696fecf --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/gnmi/client.go @@ -0,0 +1,325 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package client contains transport implementation for the parent client +// library using gnmi.proto. +// +// Note: this package should not be used directly. Use +// github.com/openconfig/gnmi/client instead. +package client + +import ( + "context" + "encoding/json" + "fmt" + "math" + "net" + "strings" + "time" + + log "github.com/golang/glog" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc" + "github.com/golang/protobuf/proto" + "github.com/openconfig/ygot/ygot" + "github.com/openconfig/gnmi/client" + "github.com/openconfig/gnmi/client/grpcutil" + "github.com/openconfig/gnmi/path" + "github.com/openconfig/gnmi/value" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +// Type defines the name resolution for this client type. +const Type = "gnmi" + +// Client handles execution of the query and caching of its results. +type Client struct { + conn *grpc.ClientConn + client gpb.GNMIClient + sub gpb.GNMI_SubscribeClient + query client.Query + recv client.ProtoHandler + handler client.NotificationHandler + connected bool +} + +// New returns a new initialized client. If error is nil, returned Client has +// established a connection to d. Close needs to be called for cleanup. +func New(ctx context.Context, d client.Destination) (client.Impl, error) { + if len(d.Addrs) != 1 { + return nil, fmt.Errorf("d.Addrs must only contain one entry: %v", d.Addrs) + } + opts := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), + } + + switch d.TLS { + case nil: + opts = append(opts, grpc.WithInsecure()) + default: + opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(d.TLS))) + } + + if d.Credentials != nil { + secure := true + if d.TLS == nil { + secure = false + } + pc := newPassCred(d.Credentials.Username, d.Credentials.Password, secure) + opts = append(opts, grpc.WithPerRPCCredentials(pc)) + } + + gCtx, cancel := context.WithTimeout(ctx, d.Timeout) + defer cancel() + + if d.TunnelConn != nil { + withContextDialer := grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return d.TunnelConn, nil + }) + opts = append(opts, withContextDialer) + } + + conn, err := grpc.DialContext(gCtx, d.Addrs[0], opts...) + if err != nil { + return nil, fmt.Errorf("Dialer(%s, %v): %v", d.Addrs[0], d.Timeout, err) + } + return NewFromConn(ctx, conn, d) +} + +// NewFromConn creates and returns the client based on the provided transport. +func NewFromConn(ctx context.Context, conn *grpc.ClientConn, d client.Destination) (*Client, error) { + ok, err := grpcutil.Lookup(ctx, conn, "gnmi.gNMI") + if err != nil { + log.V(1).Infof("gRPC reflection lookup on %q for service gnmi.gNMI failed: %v", d.Addrs, err) + // This check is disabled for now. Reflection will become part of gNMI + // specification in the near future, so we can't enforce it yet. + } + if !ok { + // This check is disabled for now. Reflection will become part of gNMI + // specification in the near future, so we can't enforce it yet. + } + + cl := gpb.NewGNMIClient(conn) + return &Client{ + conn: conn, + client: cl, + }, nil +} + +// Subscribe sends the gNMI Subscribe RPC to the server. +func (c *Client) Subscribe(ctx context.Context, q client.Query) error { + sub, err := c.client.Subscribe(ctx) + if err != nil { + return fmt.Errorf("gpb.GNMIClient.Subscribe(%v) failed to initialize Subscribe RPC: %v", q, err) + } + + sr := q.SubReq + if sr == nil { + sr, err = subscribe(q) + if err != nil { + return fmt.Errorf("generating SubscribeRequest proto: %v", err) + } + } + + if err := sub.Send(sr); err != nil { + return fmt.Errorf("client.Send(%+v): %v", sr, err) + } + + c.sub = sub + c.query = q + if q.ProtoHandler == nil { + c.recv = c.defaultRecv + c.handler = q.NotificationHandler + } else { + c.recv = q.ProtoHandler + } + return nil +} + +// Poll will send a single gNMI poll request to the server. +func (c *Client) Poll() error { + if err := c.sub.Send(&gpb.SubscribeRequest{Request: &gpb.SubscribeRequest_Poll{Poll: &gpb.Poll{}}}); err != nil { + return fmt.Errorf("client.Poll(): %v", err) + } + return nil +} + +// Peer returns the peer of the current stream. If the client is not created or +// if the peer is not valid nil is returned. +func (c *Client) Peer() string { + return c.query.Addrs[0] +} + +// Close forcefully closes the underlying connection, terminating the query +// right away. It's safe to call Close multiple times. +func (c *Client) Close() error { + return c.conn.Close() +} + +// Recv will recieve a single message from the server and process it based on +// the provided handlers (Proto or Notification). +func (c *Client) Recv() error { + n, err := c.sub.Recv() + if err != nil { + return err + } + return c.recv(n) +} + +// defaultRecv is the default implementation of recv provided by the client. +// This function will be replaced by the ProtoHandler member of the Query +// struct passed to New(), if it is set. +func (c *Client) defaultRecv(msg proto.Message) error { + if !c.connected { + c.handler(client.Connected{}) + c.connected = true + } + + resp, ok := msg.(*gpb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + log.V(1).Info(resp) + switch v := resp.Response.(type) { + default: + return fmt.Errorf("unknown response %T: %s", v, v) + case *gpb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", v) + case *gpb.SubscribeResponse_SyncResponse: + c.handler(client.Sync{}) + if c.query.Type == client.Poll || c.query.Type == client.Once { + return client.ErrStopReading + } + case *gpb.SubscribeResponse_Update: + n := v.Update + p := path.ToStrings(n.Prefix, true) + ts := time.Unix(0, n.Timestamp) + for _, u := range n.Update { + if u.Path == nil { + return fmt.Errorf("invalid nil path in update: %v", u) + } + u, err := noti(p, u.Path, ts, u) + if err != nil { + return err + } + c.handler(u) + } + for _, d := range n.Delete { + u, err := noti(p, d, ts, nil) + if err != nil { + return err + } + c.handler(u) + } + } + return nil +} + +// Capabilities calls the gNMI Capabilities RPC. +func (c *Client) Capabilities(ctx context.Context, r *gpb.CapabilityRequest) (*gpb.CapabilityResponse, error) { + return c.client.Capabilities(ctx, r) +} + +// Get calls the gNMI Get RPC. +func (c *Client) Get(ctx context.Context, r *gpb.GetRequest) (*gpb.GetResponse, error) { + return c.client.Get(ctx, r) +} + +// Set calls the gNMI Set RPC. +func (c *Client) Set(ctx context.Context, r *gpb.SetRequest) (*gpb.SetResponse, error) { + return c.client.Set(ctx, r) +} + +func getType(t client.Type) gpb.SubscriptionList_Mode { + switch t { + case client.Once: + return gpb.SubscriptionList_ONCE + case client.Stream: + return gpb.SubscriptionList_STREAM + case client.Poll: + return gpb.SubscriptionList_POLL + } + return gpb.SubscriptionList_ONCE +} + +func subscribe(q client.Query) (*gpb.SubscribeRequest, error) { + s := &gpb.SubscribeRequest_Subscribe{ + Subscribe: &gpb.SubscriptionList{ + Mode: getType(q.Type), + Prefix: &gpb.Path{Target: q.Target}, + }, + } + if q.UpdatesOnly { + s.Subscribe.UpdatesOnly = true + } + for _, qq := range q.Queries { + pp, err := ygot.StringToPath(pathToString(qq), ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + return nil, fmt.Errorf("invalid query path %q: %v", qq, err) + } + s.Subscribe.Subscription = append(s.Subscribe.Subscription, &gpb.Subscription{Path: pp}) + } + return &gpb.SubscribeRequest{Request: s}, nil +} + +func noti(prefix []string, pp *gpb.Path, ts time.Time, u *gpb.Update) (client.Notification, error) { + sp := path.ToStrings(pp, false) + // Make a full new copy of prefix + u.Path to avoid any reuse of underlying + // slice arrays. + p := make([]string, 0, len(prefix)+len(sp)) + p = append(p, prefix...) + p = append(p, sp...) + + if u == nil { + return client.Delete{Path: p, TS: ts}, nil + } + if u.Val != nil { + val, err := value.ToScalar(u.Val) + if err != nil { + return nil, fmt.Errorf("failed to decode %s: %v", p, err) + } + return client.Update{Path: p, TS: ts, Val: val, Dups: u.Duplicates}, nil + } + switch v := u.Value; v.Type { + case gpb.Encoding_BYTES: + return client.Update{Path: p, TS: ts, Val: v.Value, Dups: u.Duplicates}, nil + case gpb.Encoding_JSON, gpb.Encoding_JSON_IETF: + var val interface{} + if err := json.Unmarshal(v.Value, &val); err != nil { + return nil, fmt.Errorf("json.Unmarshal(%q, val): %v", v, err) + } + return client.Update{Path: p, TS: ts, Val: val, Dups: u.Duplicates}, nil + default: + return nil, fmt.Errorf("Unsupported value type: %v", v.Type) + } +} + +func init() { + client.Register(Type, New) +} + +func pathToString(q client.Path) string { + qq := make(client.Path, len(q)) + copy(qq, q) + // Escape all slashes within a path element. ygot.StringToPath will handle + // these escapes. + for i, e := range qq { + qq[i] = strings.Replace(e, "/", "\\/", -1) + } + return strings.Join(qq, "/") +} diff --git a/deps/github.com/openconfig/gnmi/client/gnmi/client_test.go b/deps/github.com/openconfig/gnmi/client/gnmi/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8afab8e0b1c15286967f37f7d3d6d7b62ee09874 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/gnmi/client_test.go @@ -0,0 +1,523 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + "crypto/tls" + "testing" + "time" + + "github.com/kylelemons/godebug/pretty" + "google.golang.org/grpc" + "github.com/openconfig/ygot/ygot" + "github.com/openconfig/gnmi/client" + "github.com/openconfig/gnmi/testing/fake/gnmi" + "github.com/openconfig/gnmi/testing/fake/testing/grpc/config" + "github.com/openconfig/gnmi/value" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +func TestClient(t *testing.T) { + tests := []struct { + desc string + q client.Query + updates []*fpb.Value + disableEOF bool + wantErr bool + wantNoti []client.Notification + + poll int + wantPollErr string + }{{ + desc: "empty query", + q: client.Query{}, + wantErr: true, + }, { + desc: "once query with one update", + q: client.Query{ + Target: "dev", + Type: client.Once, + Queries: []client.Path{{"a"}}, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + updates: []*fpb.Value{{ + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }}, + wantNoti: []client.Notification{ + client.Connected{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + client.Sync{}, + }, + }, { + desc: "poll query with x3 by Poll()", + poll: 3, + q: client.Query{ + Target: "dev", + Type: client.Poll, + Queries: []client.Path{{"a"}}, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + updates: []*fpb.Value{{ + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }}, + wantNoti: []client.Notification{ + client.Connected{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + client.Sync{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + client.Sync{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + client.Sync{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + client.Sync{}, + }, + }, { + desc: "once query with updates and deletes", + q: client.Query{ + Target: "dev", + Type: client.Once, + Queries: []client.Path{{"a"}}, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + updates: []*fpb.Value{{ + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_Delete{Delete: &fpb.DeleteValue{}}, + }, { + Timestamp: &fpb.Timestamp{Timestamp: 300}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }}, + wantNoti: []client.Notification{ + client.Connected{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + client.Update{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 100), Val: 5}, + client.Delete{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 200)}, + client.Sync{}, + }, + }, { + desc: "stream query with updates and deletes", + disableEOF: true, + q: client.Query{ + Target: "dev", + Type: client.Stream, + Queries: []client.Path{{"a"}}, + TLS: &tls.Config{InsecureSkipVerify: true}, + }, + updates: []*fpb.Value{{ + Timestamp: &fpb.Timestamp{Timestamp: 100}, + Repeat: 1, + Value: &fpb.Value_Sync{Sync: 1}, + }, { + Path: []string{"a"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "c"}, + Timestamp: &fpb.Timestamp{Timestamp: 200}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 5}}, + }, { + Path: []string{"a", "b"}, + Timestamp: &fpb.Timestamp{Timestamp: 300}, + Repeat: 1, + Value: &fpb.Value_Delete{Delete: &fpb.DeleteValue{}}, + }, { + Path: []string{"a", "c"}, + Timestamp: &fpb.Timestamp{Timestamp: 400}, + Repeat: 1, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 50}}, + }}, + wantNoti: []client.Notification{ + client.Connected{}, + client.Sync{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 200), Val: 5}, + client.Update{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 200), Val: 5}, + client.Update{Path: []string{"dev", "a", "c"}, TS: time.Unix(0, 200), Val: 5}, + client.Delete{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 300)}, + client.Update{Path: []string{"dev", "a", "c"}, TS: time.Unix(0, 400), Val: 50}, + }, + }} + opt, err := config.WithSelfTLSCert() + if err != nil { + t.Fatalf("failed to generate cert: %v", err) + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + s, err := gnmi.New( + &fpb.Config{ + Target: "dev1", + DisableSync: true, + Values: tt.updates, + DisableEof: tt.disableEOF, + }, + []grpc.ServerOption{opt}, + ) + if err != nil { + t.Fatal("failed to start test server") + } + defer s.Close() + + q := tt.q + q.Addrs = []string{s.Address()} + c := client.New() + defer c.Close() + var gotNoti []client.Notification + q.NotificationHandler = func(n client.Notification) error { + gotNoti = append(gotNoti, n) + return nil + } + err = c.Subscribe(context.Background(), q) + switch { + case tt.wantErr && err != nil: + return + case tt.wantErr && err == nil: + t.Fatalf("c.Subscribe(): got nil error, expected non-nil") + case !tt.wantErr && err != nil: + t.Fatalf("c.Subscribe(): got error %v, expected nil", err) + } + for i := 0; i < tt.poll; i++ { + err := c.Poll() + switch { + case err == nil && tt.wantPollErr != "": + t.Errorf("c.Poll(): got nil error, expected non-nil %v", tt.wantPollErr) + case err != nil && tt.wantPollErr == "": + t.Errorf("c.Poll(): got error %v, expected nil", err) + case err != nil && err.Error() != tt.wantPollErr: + t.Errorf("c.Poll(): got error %v, expected error %v", err, tt.wantPollErr) + } + } + if diff := pretty.Compare(tt.wantNoti, gotNoti); diff != "" { + t.Errorf("unexpected updates:\n%s", diff) + } + impl, err := c.Impl() + if err != nil { + t.Fatalf("c.Impl() failed: %v", err) + } + if got, want := impl.(*Client).Peer(), s.Address(); got != want { + t.Errorf("Peer() failed: got %v, want %v", got, want) + } + }) + } +} + +func TestGNMIMessageUpdates(t *testing.T) { + var gotNoti []client.Notification + q := client.Query{ + Target: "dev", + Type: client.Stream, + Queries: []client.Path{{"a"}}, + TLS: &tls.Config{InsecureSkipVerify: true}, + NotificationHandler: func(n client.Notification) error { + gotNoti = append(gotNoti, n) + return nil + }, + } + updates := []*gpb.SubscribeResponse{ + {Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 200, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: []string{"a"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}, + }, + }, + }, + }}, + {Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 300, + Prefix: &gpb.Path{Target: "dev", Element: []string{"a"}}, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: []string{"b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}, + }, + }, + }, + }}, + {Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 400, + Prefix: &gpb.Path{Target: "dev", Origin: "oc", Element: []string{"a"}}, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: []string{"b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}, + }, + }, + }, + }}, + } + wantNoti := []client.Notification{ + client.Connected{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 200), Val: 5}, + client.Update{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 300), Val: 5}, + client.Update{Path: []string{"dev", "oc", "a", "b"}, TS: time.Unix(0, 400), Val: 5}, + client.Sync{}, + } + opt, err := config.WithSelfTLSCert() + if err != nil { + t.Fatalf("failed to generate cert: %v", err) + } + s, err := gnmi.New( + &fpb.Config{ + Generator: &fpb.Config_Fixed{Fixed: &fpb.FixedGenerator{Responses: updates}}, + }, + []grpc.ServerOption{opt}, + ) + if err != nil { + t.Fatal("failed to start test server") + } + defer s.Close() + q.Addrs = []string{s.Address()} + c := client.New() + defer c.Close() + err = c.Subscribe(context.Background(), q) + if diff := pretty.Compare(wantNoti, gotNoti); diff != "" { + t.Errorf("unexpected updates:\n%s", diff) + } +} + +func TestGNMIWithSubscribeRequest(t *testing.T) { + q, err := client.NewQuery(&gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + Subscribe: &gpb.SubscriptionList{ + Mode: gpb.SubscriptionList_STREAM, + Prefix: &gpb.Path{Target: "dev"}, + Subscription: []*gpb.Subscription{ + {Path: &gpb.Path{Element: []string{"a"}}}, + }, + }, + }, + }) + if err != nil { + t.Fatalf("failed to create Query from gnmi SubscribeRequest: %v", err) + } + q.TLS = &tls.Config{InsecureSkipVerify: true} + var gotNoti []client.Notification + q.NotificationHandler = func(n client.Notification) error { + gotNoti = append(gotNoti, n) + return nil + } + updates := []*gpb.SubscribeResponse{ + {Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 200, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: []string{"a"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}, + }, + }, + }, + }}, + {Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 300, + Prefix: &gpb.Path{Target: "dev", Element: []string{"a"}}, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: []string{"b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}, + }, + }, + }, + }}, + {Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 400, + Prefix: &gpb.Path{Target: "dev", Origin: "oc", Element: []string{"a"}}, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: []string{"b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}, + }, + }, + }, + }}, + } + wantNoti := []client.Notification{ + client.Connected{}, + client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 200), Val: 5}, + client.Update{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 300), Val: 5}, + client.Update{Path: []string{"dev", "oc", "a", "b"}, TS: time.Unix(0, 400), Val: 5}, + client.Sync{}, + } + opt, err := config.WithSelfTLSCert() + if err != nil { + t.Fatalf("failed to generate cert: %v", err) + } + s, err := gnmi.New( + &fpb.Config{ + Generator: &fpb.Config_Fixed{Fixed: &fpb.FixedGenerator{Responses: updates}}, + }, + []grpc.ServerOption{opt}, + ) + if err != nil { + t.Fatal("failed to start test server") + } + defer s.Close() + q.Addrs = []string{s.Address()} + c := client.New() + defer c.Close() + err = c.Subscribe(context.Background(), q) + if diff := pretty.Compare(wantNoti, gotNoti); diff != "" { + t.Errorf("unexpected updates:\n%s\nwantnoti:%v\ngotnoti:%v\n", diff, wantNoti, gotNoti) + } +} + +func stringToPath(p string) *gpb.Path { + pp, err := ygot.StringToPath(p, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + panic(err) + } + return pp +} + +func TestNoti(t *testing.T) { + tests := []struct { + desc string + prefix []string + path *gpb.Path + ts time.Time + u *gpb.Update + wantNoti client.Notification + wantErr bool + }{ + { + desc: "nil update means delete", + path: stringToPath("dev/a/b"), + ts: time.Unix(0, 200), + wantNoti: client.Delete{Path: []string{"dev", "a", "b"}, TS: time.Unix(0, 200)}, + }, { + desc: "update with TypedValue", + path: stringToPath("dev/a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}}, + wantNoti: client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + }, { + desc: "update with non-scalar TypedValue", + path: stringToPath("dev/a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Val: &gpb.TypedValue{Value: &gpb.TypedValue_JsonVal{[]byte("5")}}}, + wantNoti: client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: value.DeprecatedScalar{ + Message: "Deprecated TypedValue_JsonVal", + Value: 5, + }}, + }, { + desc: "update with JSON value", + path: stringToPath("dev/a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Value: &gpb.Value{Type: gpb.Encoding_JSON, Value: []byte("5")}}, + wantNoti: client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + }, { + desc: "update with bytes value", + path: stringToPath("dev/a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Value: &gpb.Value{Type: gpb.Encoding_BYTES, Value: []byte("5")}}, + wantNoti: client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: []byte("5")}, + }, { + desc: "update with un-unmarshalable JSON value", + path: stringToPath("dev/a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Value: &gpb.Value{Type: gpb.Encoding_JSON, Value: []byte(`"5`)}}, + wantErr: true, + }, { + desc: "update with unsupported value", + path: stringToPath("dev/a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Value: &gpb.Value{Type: gpb.Encoding_PROTO}}, + wantErr: true, + }, { + desc: "with prefix", + prefix: []string{"dev"}, + path: stringToPath("a"), + ts: time.Unix(0, 100), + u: &gpb.Update{Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{5}}}, + wantNoti: client.Update{Path: []string{"dev", "a"}, TS: time.Unix(0, 100), Val: 5}, + }, + } + for _, tt := range tests { + got, err := noti(tt.prefix, tt.path, tt.ts, tt.u) + switch { + case err != nil && !tt.wantErr: + t.Errorf("%s: got error %v, want nil", tt.desc, err) + case err == nil && tt.wantErr: + t.Errorf("%s: got nil error, want non-nil", tt.desc) + } + if diff := pretty.Compare(tt.wantNoti, got); diff != "" { + t.Errorf("%s: notification diff:\n%s", tt.desc, diff) + } + } +} + +func TestPathToString(t *testing.T) { + tests := []struct { + desc string + in client.Path + want string + }{ + {"simple path", client.Path{"a", "b", "c"}, "a/b/c"}, + {"path with attributes", client.Path{"a", "b[k=v]", "c"}, "a/b[k=v]/c"}, + {"path with slashes", client.Path{"a", "b/0/1", "c"}, "a/b\\/0\\/1/c"}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got := pathToString(tt.in) + if got != tt.want { + t.Fatalf("got %q, want %q", got, tt.want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/client/gnmi/credentials.go b/deps/github.com/openconfig/gnmi/client/gnmi/credentials.go new file mode 100644 index 0000000000000000000000000000000000000000..4a0cf24f105af4625db903666301c2de0876cee8 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/gnmi/credentials.go @@ -0,0 +1,55 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "golang.org/x/net/context" + + "google.golang.org/grpc/credentials" +) + +// passCred is an username/password implementation of credentials.Credentials. +type passCred struct { + username string + password string + secure bool +} + +// GetRequestMetadata returns the current request metadata, including +// username and password in this case. +// This implements the required interface fuction of credentials.Credentials. +func (pc *passCred) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { + return map[string]string{ + "username": pc.username, + "password": pc.password, + }, nil +} + +// RequireTransportSecurity indicates whether the credentials requires transport security. +// This implements the required interface fuction of credentials.Credentials. +func (pc *passCred) RequireTransportSecurity() bool { + return pc.secure +} + +// newPassCred returns a newly created passCred as credentials.Credentials. +func newPassCred(username, password string, secure bool) credentials.PerRPCCredentials { + return &passCred{ + username: username, + password: password, + secure: secure, + } +} diff --git a/deps/github.com/openconfig/gnmi/client/grpcutil/lookup.go b/deps/github.com/openconfig/gnmi/client/grpcutil/lookup.go new file mode 100644 index 0000000000000000000000000000000000000000..fa64e66f2b667a1d913772f6f37f24ac33f58df3 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/grpcutil/lookup.go @@ -0,0 +1,59 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package grpcutil provides helper functions for working with gRPC targets. +package grpcutil + +import ( + "context" + + "google.golang.org/grpc" + + rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" +) + +// Lookup uses ServerReflection service on conn to find a named service. +// It returns an error if the remote side doesn't support ServerReflection or +// if any other error occurs. +// +// If lookup succeeds and service is found, true is returned. +func Lookup(ctx context.Context, conn *grpc.ClientConn, service string) (bool, error) { + c, err := rpb.NewServerReflectionClient(conn).ServerReflectionInfo(ctx) + if err != nil { + return false, err + } + defer c.CloseSend() + + if err := c.Send(&rpb.ServerReflectionRequest{ + MessageRequest: &rpb.ServerReflectionRequest_ListServices{}, + }); err != nil { + return false, err + } + + resp, err := c.Recv() + if err != nil { + return false, err + } + + lsResp := resp.GetListServicesResponse() + for _, s := range lsResp.GetService() { + if s.Name == service { + return true, nil + } + } + + return false, nil +} diff --git a/deps/github.com/openconfig/gnmi/client/grpcutil/lookup_test.go b/deps/github.com/openconfig/gnmi/client/grpcutil/lookup_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fff1a17f7d1e58bbd5b934f9f98c6d3547960100 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/grpcutil/lookup_test.go @@ -0,0 +1,70 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package grpcutil + +import ( + "golang.org/x/net/context" + "net" + "testing" + + log "github.com/golang/glog" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +func TestLookup(t *testing.T) { + l, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatal(err) + } + srv := grpc.NewServer() + defer srv.Stop() + + gpb.RegisterGNMIServer(srv, &gpb.UnimplementedGNMIServer{}) + reflection.Register(srv) + + go srv.Serve(l) + + c, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure()) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + + t.Run("valid service", func(t *testing.T) { + ok, err := Lookup(ctx, c, "gnmi.gNMI") + if err != nil { + log.Error(err) + } + if !ok { + log.Error("got false, want true") + } + }) + t.Run("unknown service", func(t *testing.T) { + ok, err := Lookup(ctx, c, "unknown.Unknown") + if err != nil { + log.Error(err) + } + if ok { + log.Error("got true, want false") + } + }) + +} diff --git a/deps/github.com/openconfig/gnmi/client/notification.go b/deps/github.com/openconfig/gnmi/client/notification.go new file mode 100644 index 0000000000000000000000000000000000000000..16f4a6cf9807a8610e2a4683295912702ba25403 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/notification.go @@ -0,0 +1,64 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +// Notification are internal messages used for abstracting protocol specific +// messages for clients. isNotification is only present to force typing +// assertions. +type Notification interface { + isNotification() +} + +// Update is an update to the leaf in the tree. +type Update Leaf + +func (u Update) isNotification() {} + +// Delete is an explicit delete of the path in the tree. +type Delete Leaf + +func (d Delete) isNotification() {} + +// Error is a inband error notification. It could be received without breaking +// the query or connection. +type Error struct { + s string +} + +// NewError will return a new error with the provided text. +func NewError(s string) Error { + return Error{s: s} +} + +// Error is provided to implement the error interface. +func (e Error) Error() string { + return e.s +} +func (e Error) isNotification() {} + +// Sync is an inband notification that the client has sent everything in it's +// cache at least once. This does not mean EVERYTHING you wanted is there only +// that the target has sent everything it currently has... which may be nothing. +type Sync struct{} + +func (s Sync) isNotification() {} + +// Connected is a synthetic notification sent when connection is established. +// It's sent before any other notifications on a new client. +type Connected struct{} + +func (s Connected) isNotification() {} diff --git a/deps/github.com/openconfig/gnmi/client/query.go b/deps/github.com/openconfig/gnmi/client/query.go new file mode 100644 index 0000000000000000000000000000000000000000..a75a1bb4189f23a4715bba49617206aef5dd46e5 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/query.go @@ -0,0 +1,281 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "crypto/tls" + "errors" + "fmt" + "regexp" + "time" + + "github.com/openconfig/grpctunnel/tunnel" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/path" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +// NotificationHandler is a type for the client specific handler function. +// +// Client implementations will pass all kinds of received notifications as they +// arrive. +type NotificationHandler func(Notification) error + +// ProtoHandler is a type for the raw handling of the RPC layer. Most users +// should use NotificationHandler instead. +type ProtoHandler func(proto.Message) error + +// Type defines the type of query in a Query. +type Type int + +// NewType returns a new QueryType based on the provided string. +func NewType(s string) Type { + v, ok := typeConst[s] + if !ok { + return Unknown + } + return v +} + +// String returns the string representation of the QueryType. +func (q Type) String() string { + return typeString[q] +} + +const ( + // Unknown is an unknown query and should always be treated as an error. + Unknown Type = iota + // Once will perform a Once query against the agent. + Once + // Poll will perform a Polling query against the agent. + Poll + // Stream will perform a Streaming query against the agent. + Stream +) + +var ( + typeString = map[Type]string{ + Unknown: "unknown", + Once: "once", + Poll: "poll", + Stream: "stream", + } + + typeConst = map[string]Type{ + "unknown": Unknown, + "once": Once, + "poll": Poll, + "stream": Stream, + } + + // Pre-compiled regex to match ASCII characters between [\x20-\x7E] + // i.e., printable ASCII characters and space + // https://github.com/grpc/blob/master/doc/PROTOCOL-HTTP2.md + printableASCII = regexp.MustCompile(`^[\x20-\x7E]*$`).MatchString +) + +// Destination contains data used to connect to a server. +type Destination struct { + // Addrs is a slice of addresses by which a target may be reached. Most + // clients will only handle the first element. + Addrs []string + // Target is the target of the query. Maybe empty if the query is performed + // against an end target vs. a collector. + Target string + // Replica is the specific backend to contact. This field is implementation + // specific and for direct agent communication should not be set. default is + // first available. + Replica int + // Timeout is the connection timeout for the query. It will *not* prevent a + // slow (or streaming) query from completing, this only affects the initial + // connection and broken connection detection. + // + // If Timeout is not set, default is 1 minute. + Timeout time.Duration + // Credentials are used for authentication with the target. Optional. + Credentials *Credentials + // TLS config to use when connecting to target. Optional. + TLS *tls.Config + // Extra contains arbitrary additional metadata to be passed to the + // target. Optional. + Extra map[string]string + // TunnelConn follows the net.Conn interface. + TunnelConn *tunnel.Conn +} + +// Validate validates the fields of Destination. +func (d Destination) Validate() error { + if len(d.Addrs) == 0 { + return errors.New("Destination.Addrs is empty") + } + if d.Credentials != nil { + return d.Credentials.validate() + } + return nil +} + +// Query contains all of the parameters necessary to initiate the query. +type Query struct { + // Addrs is a slice of addresses by which a target may be reached. Most + // clients will only handle the first element. + Addrs []string + AddressChains [][]string + // Target is the target of the query. Maybe empty if the query is performed + // against an end target vs. a collector. + Target string + // Replica is the specific backend to contact. This field is implementation + // specific and for direct agent communication should not be set. default is + // first available. + Replica int + // UpdatesOnly will only stream incremental updates rather than providing the + // client with an initial snapshot. This again is implementation specific + // if the agent doesn't not accept that query it is up the client library to + // decide wheter to return an error or to make a normal subscription then + // ignore the initial sync and only provide increment updates. + UpdatesOnly bool + // Queries contains the list of Paths to query. + Queries []Path + // Type of query to perform. + Type Type + // Timeout is the connection timeout for the query. It will *not* prevent a + // slow (or streaming) query from completing, this only affects the initial + // connection and broken connection detection. + // + // If Timeout is not set, default is 1 minute. + Timeout time.Duration + // NotificationHandler is the per notification callback handed to a vendor + // specific implementation. For every notificaiton this call back will be + // called. + NotificationHandler NotificationHandler + // ProtoHandler, if set, will receive all response protos sent by the + // backend. Only one of NotificationHandler or ProtoHandler may be + // set. + ProtoHandler ProtoHandler + // Credentials are used for authentication with the target. Optional. + Credentials *Credentials + // TLS config to use when connecting to target. Optional. + TLS *tls.Config + // Extra contains arbitrary additional metadata to be passed to the + // target. Optional. + Extra map[string]string + // SubReq is an optional field. If not nil, gnmi client implementation uses + // it rather than generating from client.Query while sending gnmi Subscribe RPC. + SubReq *gpb.SubscribeRequest + // TunnelConn follows the net.Conn interface. + TunnelConn *tunnel.Conn +} + +// Destination extracts a Destination instance out of Query fields. +// +// Ideally we would embed Destination in Query. But in order to not break the +// existing API we have this workaround. +func (q Query) Destination() Destination { + return Destination{ + Addrs: q.Addrs, + Target: q.Target, + Replica: q.Replica, + Timeout: q.Timeout, + Credentials: q.Credentials, + TLS: q.TLS, + Extra: q.Extra, + TunnelConn: q.TunnelConn, + } +} + +// Credentials contains information necessary to authenticate with the target. +// Currently only username/password are supported, but may contain TLS client +// certificate in the future. +type Credentials struct { + Username string + Password string +} + +// Validates the credentials against printable ASCII characters +func (c Credentials) validate() error { + if !printableASCII(c.Username) { + return errors.New("Credentials.Username contains non printable ASCII characters") + } + if !printableASCII(c.Password) { + return errors.New("Credentials.Password contains non printable ASCII characters") + } + return nil +} + +// NewQuery returns a populated Query from given gnmi SubscribeRequest. +// Query fields that are not part of SubscribeRequest must be set on +// the returned object. +// During transtion to support only gnmi, having Query and SubscribeRequest +// in sync is important. There are two approaches to ensure that; one is +// validating whether Query and SubscribeRequest are same after they are set, the other is +// populating the fields of Query from SubscribeRequest and filling out the rest +// on the returned object. NewQuery embraces the latter option. +func NewQuery(sr *gpb.SubscribeRequest) (Query, error) { + q := Query{} + if sr == nil { + return q, errors.New("input is nil") + } + + s, ok := sr.Request.(*gpb.SubscribeRequest_Subscribe) + if !ok { + return q, fmt.Errorf("got %T, want gpb.SubscribeRequest_Subscribe as input", sr) + } + + if s.Subscribe == nil { + return q, errors.New("Subscribe field in SubscribeRequest_Subscribe is nil") + } + + if s.Subscribe.Prefix == nil { + return q, errors.New("Prefix field in SubscriptionList is nil") + } + q.Target = s.Subscribe.Prefix.Target + q.UpdatesOnly = s.Subscribe.UpdatesOnly + switch s.Subscribe.Mode { + case gpb.SubscriptionList_ONCE: + q.Type = Once + case gpb.SubscriptionList_POLL: + q.Type = Poll + case gpb.SubscriptionList_STREAM: + q.Type = Stream + } + + for _, su := range s.Subscribe.Subscription { + q.Queries = append(q.Queries, path.ToStrings(su.Path, false)) + } + q.SubReq = sr + + return q, nil +} + +// Validate validates that query contains valid values that any client should +// be able use to form a valid backend request. +func (q Query) Validate() error { + if err := q.Destination().Validate(); err != nil { + return err + } + switch { + case q.Type == Unknown: + return errors.New("Query type cannot be Unknown") + case len(q.Queries) == 0: + return errors.New("Query.Queries not set") + case q.NotificationHandler != nil && q.ProtoHandler != nil: + return errors.New("only one of Notification or ProtoHandler must be set") + case q.NotificationHandler == nil && q.ProtoHandler == nil: + return errors.New("one of Notification or ProtoHandler must be set") + } + return nil +} diff --git a/deps/github.com/openconfig/gnmi/client/reconnect.go b/deps/github.com/openconfig/gnmi/client/reconnect.go new file mode 100644 index 0000000000000000000000000000000000000000..69aea20cb2c0d97582793983b5f7753c2b895d3c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/reconnect.go @@ -0,0 +1,180 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + "fmt" + "sync" + "time" + + log "github.com/golang/glog" + "github.com/cenkalti/backoff/v4" +) + +var ( + // RetryBaseDelay is the initial retry interval for re-Subscribe attempts. + // You can change this before creating ReconnectClient instances. + RetryBaseDelay = time.Second + // RetryMaxDelay caps the retry interval for re-Subscribe attempts. + // You can change this before creating ReconnectClient instances. + RetryMaxDelay = time.Minute + // RetryRandomization is the randomization factor applied to the retry + // interval. You can change this before creating ReconnectClient instances. + RetryRandomization = 0.5 +) + +// ReconnectClient is a wrapper around any Client that never returns from +// Subscribe (unless explicitly closed). Underlying calls to Subscribe are +// repeated indefinitely, with an exponential backoff between attempts. +// +// ReconnectClient should only be used with streaming or polling queries. Once +// queries will fail immediately in Subscribe. +type ReconnectClient struct { + Client + backoff *backoff.ExponentialBackOff + disconnect func() + reset func() + + mu sync.Mutex + subscribeDone chan struct{} + cancel func() + closed bool +} + +var _ Client = &ReconnectClient{} + +// Reconnect wraps c and returns a new ReconnectClient using it. +// +// disconnect callback is called each time the underlying Subscribe returns, it +// may be nil. +// +// reset callback is called each time the underlying Subscribe is retried, it +// may be nil. +// +// Closing the returned ReconnectClient will unblock Subscribe. +func Reconnect(c Client, disconnect, reset func()) *ReconnectClient { + e := backoff.NewExponentialBackOff() + e.MaxElapsedTime = 0 // Retry Subscribe indefinitely. + e.InitialInterval = RetryBaseDelay + e.MaxInterval = RetryMaxDelay + e.RandomizationFactor = RetryRandomization + return &ReconnectClient{Client: c, backoff: e, disconnect: disconnect, reset: reset} +} + +// Subscribe implements Client interface. +func (p *ReconnectClient) Subscribe(ctx context.Context, q Query, clientType ...string) error { + switch q.Type { + default: + return fmt.Errorf("ReconnectClient used for %s query", q.Type) + case Stream, Poll: + } + + ctx, done := p.initDone(ctx) + defer done() + + for { + start := time.Now() + err := p.Client.Subscribe(ctx, q, clientType...) + if p.disconnect != nil { + p.disconnect() + } + + // Check if Subscribe returned because ctx was canceled. + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + if err == nil { + p.backoff.Reset() + } + // Since Client won't tell us whether error was immediate or after + // streaming for a while, try to "guess" if it's the latter. + if time.Since(start) > RetryMaxDelay { + p.backoff.Reset() + } + + bo := p.backoff.NextBackOff() + log.Errorf("client.Subscribe (target %q) failed: %v; reconnecting in %s", q.Target, err, bo) + time.Sleep(bo) + + // Signal caller right before we attempt to reconnect. + if p.reset != nil { + p.reset() + } + } +} + +// initDone finishes Subscribe initialization before starting the inner +// Subscribe loop. +// If p is closed before initDone, a cancelled context is returned. +func (p *ReconnectClient) initDone(ctx context.Context) (context.Context, func()) { + p.mu.Lock() + defer p.mu.Unlock() + p.subscribeDone = make(chan struct{}) + + // ctx is cancelled in p.Close(). + ctx, p.cancel = context.WithCancel(ctx) + + // If Close was called before initDone returned, it didn't have a cancel + // func to trigger. Trigger it here instead. + // Since initDone and Cancel are synchronizing on p.mu, either this or + // Close will call p.cancel(), preventing a hanging client. + if p.closed { + p.cancel() + } + + return ctx, func() { + close(p.subscribeDone) + } +} + +// Close implements Client interface. +func (p *ReconnectClient) Close() error { + subscribeDone := func() chan struct{} { + p.mu.Lock() + defer p.mu.Unlock() + + if p.cancel != nil { + p.cancel() + } + p.closed = true + + return p.subscribeDone + }() + + err := p.Client.Close() + + // Wait for Subscribe to return. + if subscribeDone != nil { + <-subscribeDone + } + return err +} + +// Impl implements Client interface. +func (p *ReconnectClient) Impl() (Impl, error) { + return p.Client.Impl() +} + +// Poll implements Client interface. +// Poll may fail if Subscribe is reconnecting when it's called. +func (p *ReconnectClient) Poll() error { + return p.Client.Poll() +} diff --git a/deps/github.com/openconfig/gnmi/client/reconnect_test.go b/deps/github.com/openconfig/gnmi/client/reconnect_test.go new file mode 100644 index 0000000000000000000000000000000000000000..148c8f9828eff4bb4e5f23c284f3db5b827349f9 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/reconnect_test.go @@ -0,0 +1,102 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client_test + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/openconfig/gnmi/client" + fclient "github.com/openconfig/gnmi/client/fake" +) + +func TestReconnect(t *testing.T) { + c := breakingSubscribeClient{ + BaseClient: &client.BaseClient{}, + ch: make(chan error), + } + + disconnect := make(chan struct{}, 1) + reset := make(chan struct{}) + client.RetryBaseDelay = time.Nanosecond + client.RetryMaxDelay = time.Millisecond + rc := client.Reconnect(c, func() { disconnect <- struct{}{} }, func() { reset <- struct{}{} }) + + subscribeDone := make(chan struct{}) + go func() { + defer close(subscribeDone) + rc.Subscribe(context.Background(), client.Query{Type: client.Stream}) + }() + for i := 0; i < 100; i++ { + c.ch <- errors.New("break") + <-disconnect + <-reset + } + + // Try to trigger a race between rc.Close and rc.Subscribe. + // rc.Close should return only after rc.Subscribe is done. + closeDone := make(chan struct{}) + go func() { + defer close(closeDone) + rc.Close() + }() + select { + case <-subscribeDone: + case <-closeDone: + t.Error("rc.Close returned before rc.Subscribe") + } +} + +type breakingSubscribeClient struct { + *client.BaseClient + ch chan error +} + +func (c breakingSubscribeClient) Subscribe(ctx context.Context, q client.Query, clientType ...string) error { + return <-c.ch +} + +func (c breakingSubscribeClient) Close() error { + c.ch <- errors.New("closed") + return nil +} + +func TestReconnectEarlyClose(t *testing.T) { + block := make(fclient.Block) + defer close(block) + fclient.Mock("fake", []interface{}{block}) + + rc := client.Reconnect(&client.BaseClient{}, nil, nil) + rc.Close() + + done := make(chan struct{}) + go func() { + rc.Subscribe(context.Background(), client.Query{ + Type: client.Stream, + NotificationHandler: func(client.Notification) error { return nil }, + }, "fake") + close(done) + }() + + select { + case <-done: + case <-time.After(time.Second): + t.Error("Subscribe on a closed ReconnectClient didn't return after 1s") + } +} diff --git a/deps/github.com/openconfig/gnmi/client/register.go b/deps/github.com/openconfig/gnmi/client/register.go new file mode 100644 index 0000000000000000000000000000000000000000..500147bdd87b6c864d28443f9308ab1e5cd2c02d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/register.go @@ -0,0 +1,196 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + "errors" + "fmt" + "sort" + "sync" + "time" + + log "github.com/golang/glog" + "github.com/openconfig/gnmi/errlist" +) + +var ( + mu sync.Mutex + clientImpl = map[string]InitImpl{} +) + +// Default timeout for all queries. +const defaultTimeout = time.Minute + +// Impl is the protocol/RPC specific implementation of the streaming Client. +// Unless you're implementing a new RPC format, this shouldn't be used directly. +type Impl interface { + // Subscribe sends a Subscribe request to the server. + Subscribe(context.Context, Query) error + // Recv processes a single message from the server. This method is exposed to + // allow the generic client control the state of message processing. + Recv() error + // Close will close the underlying rpc connections. + Close() error + // Poll will send an implementation specific Poll request to the server. + Poll() error +} + +// InitImpl is a constructor signature for all transport specific implementations. +type InitImpl func(context.Context, Destination) (Impl, error) + +// Register will register the transport specific implementation. +// The name must be unique across all transports. +func Register(t string, f InitImpl) error { + mu.Lock() + defer mu.Unlock() + if _, ok := clientImpl[t]; ok { + return fmt.Errorf("Duplicate registration of type %q", t) + } + if f == nil { + return errors.New("RegisterFunc cannot be nil") + } + clientImpl[t] = f + log.V(1).Infof("client.Register(%q, func) successful.", t) + return nil +} + +// RegisterTest allows tests to override client implementation for any client +// type. It's identical to Register, except t uniqueness is not enforced. +// +// RegisterTest is similar to ResetRegisteredImpls + Register. +// Commonly used with the fake client (./fake directory). +func RegisterTest(t string, f InitImpl) error { + mu.Lock() + defer mu.Unlock() + if f == nil { + return errors.New("RegisterFunc cannot be nil") + } + clientImpl[t] = f + log.V(1).Infof("client.Register(%q, func) successful.", t) + return nil +} + +// NewImpl returns a client implementation based on the registered types. +// It will try all clientTypes listed in parallel until one succeeds. If +// clientType is nil, it will try all registered clientTypes. +// +// This function is only used internally and is exposed for testing only. +func NewImpl(ctx context.Context, d Destination, clientType ...string) (Impl, error) { + mu.Lock() + registeredCount := len(clientImpl) + if clientType == nil { + for t := range clientImpl { + clientType = append(clientType, t) + } + } + mu.Unlock() + if registeredCount == 0 { + return nil, errors.New("no registered client types") + } + + // If Timeout is not set, use a default one. There is pretty much never a + // case where clients will want to wait for initial connection + // indefinitely. Reconnect client helps with retries. + if d.Timeout == 0 { + d.Timeout = defaultTimeout + } + + log.V(1).Infof("Attempting client types: %v", clientType) + fn := func(ctx context.Context, typ string, input interface{}) (Impl, error) { + mu.Lock() + f, ok := clientImpl[typ] + mu.Unlock() + if !ok { + return nil, fmt.Errorf("no registered client %q", typ) + } + d := input.(Destination) + impl, err := f(ctx, d) + if err != nil { + return nil, err + } + log.V(1).Infof("client %q create with type %T", typ, impl) + return impl, nil + } + return getFirst(ctx, clientType, d, fn) +} + +type implFunc func(ctx context.Context, typ string, input interface{}) (Impl, error) + +// getFirst tries fn with all types in parallel and returns the Impl from first +// one to succeed. input is passed directly to fn so it's safe to use an +// unchecked type asserting inside fn. +func getFirst(ctx context.Context, types []string, input interface{}, fn implFunc) (Impl, error) { + if len(types) == 0 { + return nil, errors.New("getFirst: no client types provided") + } + errC := make(chan error, len(types)) + implC := make(chan Impl) + done := make(chan struct{}) + defer close(done) + for _, t := range types { + // Launch each clientType in parallel where each sends either an error or + // an implementation over a channel. + go func(t string) { + impl, err := fn(ctx, t, input) + if err != nil { + errC <- fmt.Errorf("client %q : %v", t, err) + return + } + select { + case implC <- impl: + case <-done: + impl.Close() + } + }(t) + } + errs := errlist.Error{List: errlist.List{Separator: "\n\t"}} + // Look for the first non-error client implementation or return an error if + // all client types fail. + for { + select { + case err := <-errC: + errs.Add(err) + if len(errs.Errors()) == len(types) { + return nil, errs.Err() + } + case impl := <-implC: + return impl, nil + } + } +} + +// ResetRegisteredImpls removes and Impls registered with Register. This should +// only be used in tests to clear out their mock Impls, so that they don't +// affect other tests. +func ResetRegisteredImpls() { + mu.Lock() + defer mu.Unlock() + clientImpl = make(map[string]InitImpl) +} + +// RegisteredImpls returns a slice of currently registered client types. +func RegisteredImpls() []string { + mu.Lock() + defer mu.Unlock() + var impls []string + for k := range clientImpl { + impls = append(impls, k) + } + sort.Strings(impls) + return impls +} diff --git a/deps/github.com/openconfig/gnmi/client/values.go b/deps/github.com/openconfig/gnmi/client/values.go new file mode 100644 index 0000000000000000000000000000000000000000..81506153c97db74a95bc6b9192391cd46060aceb --- /dev/null +++ b/deps/github.com/openconfig/gnmi/client/values.go @@ -0,0 +1,76 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import "time" + +// Leaf represents a leaf value in the tree. It includes the path to the node in +// the tree. This is returned via Leaves. It is also the basis for all +// Notification types that are used by the NotificationHandler callbacks see +// "notification.go". +type Leaf struct { + Path Path + Val interface{} + TS time.Time // TS is the timestamp of last update to this leaf. + Dups uint32 // Dups is the number of coalesced duplicates for this leaf. +} + +// Leaves implements sort.Interface over []Leaf based on paths. +type Leaves []Leaf + +func (pv Leaves) Len() int { return len(pv) } +func (pv Leaves) Swap(i, j int) { pv[i], pv[j] = pv[j], pv[i] } +func (pv Leaves) Less(i, j int) bool { + return pv[i].Path.Less(pv[j].Path) +} + +// TreeVal contains the current branch's value and the timestamp when the +// node was last updated. +type TreeVal struct { + Val interface{} `json:"value"` + TS time.Time `json:"timestamp"` +} + +// Path is a standard type for describing a path inside of streaming telemetry +// tree. +type Path []string + +// Less returns true if p sorts before p2. +func (p Path) Less(p2 Path) bool { + for x := 0; x < len(p) && x < len(p2); x++ { + if p[x] < p2[x] { + return true + } + if p[x] > p2[x] { + return false + } + } + return len(p) < len(p2) +} + +// Equal returns true if p is equivalent to p2. +func (p Path) Equal(p2 Path) bool { + if len(p) != len(p2) { + return false + } + for x := 0; x < len(p); x++ { + if p[x] != p2[x] { + return false + } + } + return true +} diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli.go b/deps/github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli.go new file mode 100644 index 0000000000000000000000000000000000000000..c06cdf0853c30f6fa674adec9afd33ee53672824 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli.go @@ -0,0 +1,358 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// The gnmi_cli program implements the GNMI CLI. +// +// usage: +// gnmi_cli --address=<ADDRESS> \ +// -q=<OPENCONFIG_PATH[,OPENCONFIG_PATH[,...]]> \ +// [-qt=<QUERY_TYPE>] \ +// [-<ADDITIONAL_OPTION(s)>] +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "os" + "os/signal" + "strings" + "sync" + "time" + "unicode/utf8" + + "flag" + + log "github.com/golang/glog" + "golang.org/x/crypto/ssh/terminal" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/cli" + "github.com/openconfig/gnmi/client" + "github.com/openconfig/gnmi/client/flags" + gclient "github.com/openconfig/gnmi/client/gnmi" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +var ( + q = client.Query{TLS: &tls.Config{}} + mu sync.Mutex + cfg = cli.Config{Display: func(b []byte) { + defer mu.Unlock() + mu.Lock() + os.Stdout.Write(append(b, '\n')) + }} + + clientTypes = flags.NewStringList(&cfg.ClientTypes, []string{gclient.Type}) + queryFlag = &flags.StringList{} + queryType = flag.String("query_type", client.Once.String(), "Type of result, one of: (o, once, p, polling, s, streaming).") + queryAddr = flags.NewStringList(&q.Addrs, nil) + + reqProto = flag.String("proto", "", "Text proto for gNMI request.") + + capabilitiesFlag = flag.Bool("capabilities", false, `When set, CLI will perform a Capabilities request. Usage: gnmi_cli -capabilities [-proto <gnmi.CapabilityRequest>] -address <address> [other flags ...]`) + getFlag = flag.Bool("get", false, `When set, CLI will perform a Get request. Usage: gnmi_cli -get -proto <gnmi.GetRequest> -address <address> [other flags ...]`) + setFlag = flag.Bool("set", false, `When set, CLI will perform a Set request. Usage: gnmi_cli -set -proto <gnmi.SetRequest> -address <address> [other flags ...]`) + + withUserPass = flag.Bool("with_user_pass", false, "When set, CLI will prompt for username/password to use when connecting to a target.") + withPerRPCAuth = flag.Bool("with_per_rpc_auth", false, "Use per RPC auth.") + + // Certificate files. + insecureFlag = flag.Bool("insecure", false, "use insecure GRPC connection.") + caCert = flag.String("ca_crt", "", "CA certificate file. Used to verify server TLS certificate.") + clientCert = flag.String("client_crt", "", "Client certificate file. Used for client certificate-based authentication.") + clientKey = flag.String("client_key", "", "Client private key file. Used for client certificate-based authentication.") +) + +func init() { + flag.Var(clientTypes, "client_types", fmt.Sprintf("List of explicit client types to attempt, one of: %s.", strings.Join(client.RegisteredImpls(), ", "))) + flag.Var(queryFlag, "query", "Comma separated list of queries. Each query is a delimited list of OpenConfig path nodes which may also be specified as a glob (*). The delimeter can be specified with the --delimiter flag.") + // Query command-line flags. + flag.Var(queryAddr, "address", "Address of the GNMI target to query.") + flag.BoolVar(&q.UpdatesOnly, "updates_only", false, "Only stream updates, not the initial sync. Setting this flag for once or polling queries will cause nothing to be returned.") + // Config command-line flags. + flag.DurationVar(&cfg.PollingInterval, "polling_interval", 30*time.Second, "Interval at which to poll in seconds if polling is specified for query_type.") + flag.UintVar(&cfg.Count, "count", 0, "Number of polling/streaming events (0 is infinite).") + flag.StringVar(&cfg.Delimiter, "delimiter", "/", "Delimiter between path nodes in query. Must be a single UTF-8 code point.") + flag.DurationVar(&cfg.StreamingDuration, "streaming_duration", 0, "Length of time to collect streaming queries (0 is infinite).") + flag.StringVar(&cfg.DisplayPrefix, "display_prefix", "", "Per output line prefix.") + flag.StringVar(&cfg.DisplayIndent, "display_indent", " ", "Output line, per nesting-level indent.") + flag.StringVar(&cfg.DisplayType, "display_type", "group", "Display output type (g, group, s, single, p, proto).") + flag.StringVar(&q.Target, "target", "", "Name of the gNMI target.") + flag.DurationVar(&q.Timeout, "timeout", 30*time.Second, "Terminate query if no RPC is established within the timeout duration.") + flag.StringVar(&cfg.Timestamp, "timestamp", "", "Specify timestamp formatting in output. One of (<empty string>, on, raw, <FORMAT>) where <empty string> is disabled, on is human readable, raw is int64 nanos since epoch, and <FORMAT> is according to golang time.Format(<FORMAT>)") + flag.BoolVar(&cfg.DisplaySize, "display_size", false, "Display the total size of query response.") + flag.BoolVar(&cfg.Latency, "latency", false, "Display the latency for receiving each update (Now - update timestamp).") + flag.StringVar(&q.TLS.ServerName, "server_name", "", "When set, CLI will use this hostname to verify server certificate during TLS handshake.") + flag.BoolVar(&q.TLS.InsecureSkipVerify, "tls_skip_verify", false, "When set, CLI will not verify the server certificate during TLS handshake.") + + // Shortcut flags that can be used in place of the longform flags above. + flag.Var(queryAddr, "a", "Short for address.") + flag.Var(queryFlag, "q", "Short for query.") + flag.StringVar(&q.Target, "t", q.Target, "Short for target.") + flag.BoolVar(&q.UpdatesOnly, "u", q.UpdatesOnly, "Short for updates_only.") + flag.UintVar(&cfg.Count, "c", cfg.Count, "Short for count.") + flag.StringVar(&cfg.Delimiter, "d", cfg.Delimiter, "Short for delimiter.") + flag.StringVar(&cfg.Timestamp, "ts", cfg.Timestamp, "Short for timestamp.") + flag.StringVar(queryType, "qt", *queryType, "Short for query_type.") + flag.StringVar(&cfg.DisplayType, "dt", cfg.DisplayType, "Short for display_type.") + flag.DurationVar(&cfg.StreamingDuration, "sd", cfg.StreamingDuration, "Short for streaming_duration.") + flag.DurationVar(&cfg.PollingInterval, "pi", cfg.PollingInterval, "Short for polling_interval.") + flag.BoolVar(&cfg.DisplaySize, "ds", cfg.DisplaySize, "Short for display_size.") + flag.BoolVar(&cfg.Latency, "l", cfg.Latency, "Short for latency.") + flag.StringVar(reqProto, "p", *reqProto, "Short for request proto.") +} + +func main() { + flag.Parse() + + ctx, cancel := context.WithCancel(context.Background()) + // Terminate immediately on Ctrl+C, skipping lame-duck mode. + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + cancel() + }() + + if len(q.Addrs) == 0 { + log.Exit("--address must be set") + } + if *withUserPass { + var err error + q.Credentials, err = readCredentials() + if err != nil { + log.Exit(err) + } + } + + if *caCert != "" { + certPool := x509.NewCertPool() + ca, err := ioutil.ReadFile(*caCert) + if err != nil { + log.Exitf("could not read %q: %s", *caCert, err) + } + if ok := certPool.AppendCertsFromPEM(ca); !ok { + log.Exit("failed to append CA certificates") + } + + q.TLS.RootCAs = certPool + } + + if *clientCert != "" || *clientKey != "" { + if *clientCert == "" || *clientKey == "" { + log.Exit("--client_crt and --client_key must be set with file locations") + } + certificate, err := tls.LoadX509KeyPair(*clientCert, *clientKey) + if err != nil { + log.Exitf("could not load client key pair: %s", err) + } + + q.TLS.Certificates = []tls.Certificate{certificate} + } + + if *insecureFlag { + q.TLS = nil + } + + var err error + switch { + case *capabilitiesFlag: // gnmi.Capabilities + err = executeCapabilities(ctx) + case *setFlag: // gnmi.Set + err = executeSet(ctx) + case *getFlag: // gnmi.Get + err = executeGet(ctx) + default: // gnmi.Subscribe + err = executeSubscribe(ctx) + } + if err != nil { + log.Error(err) + } +} + +func executeCapabilities(ctx context.Context) error { + r := &gpb.CapabilityRequest{} + if err := proto.UnmarshalText(*reqProto, r); err != nil { + return fmt.Errorf("unable to parse gnmi.CapabilityRequest from %q : %v", *reqProto, err) + } + c, err := gclient.New(ctx, client.Destination{ + Addrs: q.Addrs, + Target: q.Target, + Timeout: q.Timeout, + Credentials: q.Credentials, + TLS: q.TLS, + }) + if err != nil { + return fmt.Errorf("could not create a gNMI client: %v", err) + } + response, err := c.(*gclient.Client).Capabilities(ctx, r) + if err != nil { + return fmt.Errorf("target returned RPC error for Capabilities(%q): %v", r.String(), err) + } + cfg.Display([]byte(proto.MarshalTextString(response))) + return nil +} + +func executeGet(ctx context.Context) error { + if *reqProto == "" { + return errors.New("-proto must be set") + } + r := &gpb.GetRequest{} + if err := proto.UnmarshalText(*reqProto, r); err != nil { + return fmt.Errorf("unable to parse gnmi.GetRequest from %q : %v", *reqProto, err) + } + c, err := gclient.New(ctx, client.Destination{ + Addrs: q.Addrs, + Target: q.Target, + Timeout: q.Timeout, + Credentials: q.Credentials, + TLS: q.TLS, + }) + if err != nil { + return fmt.Errorf("could not create a gNMI client: %v", err) + } + response, err := c.(*gclient.Client).Get(ctx, r) + if err != nil { + return fmt.Errorf("target returned RPC error for Get(%q): %v", r.String(), err) + } + cfg.Display([]byte(proto.MarshalTextString(response))) + return nil +} + +func executeSet(ctx context.Context) error { + if *reqProto == "" { + return errors.New("-proto must be set") + } + r := &gpb.SetRequest{} + if err := proto.UnmarshalText(*reqProto, r); err != nil { + return fmt.Errorf("unable to parse gnmi.SetRequest from %q : %v", *reqProto, err) + } + c, err := gclient.New(ctx, client.Destination{ + Addrs: q.Addrs, + Target: q.Target, + Timeout: q.Timeout, + Credentials: q.Credentials, + TLS: q.TLS, + }) + if err != nil { + return fmt.Errorf("could not create a gNMI client: %v", err) + } + response, err := c.(*gclient.Client).Set(ctx, r) + if err != nil { + return fmt.Errorf("target returned RPC error for Set(%q) : %v", r, err) + } + cfg.Display([]byte(proto.MarshalTextString(response))) + return nil +} + +func executeSubscribe(ctx context.Context) error { + if *reqProto != "" { + // Convert SubscribeRequest to a client.Query + tq, err := cli.ParseSubscribeProto(*reqProto) + if err != nil { + log.Exitf("failed to parse gNMI SubscribeRequest text proto: %v", err) + } + // Set the fields that are not set as part of conversion above. + tq.Addrs = q.Addrs + tq.Credentials = q.Credentials + tq.Timeout = q.Timeout + tq.TLS = q.TLS + return cli.QueryDisplay(ctx, tq, &cfg) + } + + if q.Type = cli.QueryType(*queryType); q.Type == client.Unknown { + return errors.New("--query_type must be one of: (o, once, p, polling, s, streaming)") + } + // Parse queryFlag into appropriate format. + if len(*queryFlag) == 0 { + return errors.New("--query must be set") + } + for _, path := range *queryFlag { + query, err := parseQuery(path, cfg.Delimiter) + if err != nil { + return fmt.Errorf("invalid query %q : %v", path, err) + } + q.Queries = append(q.Queries, query) + } + return cli.QueryDisplay(ctx, q, &cfg) +} + +func readCredentials() (*client.Credentials, error) { + c := &client.Credentials{} + user := os.Getenv("GNMI_USER") + pass := os.Getenv("GNMI_PASS") + if user != "" && pass != "" { + c.Username = user + c.Password = pass + return c, nil + } + fmt.Print("username: ") + _, err := fmt.Scan(&c.Username) + if err != nil { + return nil, err + } + + fmt.Print("password: ") + pb, err := terminal.ReadPassword(int(os.Stdin.Fd())) + fmt.Print("\n") // Echo 'Enter' key. + if err != nil { + return nil, err + } + c.Password = string(pb) + + return c, nil +} + +func parseQuery(query, delim string) ([]string, error) { + d, w := utf8.DecodeRuneInString(delim) + if w == 0 || w != len(delim) { + return nil, fmt.Errorf("delimiter must be single UTF-8 codepoint: %q", delim) + } + // Ignore leading and trailing delimters. + query = strings.Trim(query, delim) + // Split path on delimeter with contextually aware key/value handling. + var buf []rune + inKey := false + null := rune(0) + for _, r := range query { + switch r { + case '[': + if inKey { + return nil, fmt.Errorf("malformed query, nested '[': %q ", query) + } + inKey = true + case ']': + if !inKey { + return nil, fmt.Errorf("malformed query, unmatched ']': %q", query) + } + inKey = false + case d: + if !inKey { + buf = append(buf, null) + continue + } + } + buf = append(buf, r) + } + if inKey { + return nil, fmt.Errorf("malformed query, missing trailing ']': %q", query) + } + return strings.Split(string(buf), string(null)), nil +} diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli_test.go b/deps/github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli_test.go new file mode 100644 index 0000000000000000000000000000000000000000..32959174b8a750e319cc69df2183447cafbb1c21 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_cli/gnmi_cli_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "reflect" + "testing" +) + +func TestParseQuery(t *testing.T) { + tests := []struct { + name string + input string + delim string + output []string + err bool + }{ + {name: "Invalid delimiter", delim: "ab", err: true}, + {name: "Dot delimiter", input: "a.b", delim: ".", output: []string{"a", "b"}}, + {name: "Leading delimiter", input: "/foo", delim: "/", output: []string{"foo"}}, + {name: "Trailing delimiter", input: "foo/", delim: "/", output: []string{"foo"}}, + {name: "Leading and trailing delimiter", input: "/foo/", delim: "/", output: []string{"foo"}}, + {name: "No leading and trailing delimiter", input: "foo", delim: "/", output: []string{"foo"}}, + {name: "Leading delimiter multi", input: "/foo/bar", delim: "/", output: []string{"foo", "bar"}}, + {name: "Trailing delimiter multi", input: "foo/bar/", delim: "/", output: []string{"foo", "bar"}}, + {name: "Leading and trailing delimiter multi", input: "/foo/bar/", delim: "/", output: []string{"foo", "bar"}}, + {name: "No leading and trailing delimiter multi", input: "foo/bar", delim: "/", output: []string{"foo", "bar"}}, + {name: "Key value", input: "foo[key=value]/bar", delim: "/", output: []string{"foo[key=value]", "bar"}}, + {name: "Key value contains delimiter", input: "foo[key=a/b]/bar", delim: "/", output: []string{"foo[key=a/b]", "bar"}}, + {name: "Multiple key value contains delimiter", input: "foo[key=a/b]/bar[key=c/d]/blah", delim: "/", output: []string{"foo[key=a/b]", "bar[key=c/d]", "blah"}}, + {name: "Missing [ for key value", input: "fookey=a/b]/bar", err: true}, + {name: "Missing ] for key value", input: "foo[key=a/b/bar", err: true}, + {name: "Nested [] for key value", input: "foo[key=[nest]]/bar", err: true}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := parseQuery(test.input, test.delim) + switch { + case test.err && err != nil: + return + case test.err && err == nil: + t.Errorf("parseQuery(%q): want error, got nil", test.input) + case err != nil: + t.Errorf("parseQuery(%q): got error %v, want nil", test.input, err) + default: + if !reflect.DeepEqual(got, test.output) { + t.Errorf("parseQuery(%q): got %q, want %q", test.input, got, test.output) + } + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/Dockerfile b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9b5af4e4f2d2bccddbe0a7da33d9ca881b32e824 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/Dockerfile @@ -0,0 +1,22 @@ +# Copyright 2018 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build a simple Docker image with supervisord which runs the gnmi_collector +# binary. + +FROM alpine:3.7 +RUN apk update && apk add -u python py-pip +RUN pip install supervisor +ADD supervisord.conf /etc/supervisord.conf +ADD gnmi_collector / + +ENTRYPOINT ["supervisord", "--nodaemon", "--configuration", "/etc/supervisord.conf"] + diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/README.md b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a577cdb2946a017d1c0da2bc32652b062319ee89 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/README.md @@ -0,0 +1,33 @@ +# gNMI Collector Docker Image + +This directory contains the files required to build a Docker image for the gNMI +collector. + +This is not an official Google product. + +## Launching the Docker Image + +To run the gNMI collector simply execute `run.sh` on a system running the Docker +daemon. By default: + + * The collector listens on tcp/8888 for connections. It can be connected to + using the gnmi_cli command-line tool. + * The configuration within the `config` directory (`example.cfg`) is used for + the collector. This file is a textproto whose format is defined by the + protobuf in `proto/target/target.proto` within this repository. + * The SSL certificate (`cert.pem`, `key.pem` within the `config` directory) are + used for the collector instance. For use of this image anywhere else other + than for testing, this certificate should be regenerated. + +## Building Docker Image + +To rebuild the `gnmi_collector` binary, and rebuild the Docker image, execute +the `build.sh` script. + +## Image on Docker Hub + +This image can be pulled from Docker Hub using: + +``` +docker pull openconfig/gnmi_collector +``` diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/build.sh b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..9debb425326cb4a1b42f6e9a261cb13be3f0bd1e --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Copyright 2018 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file builds a simple docker image for the gnmi_collector +# based on Alpine Linux. + +(cd $GOPATH/src/github.com/openconfig/gnmi/cmd/gnmi_collector && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .) +cp $GOPATH/src/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector . +docker build -t gnmi_collector -f Dockerfile . diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/cert.pem b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/cert.pem new file mode 100644 index 0000000000000000000000000000000000000000..7f357c630fe5652cbf0c7b47128a71b12afd74d6 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIJAIahRoMjmR+WMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTgwNjE5MDYxODEyWhcNMTkwNjE5MDYxODEyWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA16i9MxwEK7St9wHig+6FPaaDNP4hWZ47BTZWZqWE8DEC1ky+TOSmCQbD +YzNcVuK0wLrwtxOWg7YcSU/ebiCQlAz0jgkvfAUpr0b0F59Q6UyJoPHTYQsHHBVr +c+vFB72gZ7ut9DcbdqtOyE7w8ftIm7H6MFZdRHcJ9gDY/zNN+IRLMsXyMqpDgLW1 +vEYm0vsJYaVhdhBVXjsz/mM/04gYlE9I4boUgvZmQ1OGGSligFjxZiFLM9zxMvGN ++Hm/MEdB4/2dk1Z+fsJ3wKlm7TZ2NsfHx7QXKgYmnbiUlF6hXjcXagxplB0vbufy +A2cHMYAgEkKja+Ymyqj7c0xVWzgbgK2XeUnhRfRkf5y6yLvpUSBs2GuAVaWVvlB6 +Ds8UGkRljFXY7F9+HmrXX2dAPY0wpT/x1EjbJLCm1hx0zfMD+TaDF8kqclcIFG1P +6rtijR3OaHycUj+HwqKe5rjvF0fjNngWYTIE3A9hEoQOCbVcFmTYL8Ri9GUq4o5J +Ri3kVGKeeqTdvbR+PtcVxeDa8i9V8Tlp38ldCDbfWiI0jxwEg8oKLT8Yvd7RGdTe +k7iLTGRHd47/VkZmZv0ltoAcI+mN4d4Dd0yDBuu9G6gp4JKUce/jTI+jVG2PsW2O +c1INFaXx3cbxuZqG6qVSmMYUprbA31HOqGJ6GTjGhgzjbErIezcCAwEAAaNTMFEw +HQYDVR0OBBYEFJ9cI4MTGf/W57cqv/l7RHz7jvfSMB8GA1UdIwQYMBaAFJ9cI4MT +Gf/W57cqv/l7RHz7jvfSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggIBAD4PpicahaNGos8lYMGHopGZMztWaUfJ4jmwi1ckJmfYpXIpzd8Jz9JjfgXU +BKSi8SoHKDpnHcNGJ1yO2FF4aggcOILY6bx/WBNnSHF3sT0fbFH6753vVEqbCv8R +MvQ1kDR7rsge6Q34WLXRgypO80MqHtLKJKrXlFuxHazRwvqRms2p15SaL82U7kHS +382TCKCJwxp7l5lqVuYZ7z9dTDa3zvEUh4Q5CzaCpePs8PjKL10L8lshpF48Js+Q +s3sgsyekEKGQ1gQgrZKmy7AaFXhwqFdtJft0F1L56cOGV6dV6GoVbxknOSEexuOh +0WVz3JcnOIjpNWFNPTdOUMRMOKYeT1ZCIOu1A/qZIMpNFUaXE9kIfvivCkAh0N+R +Du4xvxJSEQsPm5GxVvxy5Xms31O3MOh5COWb78TyNhHkSaDwEVJS72/YEAzMGrzq +jb76RthMkdxs6kZvyfuh/Q7E7WA4DI5gUKFWmigyDAngyJ1tSQ2vet/6PBPa/bMC +woZS5WHdxSCoTaeG9iSkxDZwT/VplBC6l8FZrM7W8bQqfP31eEGyd5lB3C6I6D/A +9Xvcx7rQjOKj6zKh+Ecwse3SRTwl1BErALMWx194he1r0nK4FskYenNJ63fyAaxv +/APDkxNzWZysIVU01xPHKGviinCCeAa4+apFMVyP6mD6oQ8i +-----END CERTIFICATE----- diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/example.cfg b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/example.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e65e0dc265be7f44e5295bba38a12abe4a3c885f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/example.cfg @@ -0,0 +1,28 @@ +request: < + key: "interfaces" + value: < + subscribe: < + prefix: < + origin: "openconfig" + > + subscription: < + path: < + elem: < + name: "interfaces" + > + > + > + > + > +> +target: < + key: "target1" + value: < + addresses: "192.0.2.1:10162" + request: "interfaces" + credentials: < + username: "username" + password: "password" + > + > +> diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/key.pem b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/key.pem new file mode 100644 index 0000000000000000000000000000000000000000..28d339a0ea14ee0571edecfd625a7471e7e3dc6c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/config/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDXqL0zHAQrtK33 +AeKD7oU9poM0/iFZnjsFNlZmpYTwMQLWTL5M5KYJBsNjM1xW4rTAuvC3E5aDthxJ +T95uIJCUDPSOCS98BSmvRvQXn1DpTImg8dNhCwccFWtz68UHvaBnu630Nxt2q07I +TvDx+0ibsfowVl1Edwn2ANj/M034hEsyxfIyqkOAtbW8RibS+wlhpWF2EFVeOzP+ +Yz/TiBiUT0jhuhSC9mZDU4YZKWKAWPFmIUsz3PEy8Y34eb8wR0Hj/Z2TVn5+wnfA +qWbtNnY2x8fHtBcqBiaduJSUXqFeNxdqDGmUHS9u5/IDZwcxgCASQqNr5ibKqPtz +TFVbOBuArZd5SeFF9GR/nLrIu+lRIGzYa4BVpZW+UHoOzxQaRGWMVdjsX34eatdf +Z0A9jTClP/HUSNsksKbWHHTN8wP5NoMXySpyVwgUbU/qu2KNHc5ofJxSP4fCop7m +uO8XR+M2eBZhMgTcD2EShA4JtVwWZNgvxGL0ZSrijklGLeRUYp56pN29tH4+1xXF +4NryL1XxOWnfyV0INt9aIjSPHASDygotPxi93tEZ1N6TuItMZEd3jv9WRmZm/SW2 +gBwj6Y3h3gN3TIMG670bqCngkpRx7+NMj6NUbY+xbY5zUg0VpfHdxvG5mobqpVKY +xhSmtsDfUc6oYnoZOMaGDONsSsh7NwIDAQABAoICAQDRNZ+9qisdnxy4p/pvlH9r +jFJyoSoHP2nwJ4Nv9phdTp56+F4QSCwOS6JWZOfqXemNooyMfhMg2RTdxf55BVxc +U6kW9TA8duG2aPW4yNP8T0SgfphZ5xHRm7Hu9cThOoMWvsoFRUR+PbeiuHojtWr9 +p42XQOEf3v8pyC66e7HjKautqLvJjMUJYr3X1JIrUF8MfBxtJXE7heFtoiiz864s +ijGg0Ry+43vXSnqnhC9LZX1hX2EOAgN3MUBR0QG5OxdfrpQj79+s5si3fV8dNh0+ +DVbwk/N40Bi8Huj8ekS/GmC7fykZB6sRQy/TPyqKScpTDJRuwwnUxlOQyXdwk6CQ +z2bgN4iMpSiMlS1nbtDJOqLLwjWYgDeahRFJLaCJYXK54IlhC76q+xvsoH7hH7w9 +fDsdSiOBP7jPDBMQrvolaff5wQ13yVKuWKJeScqTfcDeyTogDB2dIT/lVhqQ/L3w +m5Byd7GgDSvXWpKVqZtyFTa4wkhEUyBQwNgpLqw4yVT4oUO2/0vvSE6PHcX4PBgF +b5t+2Xhh3Un5aCLoKuDMsr3oweHlZ+C4RaH5ShiMYmlssPch9tpdAwDzjL4dZew2 +95GV64PVNOESrMkQbHbtCXLOn5mffECQNufUERoZwf4r0VFwcF1Hg0EDE8zr6kfJ +CxZgEMP66vGm5W0Fi2PvYQKCAQEA7oZf6szL5FH+93bvZqosNBzbqicIEESCiECb ++2EZiirV136/0JCG7+HiE/BdQXftJJyx4DsBp9bYfjFB4w1W+tr01GZY5XEMjM6e +gNPc9vQbl7BDF3dvweCCMHvJ+IgXJJlLols/kl0ENQiGFoNIEgU7cbt5S/SZRbZ1 +xmcOoXhLNJcOg+twU+PXNWRzoJcVNxtnGHP9Zw71Nenk+rarm9KMlmEinr+sisPX +v3q19krL6joPH6cmVB8KP3onvjqx0ka0l4p/a9b2bIw5m+2T7VBQAGKQAxxvV9dh +9AnCZTNLOm1DYEvVMNI5QkbfUL39+f6ci/I5TXDKH8klM6HbEQKCAQEA53WCHjpx +ai/l2PiIYIY50cwOylrd1mMQfkrkDkBZWWi0Gs8uWosCXh7ngnMVFhPaRUumTrUB +Vrw5mFgi294mfJeoH8kTsc30gfgp6MX8BA+0AumlaqpSEbDt68qXLlV/82Ok2geZ +/Rc8j4tTW1L9ojKK0lFhoOoWTUj0LF99Y1JuDSz9TU26UWsvXTF2SmvCkwsdz37b +7V2yhwmESvnDLfgfMTNfDaV06hnWLo4TOPxGhIe9TvB9lNlOitYnPNzHCmsdcC1X +7iuz9O3/l1UZzcMA7kvS8LizAVozwTpivB/JqcglagQITljg9Ysn2/XDprqE9DXz +UuawVEfqWxohxwKCAQEAzGAC65LS2S2Qw8T62ALToUtgxGhxNk8dcVW60Sef1I1j +cSO/gxtzIzNunRsFLMVvQkGo/7JPdtpV4bqe88dIUeUuhpezb9Tvpe6cTVI7MLZ0 +tgJnWkISVhLiOly27+bbvZZEAjchP23H3xEQo1WVeStWhdrW0cghVGREgd1n4d7K +waL04v/m3VqJ2cvaby1FFlS6f29THuzoGmvwsQm3/OnpYCiWm2MsmjG4OO8R3pi0 +JFve/YYhB08ZXs5yI0LzfnLq5jXbfDFSMCvuaLamW17HTa0mLCQ/GwsHeSyrjj7n +JIbehjIz14MpYKqNSgehCIWq6RaBFuOfrKCS6ayzoQKCAQEAptQOvkT3nEb7z2CL +w67bm/kNrBLElJMAv1axfAgrnoMWVcCBM6fARCDYUzEpwKj8iTfWniJe3ap7hO/5 +6Cn96Y3SE172QBEnrEo3wtx5ImBDKlpH7VLoJnI1et+iCjZ8K+zlVHVoK4EFGJ2e +rlNqFu+BGcioqrgu7POpcxY8pUszoXGM/ZwJsEemcvCEx21WaG/5w6Y3VKZVWHmm +jffqE4ckzGyvlYFtfJK0yweCmW9UUJAaBfzXN2NN9koXLAohuMOuCz3ThAqiAedN +c0FWgPWOBTRHzVQDSfU/GoY/xkLPMv3c4cYYSohLVRBA4Y0JwXHtBKyiPl2iOI1B +QPrb3wKCAQAhqoqz1O0ALJSXpcU7TxbmkCGKrntByS8fAczXcCzgz6ynbhP3EX0S +rGN75dNsnOiA0Zjuj43C32Ldr/pTkNW585RwWF/CZILCJfsiqM9qlnWOQhemC5DC +6+OZbcQfhmDNrI/6ee+Af2l0td7KmX1ARanfhXz0jNveV1gU8l9YmbuqFPb98BBf +PX0UymTwYe8aWUh0VTRaL+2sJcQmlxrtKrYVii9Zcfx8f/AEM2PDf06NGnd4ric8 +VL3H5Cxo0Nrsf/JyKsC+ocs0yrqJqlcs7087Ou/KwVdTpTw0kMDSToM8pYDhujpc +FPoKEpZNSeG9PhdCnycaJnEDPOkja+nP +-----END PRIVATE KEY----- diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/run.sh b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..991153d611f20ddd72a655b661882721663a0004 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/run.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file launches a Docker image of the gNMI collector. It uses +# the configuration stored in the "config" directory (example.cfg) +# and the certificate files stored in this directory. The collector +# listens on tcp/8888 which is forwarded to the host. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +docker run -d -v $DIR/config:/config -p 8888:8888 openconfig/gnmi_collector diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/supervisord.conf b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/supervisord.conf new file mode 100644 index 0000000000000000000000000000000000000000..96b91c796e96150e8239462e112dc9427f69468d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/docker/supervisord.conf @@ -0,0 +1,17 @@ +[supervisord] +logfile=/var/log/supervisord.log ; supervisord log file +logfile_maxbytes=50MB ; maximum size of logfile before rotation +logfile_backups=10 ; number of backed up logfiles +loglevel=error ; info, debug, warn, trace +pidfile=/var/run/supervisord.pid ; pidfile location +nodaemon=true ; do not daemonize +minfds=1024 ; number of startup file descriptors +minprocs=200 ; number of process descriptors +user=root ; default user +childlogdir=/var/log/ ; where child log files will live + +[program:collector] +command = /gnmi_collector -config_file /config/example.cfg -cert_file /config/cert.pem -key_file /config/key.pem -port 8888 -alsologtostderr +startsecs=10 +startretries=10 +autorestart=true diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector.go b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector.go new file mode 100644 index 0000000000000000000000000000000000000000..0d362d4ebaa0450554167fe7c307cc1d28be8d0c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector.go @@ -0,0 +1,410 @@ +/* +Copyright 2018 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// The gnmi_collector program implements a caching gNMI collector. +package main + +import ( + "context" + "crypto/tls" + "errors" + "flag" + "fmt" + "io/ioutil" + "net" + "sync" + "time" + + + log "github.com/golang/glog" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc" + "github.com/openconfig/grpctunnel/tunnel" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/cache" + "github.com/openconfig/gnmi/client" + gnmiclient "github.com/openconfig/gnmi/client/gnmi" + coll "github.com/openconfig/gnmi/collector" + "github.com/openconfig/gnmi/subscribe" + "github.com/openconfig/gnmi/target" + + tunnelpb "github.com/openconfig/grpctunnel/proto/tunnel" + cpb "github.com/openconfig/gnmi/proto/collector" + gnmipb "github.com/openconfig/gnmi/proto/gnmi" + tpb "github.com/openconfig/gnmi/proto/target" +) + +var ( + configFile = flag.String("config_file", "", "File path for collector configuration.") + certFile = flag.String("cert_file", "", "File path for TLS certificate.") + keyFile = flag.String("key_file", "", "File path for TLS key.") + port = flag.Int("port", 0, "server port") + dialTimeout = flag.Duration("dial_timeout", time.Minute, "Timeout for dialing a connection to a target.") + metadataUpdatePeriod = flag.Duration("metadata_update_period", 0, "Period for target metadata update. 0 disables updates.") + sizeUpdatePeriod = flag.Duration("size_update_period", 0, "Period for updating the target size in metadata. 0 disables updates.") + tunnelRequest = flag.String("tunnel_request", "", "request to be performed via tunnel") // non-tunnel request will be contained in the config file. +) + +func periodic(period time.Duration, fn func()) { + if period == 0 { + return + } + t := time.NewTicker(period) + defer t.Stop() + for range t.C { + fn() + } +} + +func (c *collector) addTargetHandler(t tunnel.Target) error { + c.mu.Lock() + defer c.mu.Unlock() + + if _, ok := c.config.Target[t.ID]; ok { + return fmt.Errorf("trying to add target %s, but already in config", t.ID) + } + c.chAddTarget <- t + return nil +} + +func (c *collector) deleteTargetHandler(t tunnel.Target) error { + c.mu.Lock() + defer c.mu.Unlock() + + if _, ok := c.config.Target[t.ID]; !ok { + return fmt.Errorf("trying to delete target %s, but not found in config", t.ID) + } + c.chDeleteTarget <- t + return nil +} + +// Under normal conditions, this function will not terminate. Cancelling +// the context will stop the collector. +func runCollector(ctx context.Context) error { + if *configFile == "" { + return errors.New("config_file must be specified") + } + if *certFile == "" { + return errors.New("cert_file must be specified") + } + if *keyFile == "" { + return errors.New("key_file must be specified") + } + + c := collector{config: &tpb.Configuration{}, + cancelFuncs: map[string]func(){}, + tConn: map[string]*tunnel.Conn{}, + tRequest: *tunnelRequest, + chDeleteTarget: make(chan tunnel.Target, 1), + chAddTarget: make(chan tunnel.Target, 1), + addr: fmt.Sprintf("localhost:%d", *port)} + + // Initialize configuration. + buf, err := ioutil.ReadFile(*configFile) + if err != nil { + return fmt.Errorf("Could not read configuration from %q: %v", *configFile, err) + } + if err := proto.UnmarshalText(string(buf), c.config); err != nil { + return fmt.Errorf("Could not parse configuration from %q: %v", *configFile, err) + } + if err := target.Validate(c.config); err != nil { + return fmt.Errorf("Configuration in %q is invalid: %v", *configFile, err) + } + + // Initialize TLS credentials. + creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) + if err != nil { + return fmt.Errorf("Failed to generate credentials %v", err) + } + + // Create a grpc Server. + srv := grpc.NewServer(grpc.Creds(creds)) + + // Initialize tunnel server. + c.tServer, err = tunnel.NewServer(tunnel.ServerConfig{AddTargetHandler: c.addTargetHandler, DeleteTargetHandler: c.deleteTargetHandler}) + if err != nil { + log.Fatalf("failed to setup tunnel server: %v", err) + } + tunnelpb.RegisterTunnelServer(srv, c.tServer) + + // Initialize cache. + c.cache = cache.New(nil) + + // Start functions to periodically update metadata stored in the cache for each target. + go periodic(*metadataUpdatePeriod, c.cache.UpdateMetadata) + go periodic(*sizeUpdatePeriod, c.cache.UpdateSize) + + // Initialize collectors. + c.start(context.Background()) + + // Initialize the Collector server. + cpb.RegisterCollectorServer(srv, coll.New(c.reconnect)) + // Initialize gNMI Proxy Subscribe server. + subscribeSrv, err := subscribe.NewServer(c.cache) + if err != nil { + return fmt.Errorf("Could not instantiate gNMI server: %v", err) + } + gnmipb.RegisterGNMIServer(srv, subscribeSrv) + // Forward streaming updates to clients. + c.cache.SetClient(subscribeSrv.Update) + // Register listening port and start serving. + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) + if err != nil { + return fmt.Errorf("failed to listen: %v", err) + } + go srv.Serve(lis) + defer srv.Stop() + <-ctx.Done() + return ctx.Err() +} + +// Container for some of the target state data. It is created once +// for every device and used as a closure parameter by ProtoHandler. +type state struct { + name string + target *cache.Target + // connected status is set to true when the first gnmi notification is received. + // it gets reset to false when disconnect call back of ReconnectClient is called. + connected bool +} + +func (s *state) disconnect() { + s.connected = false + s.target.Reset() +} + +// handleUpdate parses a protobuf message received from the target. This implementation handles only +// gNMI SubscribeResponse messages. When the message is an Update, the GnmiUpdate method of the +// cache.Target is called to generate an update. If the message is a sync_response, then target is +// marked as synchronised. +func (s *state) handleUpdate(msg proto.Message) error { + if !s.connected { + s.target.Connect() + s.connected = true + } + resp, ok := msg.(*gnmipb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch v := resp.Response.(type) { + case *gnmipb.SubscribeResponse_Update: + // Gracefully handle gNMI implementations that do not set Prefix.Target in their + // SubscribeResponse Updates. + if v.Update.GetPrefix() == nil { + v.Update.Prefix = &gnmipb.Path{} + } + if v.Update.Prefix.Target == "" { + v.Update.Prefix.Target = s.name + } + s.target.GnmiUpdate(v.Update) + case *gnmipb.SubscribeResponse_SyncResponse: + s.target.Sync() + case *gnmipb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", v) + default: + return fmt.Errorf("unknown response %T: %s", v, v) + } + return nil +} + +type collector struct { + cache *cache.Cache + config *tpb.Configuration + mu sync.Mutex + cancelFuncs map[string]func() + addr string + tConn map[string]*tunnel.Conn + tServer *tunnel.Server + tRequest string + chAddTarget chan tunnel.Target + chDeleteTarget chan tunnel.Target +} + +func (c *collector) addCancel(target string, cancel func()) { + c.mu.Lock() + c.cancelFuncs[target] = cancel + c.mu.Unlock() +} + +func (c *collector) reconnect(target string) error { + c.mu.Lock() + defer c.mu.Unlock() + cancel, ok := c.cancelFuncs[target] + if !ok { + return fmt.Errorf("no such target: %q", target) + } + cancel() + delete(c.cancelFuncs, target) + return nil +} + +func (c *collector) runSingleTarget(ctx context.Context, targetID string, tc *tunnel.Conn) { + target, ok := c.config.Target[targetID] + if !ok { + log.Errorf("Unknown target %q", targetID) + return + } + + go func(name string, target *tpb.Target) { + s := &state{name: name, target: c.cache.Add(name)} + qr := c.config.Request[target.Request] + q, err := client.NewQuery(qr) + if err != nil { + log.Errorf("NewQuery(%s): %v", qr.String(), err) + return + } + q.Addrs = target.Addresses + + if target.Credentials != nil { + q.Credentials = &client.Credentials{ + Username: target.Credentials.Username, + Password: target.Credentials.Password, + } + } + + // TLS is always enabled for a target. + q.TLS = &tls.Config{ + // Today, we assume that we should not verify the certificate from the target. + InsecureSkipVerify: true, + } + + q.Target = name + q.Timeout = *dialTimeout + q.ProtoHandler = s.handleUpdate + if err := q.Validate(); err != nil { + log.Errorf("query.Validate(): %v", err) + return + } + + q.TunnelConn = c.tConn[name] + select { + case <-ctx.Done(): + return + default: + } + cl := client.BaseClient{} + if err := cl.Subscribe(ctx, q, gnmiclient.Type); err != nil { + log.Errorf("Subscribe failed for target %q: %v", name, err) + // remove target once it becomes unavailable + c.removeTarget(name) + } + }(targetID, target) +} + +func (c *collector) start(ctx context.Context) { + // First, run for non-tunnel targets. + for id := range c.config.Target { + c.runSingleTarget(ctx, id, nil) + } + + if c.tRequest == "" { + return + } + // Monitor targets from the tunnel. + go func() { + for { + select { + case target := <-c.chAddTarget: + if target.Type != tunnelpb.TargetType_GNMI_GNOI.String() { + log.Infof("recived unsupported type type: %s from target:s, skipping", target.Type, target.ID) + continue + } + if _, ok := c.tConn[target.ID]; ok { + log.Infof("recived target %s, which is already registered. skipping", target.ID) + continue + } + + ctx, cancel := context.WithCancel(ctx) + c.addCancel(target.ID, cancel) + + tc, err := tunnel.ServerConn(ctx, c.tServer, c.addr, &target) + if err != nil { + log.Errorf("failed to get new tunnel session for target %v:%v", target.ID, err) + continue + } + c.tConn[target.ID] = tc + c.addTarget(ctx, target.ID, &tpb.Target{ + Addresses: []string{c.addr}, + Request: c.tRequest, + }) + + c.runSingleTarget(ctx, target.ID, c.tConn[target.ID]) + + case target := <-c.chDeleteTarget: + if target.Type != tunnelpb.TargetType_GNMI_GNOI.String() { + log.Infof("recived unsupported type type: %s from target:s, skipping", target.Type, target.ID) + continue + } + if _, ok := c.tConn[target.ID]; !ok { + log.Infof("recived target %s, which is not registered. skipping", target.ID) + continue + } + c.removeTarget(target.ID) + } + } + }() +} + +func (c *collector) removeTarget(target string) error { + c.mu.Lock() + defer c.mu.Unlock() + if _, ok := c.config.Target[target]; !ok { + log.Infof("trying to remove target %s, but not found in config. do nothing", target) + return nil + } + delete(c.config.Target, target) + + cancel, ok := c.cancelFuncs[target] + if !ok { + return fmt.Errorf("cannot find cancel for target %q", target) + } + cancel() + delete(c.cancelFuncs, target) + if conn, ok := c.tConn[target]; ok && conn != nil { + conn.Close() + } + delete(c.tConn, target) + log.Infof("target %s removed", target) + return nil +} + +func (c *collector) addTarget(ctx context.Context, name string, target *tpb.Target) error { + c.mu.Lock() + defer c.mu.Unlock() + + if _, ok := c.config.Target[name]; ok { + log.Infof("trying to add target %s, but already in config. do nothing", target) + return nil + } + + var targetMap map[string]*tpb.Target + if c.config.Target != nil { + targetMap = c.config.Target + } else { + targetMap = make(map[string]*tpb.Target) + } + + targetMap[name] = target + c.config.Target = targetMap + + return nil +} + +func main() { + // Flag initialization. + flag.Parse() + log.Exit(runCollector(context.Background())) +} diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector_test.go b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a6a0e5e3add170f6a4a0c1d3bf4ad82fc67ea1b7 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/gnmi_collector_test.go @@ -0,0 +1,115 @@ +/* +Copyright 2018 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "path/filepath" + "testing" + "time" +) + +func TestErrorsRunCollector(t *testing.T) { + tests := []struct { + desc, config, cert, key string + valid bool + }{{ + desc: "valid config, cert and key", + config: "good.cfg", + cert: "good.crt", + key: "good.key", + valid: true, + }, { + desc: "empty config name", + cert: "good.crt", + key: "good.key", + }, { + desc: "unparseable config", + config: "unparseable.cfg", + cert: "good.crt", + key: "good.key", + }, { + desc: "invalid config", + config: "bad.cfg", + cert: "good.crt", + key: "good.key", + }, { + desc: "missing config", + config: "missing.cfg", + cert: "good.crt", + key: "good.key", + }, { + desc: "empty cert name", + config: "good.cfg", + key: "good.key", + }, { + desc: "invalid cert", + config: "good.cfg", + cert: "bad.crt", + key: "good.key", + }, { + desc: "missing cert", + config: "good.cfg", + cert: "missing.crt", + key: "good.key", + }, { + desc: "empty key name", + config: "good.cfg", + cert: "good.crt", + }, { + desc: "invalid key", + config: "good.cfg", + cert: "good.crt", + key: "bad.key", + }, { + key: "missing.key", + config: "good.cfg", + cert: "good.crt", + desc: "missing key", + }} + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + *port = 0 + *tunnelRequest = "" + if test.config != "" { + *configFile = filepath.Join("testdata", test.config) + defer func() { *configFile = "" }() + } + if test.cert != "" { + *certFile = filepath.Join("testdata", test.cert) + defer func() { *certFile = "" }() + } + if test.key != "" { + *keyFile = filepath.Join("testdata", test.key) + defer func() { *keyFile = "" }() + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + err := runCollector(ctx) + t.Logf("runCollector(ctx) : %v", err) + if test.valid { + if err != context.DeadlineExceeded { + t.Errorf("got %v, want %v", err, context.DeadlineExceeded) + } + } else { + if err == nil { + t.Error("got nil error, wanted error") + } + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.cfg b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c12778997b36eb63b89645ee48e0d188c997bf4d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.cfg @@ -0,0 +1,28 @@ +request: < + key: "interfaces" + value: < + subscribe: < + prefix: < + origin: "openconfig" + > + subscription: < + path: < + elem: < + name: "interfaces" + > + > + > + > + > +> +target: < + key: "target1" + value: < + addresses: "127.0.0.1:9998" + request: "protocols" + credentials: < + username: "username" + password: "password" + > + > +> diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.crt b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.crt new file mode 100644 index 0000000000000000000000000000000000000000..dfdadf19d5b2c3ea2f5274f6bd3baf1af27e141f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIJAOZpdHfZp2BmMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vubnl2YWxl +MQ8wDQYDVQQKDAZHb29nbGUxEzARBgNVBAsMCk5ldHdvcmtpbmcxFzAVBgNVBAMM +DmNzbEBnb29nbGUuY29tMR0wGwYJKoZIhvcNAQkBFg5jc2xAZ29vZ2xlLmNvbTAe +Fw0xODA2MTUyMjUyMTJaFw0xOTA2MTUyMjUyMTJaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vubnl2YWxlMQ8wDQYDVQQK +DAZHb29nbGUxEzARBgNVBAsMCk5ldHdvcmtpbmcxFzAVBgNVBAMMDmNzbEBnb29n +bGUuY29tMR0wGwYJKoZIhvcNAQkBFg5jc2xAZ29vZ2xlLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMC7FFKUBder3aOCs5FJyTAcVjaJv7ATdhPX +Pvdzdx8D85mhwz93cGWNhhybp37Kjwb0FxkiEcmv+wKTZWCNqYL3KL3qjqsW/1u8 diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.key b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.key new file mode 100644 index 0000000000000000000000000000000000000000..83d45f1e9a58beb65fe97ae608beb9aa826b4fd4 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/bad.key @@ -0,0 +1,12 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAuxRSlAXXq92j +grORSckwHFY2ib+wE3YT1z73c3cfA/OZocM/d3BljYYcm6d+yo8G9BcZIhHJr/sC +k2VgjamC9yi96o6rFv9bvPstE1/aJiwAyKw3Ii+lC4l8l03BooN9esDoe33ckgMw +XF8xke4qv0vgSnhbFYyzb0cTiTYE5ua56Ivf/vvHzhrDcXrBMBlweDsPv9A+fZl3 +U13IAahHvN65EIhlWFZMdZ8XIrhy76bXqRQUn2+xF27RqPvBP72xFJ/VRBMmUy9d +bPDklJR5ntLIIoDRMPz8FDqNxLKC5dLGg3Q6tdeki46O9NMekqrnNzzuN5l9MEI5 +4kfHdf91AgMBAAECggEBALZHeZYMV2Y+LxY5RFnR/kzZUBSuvTPrhA6J0gy5EN2t +oBQacQ4yiYQSkNe3w3dgXluGOd5UYZxuKympfAUQllS+K7NLBvn7ezIpjyl3nH03 +AwEOygou8/7V5YeHlkvfxxrDIXX/be9RzWBMzN+WFDoryLht1lzXCcafJQruB8Aj +djw/4xa+N64+PoNFDqu/a2xBoYJq44xBYx55e9E0PSFN8Dy+eWkNKpUR647uLjMI +CWWi5i45wXtRvqhZ6aVyTbOmSh6NMW3tnEMoCQMYt0GAJn8nlo3jfqVr7lruwzE1 diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.cfg b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.cfg new file mode 100644 index 0000000000000000000000000000000000000000..77ce0ee1304c55996205d315051743eba5ddf04d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.cfg @@ -0,0 +1,28 @@ +request: < + key: "interfaces" + value: < + subscribe: < + prefix: < + origin: "openconfig" + > + subscription: < + path: < + elem: < + name: "interfaces" + > + > + > + > + > +> +target: < + key: "target1" + value: < + addresses: "127.0.0.1:9998" + request: "interfaces" + credentials: < + username: "username" + password: "password" + > + > +> diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.crt b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.crt new file mode 100644 index 0000000000000000000000000000000000000000..f6f15027cc9fad3374b6e271e4a9f2f068139d4b --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIJAOZpdHfZp2BmMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vubnl2YWxl +MQ8wDQYDVQQKDAZHb29nbGUxEzARBgNVBAsMCk5ldHdvcmtpbmcxFzAVBgNVBAMM +DmNzbEBnb29nbGUuY29tMR0wGwYJKoZIhvcNAQkBFg5jc2xAZ29vZ2xlLmNvbTAe +Fw0xODA2MTUyMjUyMTJaFw0xOTA2MTUyMjUyMTJaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vubnl2YWxlMQ8wDQYDVQQK +DAZHb29nbGUxEzARBgNVBAsMCk5ldHdvcmtpbmcxFzAVBgNVBAMMDmNzbEBnb29n +bGUuY29tMR0wGwYJKoZIhvcNAQkBFg5jc2xAZ29vZ2xlLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMC7FFKUBder3aOCs5FJyTAcVjaJv7ATdhPX +Pvdzdx8D85mhwz93cGWNhhybp37Kjwb0FxkiEcmv+wKTZWCNqYL3KL3qjqsW/1u8 ++y0TX9omLADIrDciL6ULiXyXTcGig316wOh7fdySAzBcXzGR7iq/S+BKeFsVjLNv +RxOJNgTm5rnoi9/++8fOGsNxesEwGXB4Ow+/0D59mXdTXcgBqEe83rkQiGVYVkx1 +nxciuHLvptepFBSfb7EXbtGo+8E/vbEUn9VEEyZTL11s8OSUlHme0sgigNEw/PwU +Oo3EsoLl0saDdDq116SLjo700x6Squc3PO43mX0wQjniR8d1/3UCAwEAAaNTMFEw +HQYDVR0OBBYEFDrZ1HErw89GWWW9RDCuhdtywcr1MB8GA1UdIwQYMBaAFDrZ1HEr +w89GWWW9RDCuhdtywcr1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggEBAFxmRzjeM469YXHL5byt1gYEukSICDBwmFQKd3eoDOspPFPVC8+FF01X1OBq +3HbTssL2Tu1UxQtCdhkkqjvE4HWeat2vI/0pyXoUOWrblXAS2maF1roZuriJzJ/5 +dIY8tUlts8ZBKtbX4JjzI2Il/fB98Kv9+MTs9zQfHsRh0D9GUY+ZPFFIRYiGCwox +xOzI4ax8IsZ86yK0GpusWyw0WEOqReBRB5pBKHrUhze54nBWOUnuBook2n0EhMtp +IpNBAI0ABmlbwnamvpLbykVNUyj8CnYRm2jEVxqghid2fHRLHaw+pL9zQYPw3xCP +1L/kksDZ6KL2Y/R+BvqpYZ90Zzs= +-----END CERTIFICATE----- diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.key b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.key new file mode 100644 index 0000000000000000000000000000000000000000..948fd5376f9d05dedb67b5bd54f53dcab8a5b1f7 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/good.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAuxRSlAXXq92j +grORSckwHFY2ib+wE3YT1z73c3cfA/OZocM/d3BljYYcm6d+yo8G9BcZIhHJr/sC +k2VgjamC9yi96o6rFv9bvPstE1/aJiwAyKw3Ii+lC4l8l03BooN9esDoe33ckgMw +XF8xke4qv0vgSnhbFYyzb0cTiTYE5ua56Ivf/vvHzhrDcXrBMBlweDsPv9A+fZl3 +U13IAahHvN65EIhlWFZMdZ8XIrhy76bXqRQUn2+xF27RqPvBP72xFJ/VRBMmUy9d +bPDklJR5ntLIIoDRMPz8FDqNxLKC5dLGg3Q6tdeki46O9NMekqrnNzzuN5l9MEI5 +4kfHdf91AgMBAAECggEBALZHeZYMV2Y+LxY5RFnR/kzZUBSuvTPrhA6J0gy5EN2t +oBQacQ4yiYQSkNe3w3dgXluGOd5UYZxuKympfAUQllS+K7NLBvn7ezIpjyl3nH03 +AwEOygou8/7V5YeHlkvfxxrDIXX/be9RzWBMzN+WFDoryLht1lzXCcafJQruB8Aj +djw/4xa+N64+PoNFDqu/a2xBoYJq44xBYx55e9E0PSFN8Dy+eWkNKpUR647uLjMI +CWWi5i45wXtRvqhZ6aVyTbOmSh6NMW3tnEMoCQMYt0GAJn8nlo3jfqVr7lruwzE1 +xWARwLhh5m836l30HaYAwbF1mtYj2Aj/UbomKrAbNXUCgYEA/Noq/Gxc2Ue+4m7Y +HtNbTjLNWN96FD0kYs6xfmVDMJQbSOFkFtZDE246Fu/9M9WfH73+jFVkHZGD4FcQ +9jw2OmemtDPOQhbFvyw35RT6OJ9AruXGElq0w7eQKluliDOH0FfQHq30NEOh7E+r +W/OIG3FgQx6EmGigWGmlt59A7hMCgYEAwyFOa5yVKQDa1YOfbTKzi532SwY61LYY +30xMzFrvAxz3dRy7LN0lGQdPIHt1+A5NcNPflk0aZ5M6c9W1AMCB8qT/0cfZS74f +O3afJS6X/7/awngJsn4xIpXKfvJcGejFG78yaGdKA0r64hAcOJ/i1WZOEnDH9s4c +y10pkKW9bVcCgYEAy0lUGhG9LGuESmQ3mcDvvZUWh4dUjQUDRfVLuoULWfkZXqTn +ZXzp/Ks4RMy503zCLDECgCz/Cs69KhaV6oJKTvaajnLAN147qhGQ9rrA2dkflf3i +G0uV3WytmYCJO1HCtXyENMZCeeqOOoFrEchf+0BR1mSo0if8ElkOBO0itMUCgYBC +SHAREfGBKi8r1Dg1qcFfrdraBVW6p9ivEYYM7N6/2fkdKRNT3BMb+2WfjRC3oHQh +3XbX4X0UrehvLKr9hGcf+3iC1DY2CnEli9fnk3pCf/NZl9kDbuZV7zgIA1/R1UYP +dNU0rdFwaOnsHLjZa0ZsYwImNXVXmxAg7JaP8hz0pQKBgQDDQehYCkTABhwn6PpS +hfW4R9rdPvj7UNHsK0pf8x9w+r0l04qM7y71hKCZ47kVNYNrDWCZfTvFyrMdHGT1 +8KPj4gSjvpuCU9pOKYGqwO5rOY04WJf9gtdlQrK5qNU2TFzIexyhTEkXb1+zbY/J +1KD4NaF9iYRfrMFjKSRzQoUanw== +-----END PRIVATE KEY----- diff --git a/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/unparseable.cfg b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/unparseable.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6b2236550744b13dee31c1c00c3efc0973e74781 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/cmd/gnmi_collector/testdata/unparseable.cfg @@ -0,0 +1,29 @@ +foo: "invalid message" +request: < + key: "interfaces" + value: < + subscribe: < + prefix: < + origin: "openconfig" + > + subscription: < + path: < + elem: < + name: "interfaces" + > + > + > + > + > +> +target: < + key: "target1" + value: < + addresses: "127.0.0.1:9998" + request: "interfaces" + credentials: < + username: "username" + password: "password" + > + > +> diff --git a/deps/github.com/openconfig/gnmi/coalesce/coalesce.go b/deps/github.com/openconfig/gnmi/coalesce/coalesce.go new file mode 100644 index 0000000000000000000000000000000000000000..eb280d4396173b84ac37961ba970a30342307c45 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/coalesce/coalesce.go @@ -0,0 +1,168 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package coalesce provides an in-order queue for collecting outputs from a +// producer to provide inputs to a consumer. Coalescing happens when an output +// has been produced more than once by the producer before being consumed. In +// this case the outputs are coalesced and delivered only once. +package coalesce + +import ( + "context" + "errors" + "sync" +) + +// Queue is a structure that implements in-order delivery of coalesced inputs. +// Operations on Queue are threadsafe and any number of producers may contribute +// to the queue and any number of consumers may receive, however, no fairness +// among consumers is guaranteed and it is possible for starvation to occur. +type Queue struct { + sync.Mutex + // inserted is used to signal blocked consumers that a value has been added. + inserted chan struct{} + // closed is a signal that no Inserts can occur and Next should return error + // for an empty queue. + closed chan struct{} + // coalesced tracks the number of values per interface that have been + // coalesced. + coalesced map[interface{}]uint32 + // queue is the in-order values transferred from producer to consumer. + queue []interface{} +} + +// NewQueue returns an initialized Queue ready for use. +func NewQueue() *Queue { + return &Queue{ + inserted: make(chan struct{}, 1), + closed: make(chan struct{}), + coalesced: make(map[interface{}]uint32), + } +} + +var errClosedQueue = errors.New("closed queue") + +// IsClosedQueue returns whether a given error is coalesce.errClosedQueue. +func IsClosedQueue(err error) bool { + return err == errClosedQueue +} + +// Insert adds i to the queue and returns true if i does not already exist. +// Insert returns an error if the Queue has been closed. +func (q *Queue) Insert(i interface{}) (bool, error) { + select { + case <-q.closed: + return false, errClosedQueue + default: + } + + ok := q.insert(i) + + if ok { + select { + case q.inserted <- struct{}{}: + default: + } + } + + return ok, nil +} + +func (q *Queue) insert(i interface{}) bool { + defer q.Unlock() + q.Lock() + + if _, ok := q.coalesced[i]; ok { + q.coalesced[i]++ + return false + } + q.queue = append(q.queue, i) + q.coalesced[i] = 0 + return true +} + +// Next returns the next item in the queue and the number of duplicates that +// were dropped, or an error if the Queue is empty and has been closed. Calls to +// Next are blocking. +func (q *Queue) Next(ctx context.Context) (interface{}, uint32, error) { + for { + i, coalesced, valid := q.next() + if valid { + return i, coalesced, nil + } + // Wait for an insert or a close. + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + case <-q.inserted: + case <-q.closed: + // Make sure all values are delivered. + if q.Len() == 0 { + return nil, 0, errClosedQueue + } + } + } +} + +// next returns the next item and the number of coalesced duplicates if there is +// an item to consume as indicated by the bool. If the bool is false, the +// caller has to option to block and try again or return an error. +func (q *Queue) next() (interface{}, uint32, bool) { + defer q.Unlock() + q.Lock() + if len(q.queue) == 0 { + return nil, 0, false + } + var i interface{} + i, q.queue = q.queue[0], q.queue[1:] + coalesced := q.coalesced[i] + delete(q.coalesced, i) + return i, coalesced, true +} + +// Len returns the current length of the queue, useful for reporting statistics. +// Since the queue is designed to be operated on by one or more producers and +// one or more consumers, this return value only indicates what the length was +// at the instant it was evaluated which may change before the caller gets the +// return value. +func (q *Queue) Len() int { + defer q.Unlock() + q.Lock() + return len(q.queue) +} + +// Close closes the queue for inserts. +func (q *Queue) Close() { + defer q.Unlock() + q.Lock() + // Avoid panic on closing the closed channel. + select { + case <-q.closed: + return + default: + close(q.closed) + } +} + +// IsClosed reports if the queue is in a closed state. +func (q *Queue) IsClosed() bool { + select { + case <-q.closed: + return true + default: + } + return false +} diff --git a/deps/github.com/openconfig/gnmi/coalesce/coalesce_test.go b/deps/github.com/openconfig/gnmi/coalesce/coalesce_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a3647a01b4ca14779abb7a8c895c6e10dcdb3c60 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/coalesce/coalesce_test.go @@ -0,0 +1,146 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package coalesce + +import ( + "context" + "testing" +) + +func TestInsert(t *testing.T) { + q := NewQueue() + tests := []struct { + s string + want bool + size int + }{ + {"hello", true, 1}, + {"hello", false, 1}, + {"world", true, 2}, + {"hello", false, 2}, + {"world", false, 2}, + {"world", false, 2}, + {"!", true, 3}, + } + for x, tt := range tests { + if got, _ := q.Insert(tt.s); got != tt.want { + t.Errorf("#%d: q.Insert(%q): got %t, want %t", x, tt.s, got, tt.want) + } + if size := q.Len(); size != tt.size { + t.Errorf("#%d: got queue length %d, want %d", x, size, tt.size) + } + } +} + +func TestNext(t *testing.T) { + q := NewQueue() + type stringCount struct { + s string + count uint32 + } + tests := []struct { + s []string + want []stringCount + }{ + {[]string{"hello"}, []stringCount{{"hello", 0}}}, + {[]string{"hello", "hello", "hello"}, []stringCount{{"hello", 2}}}, + {[]string{"hello", "world", "hello"}, []stringCount{{"hello", 1}, {"world", 0}}}, + {[]string{"hello"}, []stringCount{{"hello", 0}}}, + } + for x, tt := range tests { + for _, s := range tt.s { + q.Insert(s) + } + for _, want := range tt.want { + i, count, err := q.Next(context.Background()) + if err != nil { + t.Errorf("#%d: q.Next() unexpected error %v", x, err) + continue + } + s, ok := i.(string) + if !ok { + t.Errorf("#%d: unable to cast to string %v", x, i) + continue + } + if s != want.s || count != want.count { + t.Errorf("#%d: q.Next(): got %q (%d) want %q (%d)", x, s, count, want.s, want.count) + } + } + } +} + +func TestNextCancelContext(t *testing.T) { + q := NewQueue() + start := make(chan struct{}) + done := make(chan struct{}) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + close(start) + _, _, err := q.Next(ctx) + if err == nil { + t.Error("got nil error, want error") + } + close(done) + }() + <-start + cancel() + <-done +} + +func TestClose(t *testing.T) { + ctx := context.Background() + for _, tt := range [][]string{ + {}, + {"a"}, + {"a", "b"}, + {"a", "b", "c"}, + } { + q := NewQueue() + if q.IsClosed() { + t.Error("newly created queue IsClosed() got true, want false") + } + for _, s := range tt { + _, err := q.Insert(s) + if err != nil { + t.Fatal(err) + } + } + q.Close() + if !q.IsClosed() { + t.Error("closed queue IsClosed() got false, want true") + } + if _, err := q.Insert("foo"); !IsClosedQueue(err) { + t.Errorf("q.Insert() got %v, want %v", err, errClosedQueue) + } + for x, want := range tt { + i, _, err := q.Next(ctx) + got, ok := i.(string) + switch { + case err != nil: + t.Errorf("#%d: q.Next(): got error %v before result %q received.", x, err, want) + case !ok || got != want: + t.Errorf("#%d: q.Next(): got %q, want %q", x, got, want) + } + } + if _, _, err := q.Next(ctx); !IsClosedQueue(err) { + t.Errorf("q.Next(): got %v, want %v", err, errClosedQueue) + } + if _, err := q.Insert("foo"); !IsClosedQueue(err) { + t.Errorf("q.Insert() got %v, want %v", err, errClosedQueue) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/collector/collector.go b/deps/github.com/openconfig/gnmi/collector/collector.go new file mode 100644 index 0000000000000000000000000000000000000000..f242bc86a1427500ff615afeb9008668e80e18a4 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/collector/collector.go @@ -0,0 +1,61 @@ +/* +Copyright 2020 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package collector provides a gRPC interface to reconnect targets in a gNMI +// collector. +package collector + +import ( + "context" + "fmt" + + log "github.com/golang/glog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + pb "github.com/openconfig/gnmi/proto/collector" +) + +// Server provides an implementation of a gRPC service to allow reconnection of +// targets. +type Server struct { + reconnect func(string) error +} + +// New constructs a Server struct with a specified handler for reconnection +// requests. +func New(reconnect func(string) error) *Server { + return &Server{reconnect: reconnect} +} + +// Reconnect provides the implementation of the Collector.Reconnect RPC allowing +// one or more gNMI targets to be forcefully reconnected. +func (s *Server) Reconnect(ctx context.Context, request *pb.ReconnectRequest) (*pb.Nil, error) { + var failed []string + for _, target := range request.Target { + if err := s.reconnect(target); err != nil { + failed = append(failed, target) + log.Warningf("Invalid reconnect request for %q", target) + } else { + log.Infof("Forced reconnect of %q", target) + } + } + var err error + if len(failed) > 0 { + err = status.Error(codes.NotFound, fmt.Sprintf("invalid targets %q", failed)) + } + return &pb.Nil{}, err +} diff --git a/deps/github.com/openconfig/gnmi/collector/collector_test.go b/deps/github.com/openconfig/gnmi/collector/collector_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f50204fc396a6d898be8fcce60e4c204dd79bc1c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/collector/collector_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2020 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "context" + "fmt" + "testing" + + pb "github.com/openconfig/gnmi/proto/collector" +) + +func TestReconnect(t *testing.T) { + server := New(func(target string) error { + targets := map[string]bool{ + "target1": true, + "target2": true, + } + if _, ok := targets[target]; !ok { + return fmt.Errorf("invalid target %q", target) + } + return nil + }) + for _, tt := range []struct { + desc string + targets []string + err bool + }{ + {desc: "empty request"}, + {desc: "one target", targets: []string{"target1"}}, + {desc: "multiple targets", targets: []string{"target1", "target2"}}, + {desc: "error target", targets: []string{"unknown"}, err: true}, + {desc: "multiple targets, one error", targets: []string{"target1", "unknown"}, err: true}, + {desc: "multiple errors", targets: []string{"unknown1", "unknown2"}, err: true}, + } { + t.Run(tt.desc, func(t *testing.T) { + _, err := server.Reconnect(context.Background(), &pb.ReconnectRequest{Target: tt.targets}) + switch { + case err != nil && !tt.err: + t.Errorf("got error %v, want nil", err) + case err == nil && tt.err: + t.Error("got nil, want error") + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/compile_protos.sh b/deps/github.com/openconfig/gnmi/compile_protos.sh new file mode 100755 index 0000000000000000000000000000000000000000..af3470dc343aff7c55c73cb4b6ea2583a1d7ead9 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/compile_protos.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +# Go +proto_imports_go="${GOPATH}/src" +protoc -I=$proto_imports_go --go_out=$proto_imports_go --go_opt=paths=source_relative --go-grpc_out=$proto_imports_go --go-grpc_opt=paths=source_relative,require_unimplemented_servers=false github.com/openconfig/gnmi/testing/fake/proto/fake.proto +protoc -I=$proto_imports_go --go_out=$proto_imports_go --go_opt=paths=source_relative --go-grpc_out=$proto_imports_go --go-grpc_opt=paths=source_relative,require_unimplemented_servers=false github.com/openconfig/gnmi/proto/gnmi/gnmi.proto +protoc -I=$proto_imports_go --go_out=$proto_imports_go --go_opt=paths=source_relative --go-grpc_out=$proto_imports_go --go-grpc_opt=paths=source_relative,require_unimplemented_servers=false github.com/openconfig/gnmi/proto/collector/collector.proto +protoc -I=$proto_imports_go --go_out=$proto_imports_go --go_opt=paths=source_relative github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto +protoc -I=$proto_imports_go --go_out=$proto_imports_go --go_opt=paths=source_relative github.com/openconfig/gnmi/proto/target/target.proto + +# Python +proto_imports_python=".:${GOPATH}/src" +python3 -m grpc_tools.protoc -I=$proto_imports_python --python_out=. --grpc_python_out=. testing/fake/proto/fake.proto +python3 -m grpc_tools.protoc -I=$proto_imports_python --python_out=. --grpc_python_out=. proto/gnmi/gnmi.proto +python3 -m grpc_tools.protoc -I=$proto_imports_python --python_out=. --grpc_python_out=. proto/gnmi_ext/gnmi_ext.proto +python3 -m grpc_tools.protoc -I=$proto_imports_python --python_out=. --grpc_python_out=. proto/target/target.proto +python3 -m grpc_tools.protoc -I=$proto_imports_python --python_out=. --grpc_python_out=. proto/collector/collector.proto diff --git a/deps/github.com/openconfig/gnmi/connection/connection.go b/deps/github.com/openconfig/gnmi/connection/connection.go new file mode 100644 index 0000000000000000000000000000000000000000..a75de139a953a0c766ecbeb87eeeb394a4f0ee7a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/connection/connection.go @@ -0,0 +1,158 @@ +/* +Copyright 2019 Google LLC. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package connection manages cached client connections to gRPC servers. +package connection + +import ( + "context" + "errors" + "sync" + + log "github.com/golang/glog" + "google.golang.org/grpc" +) + +type connection struct { + id string + ref int + c *grpc.ClientConn // Set during dial attempt, before signaling ready. + err error // Set during dial attempt, before signaling ready. + ready chan struct{} +} + +// Manager provides functionality for creating cached client gRPC connections. +type Manager struct { + opts []grpc.DialOption + d Dial + + mu sync.Mutex + conns map[string]*connection +} + +// NewManager creates a new Manager. The opts arguments are used +// to dial new gRPC targets, with the same semantics as grpc.DialContext. +func NewManager(opts ...grpc.DialOption) (*Manager, error) { + return NewManagerCustom(grpc.DialContext, opts...) +} + +// Dial defines a function to dial the gRPC connection. +type Dial func(ctx context.Context, target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) + +// NewManagerCustom creates a new Manager. The opts arguments are used +// to dial new gRPC targets, using the provided Dial function. +func NewManagerCustom(d Dial, opts ...grpc.DialOption) (*Manager, error) { + if d == nil { + return nil, errors.New("nil Dial provided") + } + m := &Manager{} + m.conns = map[string]*connection{} + m.opts = opts + m.d = d + return m, nil +} + +// remove should be called while locking m. +func (m *Manager) remove(addr string) { + c, ok := m.conns[addr] + if !ok { + log.Errorf("Connection %q missing or already removed", addr) + } + delete(m.conns, addr) + if c.c != nil { + if err := c.c.Close(); err != nil { + log.Errorf("Error cleaning up connection %q: %v", addr, err) + } + } +} + +func (m *Manager) dial(ctx context.Context, addr string, c *connection) { + defer close(c.ready) + cc, err := m.d(ctx, addr, m.opts...) + if err != nil { + log.Infof("Error creating gRPC connection to %q: %v", addr, err) + m.mu.Lock() + m.remove(addr) + c.err = err + m.mu.Unlock() + return + } + + log.Infof("Created gRPC connection to %q", addr) + c.c = cc +} + +func (c *connection) done(m *Manager) func() { + var once sync.Once + fn := func() { + if c == nil { + log.Error("Attempted to call done on nil connection") + return + } + m.mu.Lock() + defer m.mu.Unlock() + c.ref-- + if c.ref <= 0 { + m.remove(c.id) + } + } + return func() { + once.Do(fn) + } +} + +func newConnection(addr string) *connection { + return &connection{ + id: addr, + ready: make(chan struct{}), + } +} + +// Connection creates a new grpc.ClientConn to the destination address or +// returns the existing connection, along with a done function. +// +// Usage is registered when a connection is retrieved using Connection. Clients +// should call the returned done function when the returned connection handle is +// unused. Subsequent calls to the same done function have no effect. If an +// error is returned, done has no effect. Connections with no usages will be +// immediately closed and removed from Manager. +// +// If there is already a pending connection attempt for the same addr, +// Connection blocks until that attempt finishes and returns a shared result. +// Note that canceling the context of a pending attempt early would propagate +// an error to blocked callers. +func (m *Manager) Connection(ctx context.Context, addr string) (conn *grpc.ClientConn, done func(), err error) { + select { + case <-ctx.Done(): + return nil, func() {}, ctx.Err() + default: + m.mu.Lock() + c, ok := m.conns[addr] + if !ok { + c = newConnection(addr) + m.conns[addr] = c + go m.dial(ctx, addr, c) + } + c.ref++ + m.mu.Unlock() + + <-c.ready + if c.err != nil { + return nil, func() {}, c.err + } + log.V(2).Infof("Reusing connection %q", addr) + return c.c, c.done(m), nil + } +} diff --git a/deps/github.com/openconfig/gnmi/connection/connection_test.go b/deps/github.com/openconfig/gnmi/connection/connection_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b8849fa3e49e3f07b688f4b4110a47cd6c00a0ef --- /dev/null +++ b/deps/github.com/openconfig/gnmi/connection/connection_test.go @@ -0,0 +1,300 @@ +/* +Copyright 2019 Google LLC. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package connection + +import ( + "context" + "errors" + "net" + "sync" + "testing" + "time" + + "google.golang.org/grpc" + + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +func newDevice(t *testing.T) (string, func()) { + t.Helper() + + srv := grpc.NewServer() + s := &pb.UnimplementedGNMIServer{} + pb.RegisterGNMIServer(srv, s) + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + go srv.Serve(lis) + + return lis.Addr().String(), srv.Stop +} + +func TestCtxCanceled(t *testing.T) { + addr, stop := newDevice(t) + defer stop() + ctx, cancel := context.WithCancel(context.Background()) + cancel() + m, err := NewManager(grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to initialize Manager: %v", err) + } + if _, _, err := m.Connection(ctx, addr); err == nil { + t.Error("Connection returned no error, want error") + } +} + +func assertConns(t *testing.T, m *Manager, want int) { + t.Helper() + if l := len(m.conns); l != want { + t.Fatalf("got %v connections, want %v", l, want) + } +} + +func assertRefs(t *testing.T, m *Manager, addr string, want int) { + t.Helper() + c, ok := m.conns[addr] + if !ok { + t.Fatalf("connection %q missing", addr) + } + if r := c.ref; r != want { + t.Fatalf("got %v references, want %v", r, want) + } +} + +func TestConcurrentConnection(t *testing.T) { + addr, stop := newDevice(t) + defer stop() + ctx := context.Background() + m, err := NewManager(grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to initialize Manager: %v", err) + } + var wg sync.WaitGroup + lim := 300 + wg.Add(lim) + + for i := 0; i < lim; i++ { + go func() { + conn, _, err := m.Connection(ctx, addr) + switch { + case err != nil: + t.Errorf("got error creating connection: %v, want no error", err) + case conn == nil: + t.Error("got nil connection, expected not nil") + } + wg.Done() + }() + } + wg.Wait() + + assertConns(t, m, 1) + assertRefs(t, m, addr, lim) +} + +func TestDone(t *testing.T) { + addr, stop := newDevice(t) + defer stop() + ctx := context.Background() + m, err := NewManager(grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to initialize Manager: %v", err) + } + + _, done1, err := m.Connection(ctx, addr) + if err != nil { + t.Fatalf("got error creating connection: %v, want no error", err) + } + assertConns(t, m, 1) + assertRefs(t, m, addr, 1) + + _, done2, err := m.Connection(ctx, addr) + if err != nil { + t.Fatalf("got error creating connection: %v, want no error", err) + } + assertConns(t, m, 1) + assertRefs(t, m, addr, 2) + + done1() + assertConns(t, m, 1) + assertRefs(t, m, addr, 1) + + done2() + assertConns(t, m, 0) + + done1() // No panic. + done2() // No panic. +} + +// TestConnectionDone simulates concurrently creating, reusing, and +// destroying multiple connections to different addresses. +func TestConnectionDone(t *testing.T) { + type connTest struct { + desc string + connections int + dones int + connectionID string // Populated by test. + } + + tests := []*connTest{ + // NOTE: done counts are relatively low to avoid connection + // thrashing that causes gRPC attempts to hang and affect this test. + { + desc: "connection1", + connections: 200, + dones: 20, + }, + { + desc: "connection2", + connections: 200, + dones: 25, + }, + { + desc: "connection3", + connections: 200, + dones: 30, + }, + } + + process := func(ctx context.Context, t *testing.T, c *connTest, m *Manager, wg *sync.WaitGroup) { + for i := 0; i < c.connections; i++ { + d := i + go func() { + conn, done, err := m.Connection(ctx, c.connectionID) + wg.Done() + if conn == nil { + t.Error("got nil connection") + } + if err != nil { + t.Errorf("got error creating connection: %v, want no error", err) + } + if d < c.dones { + go func() { + done() + wg.Done() + }() + } + }() + } + } + + var wg sync.WaitGroup + m, err := NewManager(grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to initialize Manager: %v", err) + } + for _, tt := range tests { + wg.Add(tt.connections + tt.dones) + addr, stop := newDevice(t) + defer stop() + tt.connectionID = addr + } + for _, tt := range tests { + go process(context.Background(), t, tt, m, &wg) + } + wg.Wait() + + if len(m.conns) != len(tests) { + t.Fatalf("got %v connections, want %v", len(m.conns), len(tests)) + } + for _, tt := range tests { + _, ok := m.conns[tt.connectionID] + if !ok { + t.Fatalf("%s: missing connection", tt.desc) + } + assertRefs(t, m, tt.connectionID, tt.connections-tt.dones) + } +} + +func errDialWait(d time.Duration) func(_ context.Context, _ string, _ ...grpc.DialOption) (*grpc.ClientConn, error) { + return func(_ context.Context, _ string, _ ...grpc.DialOption) (*grpc.ClientConn, error) { + time.Sleep(d) + return nil, errors.New("error occurred") + } +} + +func TestConcurrentDialErr(t *testing.T) { + ctx := context.Background() + m, err := NewManagerCustom(errDialWait(time.Second), grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to initialize Manager: %v", err) + } + var wg sync.WaitGroup + errs := make(chan error) + defer close(errs) + start := make(chan struct{}) + + lim := 2 + wg.Add(lim) + for i := 0; i < lim; i++ { + go func() { + wg.Done() + <-start + _, _, err := m.Connection(ctx, "") + errs <- err + }() + } + + // Wait for all goroutines to be ready. + wg.Wait() + close(start) + + var prevErr error + for i := 0; i < lim; i++ { + err := <-errs + if err == nil { + t.Fatal("got no error, want error") + } + if prevErr == nil { + prevErr = err + } else if err != prevErr { + t.Fatal("got different error instance, want same") + } + } +} + +func TestNewManagerCustom(t *testing.T) { + tests := []struct { + desc string + d Dial + opts []grpc.DialOption + wantErr bool + }{ + { + desc: "missing dial", + opts: []grpc.DialOption{grpc.WithBlock()}, + wantErr: true, + }, { + desc: "missing opts", + d: grpc.DialContext, + }, { + desc: "valid dial and opts", + opts: []grpc.DialOption{grpc.WithBlock()}, + d: grpc.DialContext, + }, + } + + for _, tt := range tests { + _, err := NewManagerCustom(tt.d, tt.opts...) + switch { + case err == nil && !tt.wantErr: + case err == nil && tt.wantErr: + t.Errorf("%v: got no error, want error.", tt.desc) + case err != nil && !tt.wantErr: + t.Errorf("%v: got error, want no error.", tt.desc) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/ctree/tree.go b/deps/github.com/openconfig/gnmi/ctree/tree.go new file mode 100644 index 0000000000000000000000000000000000000000..98e28986738893bf6f1bb6747e801c9f2a345304 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/ctree/tree.go @@ -0,0 +1,451 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package ctree implements a Tree container whose methods are all thread-safe +// allowing concurrent access for multiple goroutines. This container was +// designed to support concurrent reads using sync.RWLock and are optimized for +// situations where reads and updates to previously Added values are +// dominant. The acquisition of write locks is avoided wherever possible. +package ctree + +import ( + "fmt" + "sort" + "strings" + "sync" +) + +type branch map[string]*Tree + +// Tree is a thread-safe container. +type Tree struct { + mu sync.RWMutex + // Each node is either a leaf or a branch. + leafBranch interface{} +} + +// Leaf is a Tree node that represents a leaf. +// +// Leaf is safe for use from multiple goroutines and will return the latest +// value. +// This means that if value in this leaf was updated after Leaf was retrieved, +// Value will return the updated content, not the original one. +// This also means that multiple calls to Value may return different results. +type Leaf Tree + +// DetachedLeaf returns a Leaf that's not attached to any tree. +func DetachedLeaf(val interface{}) *Leaf { + return &Leaf{leafBranch: val} +} + +// Value returns the latest value stored in this leaf. Value is safe to call on +// nil Leaf. +func (l *Leaf) Value() interface{} { + if l == nil { + return nil + } + defer l.mu.RUnlock() + l.mu.RLock() + return l.leafBranch +} + +// Update sets the value of this Leaf to val. +func (l *Leaf) Update(val interface{}) { + defer l.mu.Unlock() + l.mu.Lock() + l.leafBranch = val +} + +func newBranch(path []string, value interface{}) *Tree { + if len(path) == 0 { + return &Tree{leafBranch: value} + } + return &Tree{leafBranch: branch{path[0]: newBranch(path[1:], value)}} +} + +// isBranch assumes the calling function holds a lock on t. +func (t *Tree) isBranch() bool { + _, ok := t.leafBranch.(branch) + return ok +} + +// IsBranch returns whether the Tree node represents a branch. +// Returns false if called on a nil node. +func (t *Tree) IsBranch() bool { + if t == nil { + return false + } + defer t.mu.RUnlock() + t.mu.RLock() + return t.isBranch() +} + +// Children returns a mapping of child nodes if current node represents a branch, nil otherwise. +func (t *Tree) Children() map[string]*Tree { + if t == nil { + return nil + } + defer t.mu.RUnlock() + t.mu.RLock() + if t.isBranch() { + ret := make(branch) + for k, v := range t.leafBranch.(branch) { + ret[k] = v + } + return ret + } + return nil +} + +// Value returns the latest value stored in node t if it represents a leaf, nil otherwise. Value is safe to call on +// nil Tree. +func (t *Tree) Value() interface{} { + if t == nil { + return nil + } + defer t.mu.RUnlock() + t.mu.RLock() + if t.isBranch() { + return nil + } + return t.leafBranch +} + +// slowAdd will add a new branch to Tree t all the way to the leaf containing +// value, unless another routine added the branch before the write lock was +// acquired, in which case it will return to the normal Add for the remaining +// path that exists, but while still holding the write lock. This routine is +// called as a fallback by Add when a node does not already exist. +func (t *Tree) slowAdd(path []string, value interface{}) error { + if t.leafBranch == nil { + t.leafBranch = branch{} + } + switch b := t.leafBranch.(type) { + case branch: + // Verify the branch was not added by another routine during the + // reader/writer lock exchange. + br := b[path[0]] + if br == nil { + br = newBranch(path[1:], value) + b[path[0]] = br + } + return br.Add(path[1:], value) + default: + return fmt.Errorf("attempted to add value %#v at path %q which is already a leaf with value %#v", value, path, t.leafBranch) + } +} + +func (t *Tree) terminalAdd(value interface{}) error { + defer t.mu.Unlock() + t.mu.Lock() + if _, ok := t.leafBranch.(branch); ok { + return fmt.Errorf("attempted to add a leaf in place of a branch") + } + t.leafBranch = value + return nil +} + +func (t *Tree) intermediateAdd(path []string, value interface{}) error { + readerLocked := true + defer func() { + if readerLocked { + t.mu.RUnlock() + } + }() + t.mu.RLock() + var br *Tree + switch b := t.leafBranch.(type) { + case nil: + case branch: + br = b[path[0]] + default: + return fmt.Errorf("attempted to add value %#v at path %q which is already a leaf with value %#v", value, path, t.leafBranch) + } + if br == nil { + // Exchange the reader lock on t for a writer lock to add new node(s) to the + // Tree. + t.mu.RUnlock() + readerLocked = false + defer t.mu.Unlock() + t.mu.Lock() + return t.slowAdd(path, value) + } + return br.Add(path[1:], value) +} + +// Add adds value to the Tree at the specified path and returns true on +// success. +func (t *Tree) Add(path []string, value interface{}) error { + if len(path) == 0 { + return t.terminalAdd(value) + } + return t.intermediateAdd(path, value) +} + +// Get returns the Tree node if path points to it, nil otherwise. +// All nodes in path must be fully specified with no globbing (*). +func (t *Tree) Get(path []string) *Tree { + defer t.mu.RUnlock() + t.mu.RLock() + if len(path) == 0 { + return t + } + if b, ok := t.leafBranch.(branch); ok { + if br := b[path[0]]; br != nil { + return br.Get(path[1:]) + } + } + return nil +} + +// GetLeafValue returns the leaf value if path points to a leaf in t, nil otherwise. All +// nodes in path must be fully specified with no globbing (*). +func (t *Tree) GetLeafValue(path []string) interface{} { + return t.Get(path).Value() +} + +// GetLeaf returns the leaf node if path points to a leaf in t, nil otherwise. All +// nodes in path must be fully specified with no globbing (*). +func (t *Tree) GetLeaf(path []string) *Leaf { + return (*Leaf)(t.Get(path)) +} + +// VisitFunc is a callback func triggered on leaf values by Query and Walk. +// +// The provided Leaf is the leaf node of the tree, val is the value stored +// inside of it. l can be retained after VisitFunc returns and l.Value() can be +// called to get the latest value for that leaf. +// +// Note that l.Value can *not* be called inside VisitFunc, because the node is +// already locked by Query/Walk. +// +// If the function returns an error, the Walk or Query will terminate early and +// return the supplied error. +type VisitFunc func(path []string, l *Leaf, val interface{}) error + +func (t *Tree) enumerateChildren(prefix, path []string, f VisitFunc) error { + // Caller should hold a read lock on t. + if len(path) == 0 { + switch b := t.leafBranch.(type) { + case branch: + for k, br := range b { + if err := br.queryInternal(append(prefix, k), path, f); err != nil { + return err + } + } + default: + return f(prefix, (*Leaf)(t), t.leafBranch) + } + return nil + } + if b, ok := t.leafBranch.(branch); ok { + for k, br := range b { + if err := br.queryInternal(append(prefix, k), path[1:], f); err != nil { + return err + } + } + } + return nil +} + +// Query calls f for all leaves that match a given query where zero or more +// nodes in path may be specified by globs (*). Results and their full paths +// are passed to f as they are found in the Tree. No ordering of paths is +// guaranteed. +func (t *Tree) Query(path []string, f VisitFunc) error { + return t.queryInternal(nil, path, f) +} + +func (t *Tree) queryInternal(prefix, path []string, f VisitFunc) error { + defer t.mu.RUnlock() + t.mu.RLock() + if len(path) == 0 || path[0] == "*" { + return t.enumerateChildren(prefix, path, f) + } + if b, ok := t.leafBranch.(branch); ok { + if br := b[path[0]]; br != nil { + return br.queryInternal(append(prefix, path[0]), path[1:], f) + } + } + return nil +} + +func (t *Tree) walkInternal(path []string, f VisitFunc) error { + defer t.mu.RUnlock() + t.mu.RLock() + if b, ok := t.leafBranch.(branch); ok { + l := len(path) + for name, br := range b { + p := make([]string, l, l+1) + copy(p, path) + if err := br.walkInternal(append(p, name), f); err != nil { + return err + } + } + return nil + } + // If this is the root node and it has no children and the value is nil, + // most likely it's just the zero value of Tree not a valid leaf. + if len(path) == 0 && t.leafBranch == nil { + return nil + } + return f(path, (*Leaf)(t), t.leafBranch) +} + +// Walk calls f for all leaves. +func (t *Tree) Walk(f VisitFunc) error { + return t.walkInternal(nil, f) +} + +func (t *Tree) walkInternalSorted(path []string, f VisitFunc) error { + defer t.mu.RUnlock() + t.mu.RLock() + if b, ok := t.leafBranch.(branch); ok { + names := make([]string, 0, len(b)) + for name := range b { + names = append(names, name) + } + sort.Strings(names) + l := len(path) + for _, name := range names { + p := make([]string, l, l+1) + copy(p, path) + if err := b[name].walkInternalSorted(append(p, name), f); err != nil { + return err + } + } + return nil + } + // If this is the root node and it has no children and the value is nil, + // most likely it's just the zero value of Tree not a valid leaf. + if len(path) == 0 && t.leafBranch == nil { + return nil + } + return f(path, (*Leaf)(t), t.leafBranch) +} + +// WalkSorted calls f for all leaves in string sorted order. +func (t *Tree) WalkSorted(f VisitFunc) error { + return t.walkInternalSorted(nil, f) +} + +// internalDelete removes nodes recursively that match subpath. It returns true +// if the current node is to be removed from the parent and a slice of subpaths +// ([]string) for all leaves deleted thus far. +func (t *Tree) internalDelete(subpath []string, condition func(interface{}) bool) (bool, [][]string) { + if len(subpath) == 0 || subpath[0] == "*" { + if len(subpath) != 0 { + subpath = subpath[1:] + } + // The subpath is a full path to a leaf. + switch b := t.leafBranch.(type) { + case branch: + // The subpath terminates in a branch node and will recursively delete any + // progeny leaves. + allLeaves := [][]string{} + for k, v := range b { + del, leaves := v.internalDelete(subpath, condition) + leaf := []string{k} + for _, l := range leaves { + allLeaves = append(allLeaves, append(leaf, l...)) + } + if del { + delete(b, k) + } + } + return len(t.leafBranch.(branch)) == 0, allLeaves + default: + if condition(t.leafBranch) { + // The second parameter is an empty path that will be filled as recursion + // unwinds for this leaf that will be deleted in its parent. + return true, [][]string{[]string{}} + } + return false, [][]string{} + } + } + if b, ok := t.leafBranch.(branch); ok { + // Continue to recurse on subpath while it matches nodes in the Tree. + if br := b[subpath[0]]; br != nil { + delBr, allLeaves := br.internalDelete(subpath[1:], condition) + leaf := []string{subpath[0]} + // Prepend branch node name to all progeny leaves of branch. + for i := range allLeaves { + allLeaves[i] = append(leaf, allLeaves[i]...) + } + // Remove branch if requested. + if delBr { + delete(b, subpath[0]) + } + // Node contains no branches so remove this branch node as well. + if len(b) == 0 { + return true, allLeaves + } + // This node still contains branches, don't delete it. + return false, allLeaves + } + } + // The subpath doesn't match any Tree branch, return empty list of leaves. + return false, [][]string{} +} + +// DeleteConditional removes all leaves at or below subpath as well as any +// ancestors with no children for those leaves which the given conditional +// function returns true, returning the list of all leaves removed. +// DeleteConditional prevents all other concurrent access. +func (t *Tree) DeleteConditional(subpath []string, condition func(interface{}) bool) [][]string { + // It is possible that a single delete operation will remove the whole Tree, + // so only the top level write lock is obtained to prevent concurrent accesses + // to the entire Tree. + defer t.mu.Unlock() + t.mu.Lock() + delBr, leaves := t.internalDelete(subpath, condition) + if delBr { + t.leafBranch = nil + } + return leaves +} + +// Delete removes all leaves at or below subpath as well as any ancestors with +// no children, returning the list of all leaves removed. Deletes prevent all +// other concurrent access. +func (t *Tree) Delete(subpath []string) [][]string { + always := func(interface{}) bool { return true } + return t.DeleteConditional(subpath, always) +} + +// String implements the string interface for Tree returning a stable output +// sorting keys at each level. +func (t *Tree) String() string { + if t == nil { + return "" + } + defer t.mu.RUnlock() + t.mu.RLock() + if t.isBranch() { + b := t.leafBranch.(branch) + keys := make([]string, 0, len(b)) + for k := range b { + keys = append(keys, k) + } + sort.Strings(keys) + children := make([]string, 0, len(b)) + for _, k := range keys { + children = append(children, fmt.Sprintf("%q: %s", k, b[k])) + } + return fmt.Sprintf("{ %s }", strings.Join(children, ", ")) + } + return fmt.Sprintf("%#v", t.leafBranch) +} diff --git a/deps/github.com/openconfig/gnmi/ctree/tree_test.go b/deps/github.com/openconfig/gnmi/ctree/tree_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0a2c0b7baacadf4f8e387265eb0523cd6883bed9 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/ctree/tree_test.go @@ -0,0 +1,833 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ctree + +import ( + "errors" + "fmt" + "reflect" + "runtime" + "strings" + "sync" + "sync/atomic" + "testing" + + "github.com/kylelemons/godebug/pretty" +) + +func TestAdd(t *testing.T) { + tr := &Tree{} + if err := tr.Add([]string{}, "foo"); err != nil { + t.Error(err) + } + if err := tr.Add([]string{"a"}, "foo"); err == nil { + t.Error("got nil, expected error adding a leaf to a leaf") + } + tr = &Tree{} + if err := tr.Add([]string{"a"}, "foo"); err != nil { + t.Error(err) + } + if err := tr.Add([]string{}, "foo"); err == nil { + t.Error("got nil, want error adding leaf in place of a branch") + } + if err := tr.Add([]string{"a", "b"}, "foo"); err == nil { + t.Error("got nil, want error adding a leaf to a leaf") + } + if err := tr.Add([]string{"b", "c", "d", "e"}, "foo"); err != nil { + t.Error(err) + } +} + +func TestSlowAdd(t *testing.T) { + for _, test := range []struct { + tree *Tree + path []string + expectErr bool + }{ + { + tree: &Tree{leafBranch: "not a branch"}, + path: []string{"a"}, + expectErr: true, + }, + { + tree: &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}}}, + path: []string{"a"}, + expectErr: false, + }, + } { + testVal := "testVal" + err := test.tree.slowAdd(test.path, testVal) + gotErr := err != nil + if gotErr != test.expectErr { + t.Errorf("slowAdd(%v, %v) = %v, want %v", test.path, testVal, gotErr, test.expectErr) + } + } +} + +func TestTreeGetLeafValue(t *testing.T) { + tr := &Tree{} + for x, tt := range []struct { + path []string + value string + }{ + {[]string{"a", "b"}, "value0"}, + {[]string{"a", "c"}, "value1"}, + {[]string{"a", "d"}, "value2"}, + {[]string{"b"}, "value2"}, + {[]string{"c", "a"}, "value3"}, + {[]string{"c", "b", "a"}, "value4"}, + {[]string{"c", "d"}, "value5"}, + } { + // Value shouldn't exist before addition. + if value := tr.GetLeafValue(tt.path); nil != value { + t.Errorf("#%d: got %v, expected %v", x, value, nil) + } + if err := tr.Add(tt.path, tt.value); err != nil { + t.Error(err) + } + value := tr.GetLeafValue(tt.path) + // Value should exist on successful addition. + if tt.value != value { + t.Errorf("#%d: got %v, expected %v", x, value, tt.value) + } + } +} + +var testPaths = [][]string{ + []string{"a", "b", "c"}, + []string{"a", "d"}, + []string{"b", "a", "d"}, + []string{"b", "c", "d"}, + []string{"c", "d", "e", "f", "g", "h", "i"}, + []string{"d"}, +} + +func buildTree(t *Tree) { + buildTreePaths(t, testPaths) +} + +func buildTreePaths(t *Tree, paths [][]string) { + for _, path := range paths { + value := strings.Join(path, "/") + t.Add(path, value) + } +} + +type expectedQuery struct { + query []string + results map[string]interface{} +} + +func TestQuery(t *testing.T) { + tr := &Tree{} + results := make(map[string]interface{}) + appendResults := func(p []string, _ *Leaf, v interface{}) error { results[strings.Join(p, "/")] = v; return nil } + + tr.Query([]string{"*"}, appendResults) + if len(results) > 0 { + t.Errorf("tr.Query: got %d results, expected 0", len(results)) + } + + buildTree(tr) + // Test a set of queries. + for x, tt := range []expectedQuery{ + {[]string{"a", "d"}, map[string]interface{}{"a/d": "a/d"}}, + {[]string{"a"}, map[string]interface{}{"a/d": "a/d", "a/b/c": "a/b/c"}}, + // A trailing glob is equivalent to a query without it, as above. + {[]string{"a", "*"}, map[string]interface{}{"a/d": "a/d", "a/b/c": "a/b/c"}}, + {[]string{"*"}, map[string]interface{}{"a/d": "a/d", "a/b/c": "a/b/c", "b/c/d": "b/c/d", "b/a/d": "b/a/d", "c/d/e/f/g/h/i": "c/d/e/f/g/h/i", "d": "d"}}, + {[]string{"*", "*", "d"}, map[string]interface{}{"b/c/d": "b/c/d", "b/a/d": "b/a/d"}}, + {[]string{"c", "d", "e"}, map[string]interface{}{"c/d/e/f/g/h/i": "c/d/e/f/g/h/i"}}, + } { + results = make(map[string]interface{}) + tr.Query(tt.query, appendResults) + + if !reflect.DeepEqual(results, tt.results) { + t.Errorf("#%d: got results: %v\nwant results: %v", x, results, tt.results) + } + } +} + +func TestUpdateLeaf(t *testing.T) { + tr := &Tree{} + buildTree(tr) + + l := tr.GetLeaf(testPaths[0]) + + nv := "new value" + tr.Add(testPaths[0], nv) + + if got := l.Value(); got != nv { + t.Errorf("Value on updated leaf returned %+v, want %+v", got, nv) + } +} + +func TestWalk(t *testing.T) { + tr := &Tree{} + buildTree(tr) + paths := [][]string{} + tr.Walk(func(path []string, _ *Leaf, value interface{}) error { + got, want := value.(string), strings.Join(path, "/") + if got != want { + t.Errorf("Walk got value %q, want %q", got, want) + } + paths = append(paths, path) + return nil + }) + if got, want := len(paths), len(testPaths); got != want { + t.Errorf("Walk got %d paths, want %d", got, want) + } +gotpaths: + for _, p := range paths { + for _, tp := range testPaths { + if reflect.DeepEqual(p, tp) { + continue gotpaths + } + } + t.Errorf("Walk got path %q, wanted one of %q", p, testPaths) + } +wantpaths: + for _, tp := range testPaths { + for _, p := range paths { + if reflect.DeepEqual(p, tp) { + continue wantpaths + } + } + t.Errorf("Walk got paths %q, want %q included", paths, tp) + } +} + +func TestWalkSorted(t *testing.T) { + tr := &Tree{} + buildTree(tr) + paths := [][]string{} + tr.WalkSorted(func(path []string, _ *Leaf, value interface{}) error { + got, want := value.(string), strings.Join(path, "/") + if got != want { + t.Errorf("WalkSorted got value %q, want %q", got, want) + } + paths = append(paths, path) + return nil + }) + if got, want := len(paths), len(testPaths); got != want { + t.Errorf("WalkSorted got %d paths, want %d", got, want) + } + if !reflect.DeepEqual(paths, testPaths) { + t.Errorf("WalkSorted got %q, want %q", paths, testPaths) + } +} + +func TestEmptyWalk(t *testing.T) { + tr := &Tree{} + tr.Walk(func(_ []string, _ *Leaf, _ interface{}) error { + t.Error("Walk on empty tree should not call func.") + return nil + }) + tr.WalkSorted(func(_ []string, _ *Leaf, _ interface{}) error { + t.Error("WalkSorted on empty tree should not call func.") + return nil + }) +} + +func TestVisitFuncError(t *testing.T) { + tr := &Tree{} + buildTree(tr) + err := errors.New("error") + var count int + visitFunc := func(_ []string, _ *Leaf, _ interface{}) error { + count++ + switch count { + case 1: + return err + default: + return fmt.Errorf("got count %d, want 1", count) + } + } + + if got := tr.Query([]string{"*"}, visitFunc); got != err { + t.Errorf("got error %q, want error %q", got, err) + } + + count = 0 + if got := tr.Walk(visitFunc); got != err { + t.Errorf("got error %q, want error %q", got, err) + } + + count = 0 + if got := tr.WalkSorted(visitFunc); got != err { + t.Errorf("got error %q, want error %q", got, err) + } +} + +type expectedDelete struct { + subpath []string + leaves map[string]bool +} + +func TestDelete(t *testing.T) { + tr := &Tree{} + deleted := tr.Delete([]string{"a", "b"}) + if len(deleted) > 0 { + t.Errorf("Delete on empty tree should return empty slice.") + } + for x, tt := range []expectedDelete{ + {[]string{"x"}, map[string]bool{}}, + // root delete with glob appended for a non-existing root + {[]string{"x", "*"}, map[string]bool{}}, + {[]string{"d"}, map[string]bool{"d": true}}, + {[]string{"a"}, map[string]bool{"a/d": true, "a/b/c": true}}, + // root delete with a glob appended + {[]string{"a", "*"}, map[string]bool{"a/d": true, "a/b/c": true}}, + {[]string{"b", "c", "d"}, map[string]bool{"b/c/d": true}}, + // delete with glob in the middle of the path + {[]string{"b", "*", "d"}, map[string]bool{"b/a/d": true, "b/c/d": true}}, + // delete with glob in the middle of the path for a non-existing path + {[]string{"b", "*", "x"}, map[string]bool{}}, + {[]string{"b"}, map[string]bool{"b/a/d": true, "b/c/d": true}}, + {[]string{"c", "d", "e"}, map[string]bool{"c/d/e/f/g/h/i": true}}, + {[]string{}, map[string]bool{"a/d": true, "a/b/c": true, "b/a/d": true, "b/c/d": true, "c/d/e/f/g/h/i": true, "d": true}}, + // just glob in the path to delete all the tree + {[]string{"*"}, map[string]bool{"a/d": true, "a/b/c": true, "b/a/d": true, "b/c/d": true, "c/d/e/f/g/h/i": true, "d": true}}, + } { + // Rebuild tree for each query. + buildTree(tr) + for _, leaf := range tr.Delete(tt.subpath) { + leafpath := strings.Join(leaf, "/") + if _, ok := tt.leaves[leafpath]; !ok { + t.Errorf("#%d: unexpected deleted leaf %v", x, leaf) + } + delete(tt.leaves, leafpath) + } + if len(tt.leaves) > 0 { + t.Errorf("#%d: expected leaves missing from return: %v", x, tt.leaves) + } + } + if tr.leafBranch != nil { + t.Errorf("tree should be empty, but root still has branches %#v", tr.leafBranch) + } +} + +func TestDeleteConditional(t *testing.T) { + never := func(interface{}) bool { return false } + tr := &Tree{} + buildTree(tr) + leaves := tr.DeleteConditional([]string{}, never) + if len(leaves) > 0 { + t.Errorf("Leaves deleted for false condition: %v", leaves) + } + always := func(interface{}) bool { return true } + // This is the same test as the last case for TestDelete, above. + leaves = tr.DeleteConditional([]string{}, always) + if len(leaves) != 6 { + t.Errorf("Not all leaves deleted: %v", leaves) + } + valEqualsD := func(v interface{}) bool { return v == "d" } + buildTree(tr) + leaves = tr.DeleteConditional([]string{}, valEqualsD) + if expected := [][]string{[]string{"d"}}; !reflect.DeepEqual(expected, leaves) { + t.Errorf("got %v, expected %v", leaves, expected) + } + if v := tr.GetLeafValue([]string{"d"}); nil != v { + t.Errorf("got %v, expected %v", v, nil) + } +} + +type expectedTreeEqual struct { + t1 *Tree + t2 *Tree + equal bool +} + +func TestEqual(t *testing.T) { + for x, tt := range []expectedTreeEqual{ + {nil, nil, true}, + {nil, &Tree{}, false}, + {&Tree{}, nil, false}, + {&Tree{}, &Tree{}, true}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}}}, + &Tree{}, + false}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}}}, + &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}}}, + true}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: "b"}}}, + &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}}}, + false}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}}}, + &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: "b"}}}, + false}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: "b"}}}, + &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: "b"}}}, + true}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: branch{"b/c": &Tree{leafBranch: "b/c"}}}}}, + &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: "b"}}}, + false}, + {&Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: branch{"b/c": &Tree{leafBranch: "b/c"}}}}}, + &Tree{leafBranch: branch{"a": &Tree{leafBranch: "a"}, "b": &Tree{leafBranch: branch{"b/c": &Tree{leafBranch: "b/c"}}}}}, + true}, + } { + if equal := reflect.DeepEqual(tt.t1, tt.t2); tt.equal != equal { + t.Errorf("#%d: got %t, expected %t", x, equal, tt.equal) + } + } +} + +func generatePaths(count int) [][]string { + paths := [][]string{} + for c := 0; c < count; c++ { + p := []string{} + for d := 3; d < 16; d++ { + p = append(p, string(rune(c%d+65))) + } + paths = append(paths, p) + } + return paths +} + +func buildTreeRange(wg *sync.WaitGroup, t *Tree, paths [][]string, index, modulus int) { + if wg != nil { + defer wg.Done() + } + for i := index; i < len(paths); i += modulus { + t.Add(paths[i], strings.Join(paths[i], "/")) + } +} + +func TestParallelAdd(t *testing.T) { + trees := []*Tree{} + paths := generatePaths(10000) + wg := new(sync.WaitGroup) + r := runtime.GOMAXPROCS(8) + for _, tt := range []int{1, 2, 4, 8} { + atree := &Tree{} + trees = append(trees, atree) + for i := 0; i < tt; i++ { + wg.Add(1) + go buildTreeRange(wg, atree, paths, i, tt) + } + } + wg.Wait() + runtime.GOMAXPROCS(r) + for i := 1; i < len(trees); i++ { + if !reflect.DeepEqual(trees[0], trees[i]) { + t.Errorf("tree %d does not equal serially created tree.", i) + } + } +} + +func deleteTreeRange(wg *sync.WaitGroup, t *Tree, paths [][]string, index, modulus int) { + defer wg.Done() + for i := index; i < len(paths); i += modulus { + t.Delete(paths[i]) + } +} + +func TestParallelDelete(t *testing.T) { + trees := []*Tree{} + paths := generatePaths(100000) + wg := new(sync.WaitGroup) + r := runtime.GOMAXPROCS(8) + parallelSize := []int{2, 4, 8} + for _, tt := range parallelSize { + atree := &Tree{} + trees = append(trees, atree) + for i := 0; i < tt; i++ { + wg.Add(1) + go buildTreeRange(wg, atree, paths, i, tt) + } + } + wg.Wait() + for x, tt := range parallelSize { + atree := trees[x] + for i := 0; i < tt; i++ { + wg.Add(1) + go deleteTreeRange(wg, atree, paths, i, tt) + } + } + wg.Wait() + runtime.GOMAXPROCS(r) + emptyTree := &Tree{} + for i := 0; i < len(trees); i++ { + if !reflect.DeepEqual(trees[i], emptyTree) { + t.Errorf("tree %d does not equal empty tree. %#v != %#v", i, trees[i], emptyTree) + } + } +} + +func query(tr *Tree, path []string) (ret []interface{}) { + tr.Query(path, func(_ []string, _ *Leaf, val interface{}) error { + ret = append(ret, val) + return nil + }) + return ret +} + +func queryGetTreeRange(t *testing.T, wg *sync.WaitGroup, tr *Tree, paths [][]string, index, modulus int) { + defer wg.Done() + for i := index; i < len(paths); i += modulus { + // Test get of paths. + got := tr.GetLeafValue(paths[i]) + if want := strings.Join(paths[i], "/"); got != want { + t.Errorf("Get(%v): got result %s, want %s using %d threads", paths[i], got, want, modulus) + } + // Test query of whole paths. + results := query(tr, paths[i]) + if n := len(results); n != 1 { + t.Errorf("Query(%v): got %d results, want 1", paths[i], n) + } + for _, got := range results { + if want := strings.Join(paths[i], "/"); got != want { + t.Errorf("got result %s, want %s using %d threads", got, want, modulus) + } + } + // Test query of partial paths. + results = query(tr, paths[i][:3]) + if n := len(results); n < 1 { + t.Errorf("Query(%v): got %d results, want >= 1", paths[i], n) + } + for _, got := range results { + if want := strings.Join(paths[i][0:1], "/"); !strings.HasPrefix(got.(string), want) { + t.Errorf("got result %s, want to have prefix %s using %d threads", got, want, modulus) + } + } + } +} + +func TestParallelQueryGet(t *testing.T) { + paths := generatePaths(10000) + r := runtime.GOMAXPROCS(8) + atree := &Tree{} + buildTreeRange(nil, atree, paths, 0, 1) + wg := new(sync.WaitGroup) + for _, tt := range []int{2, 4, 8} { + for i := 0; i < tt; i++ { + wg.Add(1) + go queryGetTreeRange(t, wg, atree, paths, i, tt) + } + } + wg.Wait() + runtime.GOMAXPROCS(r) +} + +func makePath(i int64) []string { + path := []string{} + for depth := 0; depth < 5; depth++ { + path = append(path, fmt.Sprintf("%d", i&15)) + i >>= 4 + } + return path +} + +func makePathValue(i int64) ([]string, *string) { + value := fmt.Sprintf("value_%d", i) + return makePath(i), &value +} + +func BenchmarkTreeParallelAddNew(b *testing.B) { + t := &Tree{} + var x int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + t.Add(makePathValue(atomic.AddInt64(&x, 1))) + } + }) +} + +func BenchmarkTreeParallelAddUpdate(b *testing.B) { + t := &Tree{} + for i := 0; i < b.N; i++ { + t.Add(makePathValue(int64(i))) + } + // Only time the updates to already existing keys. + b.ResetTimer() + var x int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + t.Add(makePathValue(atomic.AddInt64(&x, 1))) + } + }) +} + +func BenchmarkTreeParallelGet(b *testing.B) { + t := &Tree{} + for i := 0; i < b.N; i++ { + t.Add(makePathValue(int64(i))) + } + // Only time the Get calls. + b.ResetTimer() + var x int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + t.GetLeafValue(makePath(atomic.AddInt64(&x, 1))) + } + }) +} + +func BenchmarkTreeParallelQuerySingle(b *testing.B) { + t := &Tree{} + for i := 1; i <= b.N; i++ { + t.Add(makePathValue(int64(i))) + } + var count int64 + // Create a query channel. + c := make(chan interface{}, 1000) + // Create parallel consumers. + var wg sync.WaitGroup + collect := func(c <-chan interface{}) { + for r := range c { + if r != nil { + atomic.AddInt64(&count, 1) + } + } + wg.Done() + } + for i := 0; i < runtime.GOMAXPROCS(0); i++ { + wg.Add(1) + go collect(c) + } + // Only time the Query calls. + b.ResetTimer() + var x int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + t.Query(makePath(atomic.AddInt64(&x, 1)), func(_ []string, _ *Leaf, val interface{}) error { c <- val; return nil }) + } + }) + close(c) + wg.Wait() + b.Logf("Query result count: %v", count) +} + +func BenchmarkTreeParallelQueryMany(b *testing.B) { + t := &Tree{} + for i := 1; i <= b.N; i++ { + t.Add(makePathValue(int64(i))) + } + var count int64 + // Create a query channel. + c := make(chan interface{}, 1000) + // Create parallel consumers. + var wg sync.WaitGroup + collect := func(c <-chan interface{}) { + for r := range c { + if r != nil { + atomic.AddInt64(&count, 1) + } + } + wg.Done() + } + for i := 0; i < runtime.GOMAXPROCS(0); i++ { + wg.Add(1) + go collect(c) + } + // Only time the Query calls. + b.ResetTimer() + var x int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + // For each query, use only a subpath to retrieve multiple elements. + t.Query(makePath(atomic.AddInt64(&x, 1))[0:3], func(_ []string, _ *Leaf, val interface{}) error { c <- val; return nil }) + } + }) + close(c) + wg.Wait() + b.Logf("Query result count: %v", count) +} + +func TestIsBranch(t *testing.T) { + tr := &Tree{} + buildTree(tr) + for _, test := range []struct { + node *Tree + want bool + }{ + { + node: tr, + want: true, + }, { + node: tr.Get(testPaths[0]), + want: false, + }, { + node: nil, + want: false, + }, + } { + got := test.node.IsBranch() + if got != test.want { + t.Errorf("IsBranch(%v) = %v, want %v", test.node, got, test.want) + } + } +} + +func TestGet(t *testing.T) { + tr := &Tree{} + testPaths = [][]string{ + []string{"a", "b", "c"}, + []string{"a", "d"}, + []string{"d"}, + } + buildTreePaths(tr, testPaths) + for _, test := range []struct { + path []string + want *Tree + }{ + { + path: []string{"a"}, + want: &Tree{leafBranch: branch{ + "b": &Tree{leafBranch: branch{ + "c": &Tree{leafBranch: "a/b/c"}}}, + "d": &Tree{leafBranch: "a/d"}}}, + }, { + path: []string{"a", "d"}, + want: &Tree{leafBranch: "a/d"}, + }, { + path: []string{}, + want: &Tree{leafBranch: branch{ + "a": &Tree{leafBranch: branch{ + "b": &Tree{leafBranch: branch{ + "c": &Tree{leafBranch: "a/b/c"}}}, + "d": &Tree{leafBranch: "a/d"}}}, + "d": &Tree{leafBranch: "d"}}}, + }, { + path: []string{"non existent path"}, + want: nil, + }, + } { + got := tr.Get(test.path) + if diff := pretty.Compare(got, test.want); diff != "" { + t.Errorf("Get(%v) returned diff (-got +want):\n%s", test.path, diff) + } + } +} + +func TestGetChildren(t *testing.T) { + tr := &Tree{} + testPaths = [][]string{ + []string{"a", "b", "c"}, + []string{"a", "d"}, + []string{"d"}, + } + buildTreePaths(tr, testPaths) + for _, test := range []struct { + node *Tree + want branch + }{ + { + node: tr, + want: branch{ + "a": &Tree{leafBranch: branch{ + "b": &Tree{leafBranch: branch{ + "c": &Tree{leafBranch: "a/b/c"}}}, + "d": &Tree{leafBranch: "a/d"}}}, + "d": &Tree{leafBranch: "d"}, + }, + }, { + node: tr.Get(testPaths[0]), // Leaf. + want: nil, + }, { + node: nil, + want: nil, + }, + } { + got := test.node.Children() + if diff := pretty.Compare(got, test.want); diff != "" { + t.Errorf("Children(%s) returned diff (-got +want):\n%s", test.node, diff) + } + } +} + +func TestTreeValue(t *testing.T) { + for _, test := range []struct { + node *Tree + want interface{} + }{ + { + node: &Tree{leafBranch: branch{}}, + want: nil, + }, { + node: &Tree{leafBranch: "val1"}, + want: "val1", + }, { + node: nil, + want: nil, + }, + } { + got := test.node.Value() + if got != test.want { + t.Errorf("Value(%v) = %v, want %v", test.node, got, test.want) + } + } +} + +func TestLeafValue(t *testing.T) { + for _, test := range []struct { + node *Leaf + want interface{} + }{ + { + node: &Leaf{leafBranch: "val1"}, + want: "val1", + }, { + node: nil, + want: nil, + }, + } { + got := test.node.Value() + if got != test.want { + t.Errorf("Value(%v) = %v, want %v", test.node, got, test.want) + } + } +} + +func TestString(t *testing.T) { + tr := &Tree{} + testPaths = [][]string{ + []string{"a", "b", "c"}, + []string{"a", "d"}, + []string{"d"}, + } + buildTreePaths(tr, testPaths) + for _, test := range []struct { + node *Tree + want string + }{ + { + node: tr, + want: `{ "a": { "b": { "c": "a/b/c" }, "d": "a/d" }, "d": "d" }`, + }, { + node: tr.Get([]string{"a"}), + want: `{ "b": { "c": "a/b/c" }, "d": "a/d" }`, + }, { + node: tr.Get([]string{"d"}), + want: `"d"`, + }, { + node: nil, + want: "", + }, + } { + // Test explicitly. + got := test.node.String() + if got != test.want { + t.Errorf("String\n\tgot: %q\n\twant: %q", got, test.want) + } + // Test via string format specifier. + got = fmt.Sprintf("%s", test.node) + if got != test.want { + t.Errorf("string format specifier\n\tgot: %q\n\twant: %q", got, test.want) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/errdiff/errdiff.go b/deps/github.com/openconfig/gnmi/errdiff/errdiff.go new file mode 100644 index 0000000000000000000000000000000000000000..b8709be13d3cb4fd39b9404ecaa4e3ce11a96ae2 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/errdiff/errdiff.go @@ -0,0 +1,146 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package errdiff makes it easy to compare Error by code, substring or exact +// match in tests. +// +// Similar in intended usage to messagediff.Diff and pretty.Compare, +// particularly in table-driven tests. +// +// Example usage: +// +// testCases := []struct { +// ... +// wantSubstring string +// }{ +// // Success +// {...}, +// // Failures +// {..., wantSubstring: "failed"}, +// {..., wantSubstring: "too many users"}, +// } +// for _, c := range testCases { +// got, err := fn(...) +// if diff := errdiff.Substring(err, c.wantSubstring); diff != "" { +// t.Errorf("fn() %v", diff) +// continue +// } +// ... +// } +// +// The generic function Check may be used in place of Code or +// Substring or when comparing against another error or for simple +// existance of an error: +// +// testCases := []struct { +// ... +// err interface{} +// }{ +// // Success +// {...}, +// // Failures +// {..., err: io.EOF}, // An explicit error +// {..., err: "my expected error string"}, // contains text +// {..., err: true}, // expect an error, don't care what +// } +// for _, c := range testCases { +// got, err := fn(...) +// if diff := errdiff.Check(err, c.err); diff != "" { +// t.Errorf("fn() %v", diff) +// continue +// } +// ... +package errdiff + +import ( + "fmt" + "strings" +) + +// Text returns a message describing the difference between the +// error text and the desired input. want="" indicates that no error is +// expected. +func Text(got error, want string) string { + if want == "" { + if got == nil { + return "" + } + return fmt.Sprintf("got err=%v, want err=nil", got) + } + if got == nil { + return fmt.Sprintf("got err=nil, want err with exact text %q", want) + } + if got.Error() != want { + return fmt.Sprintf("got err=%v, want err with exact text %q", got, want) + } + return "" +} + +// Substring returns a message describing the difference between the +// error text and the desired input. want="" indicates that no error is +// expected. +func Substring(got error, want string) string { + if want == "" { + if got == nil { + return "" + } + return fmt.Sprintf("got err=%v, want err=nil", got) + } + if got == nil { + return fmt.Sprintf("got err=nil, want err containing %q", want) + } + if !strings.Contains(got.Error(), want) { + return fmt.Sprintf("got err=%v, want err containing %q", got, want) + } + return "" +} + +// Check returns a message describing the difference between the error err and +// want. If want is a codes.Code, this function is the same as Code. +// If want is a string, this function is the same as Substring. If +// want is an error, this is essentially the same as ExactTextCompare(got, +// w.Error()). If want is a bool, err is simply tested for existance (want of +// true means an error is wanted). +func Check(got error, want interface{}) string { + switch w := want.(type) { + case nil: + if got == nil { + return "" + } + return fmt.Sprintf("got err=%v, want err=nil", got) + case bool: + switch { + case w && got == nil: + return "did not get expected error" + case !w && got != nil: + return fmt.Sprintf("got err=%v, want err=nil", got) + } + return "" + case string: + return Substring(got, w) + case error: + switch { + case got == nil: + return fmt.Sprintf("got err=nil, want err=%v", w) + case got.Error() == w.Error(): + return "" + default: + return fmt.Sprintf("got err=%v, want err=%v", got, w) + } + default: + return fmt.Sprintf("unsupported type in Check: %T", want) + } +} diff --git a/deps/github.com/openconfig/gnmi/errdiff/errdiff_test.go b/deps/github.com/openconfig/gnmi/errdiff/errdiff_test.go new file mode 100644 index 0000000000000000000000000000000000000000..061b004ab3bf6b0e5af54ac6480dff1471756060 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/errdiff/errdiff_test.go @@ -0,0 +1,156 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errdiff + +import ( + "errors" + "io" + "strings" + "testing" +) + +func TestText(t *testing.T) { + tests := []struct { + desc string + got error + want string + result string + }{ + {desc: "empty error"}, + {desc: "match", got: errors.New("abc"), want: "abc", result: ""}, + {desc: "message no match", got: errors.New("ab"), want: "abc", result: `got err=ab, want err with exact text "abc"`}, + {desc: "want nil", got: errors.New("ab"), result: `got err=ab, want err=nil`}, + {desc: "got nil want message", want: "abc", result: `got err=nil, want err with exact text "abc"`}, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := Text(tt.got, tt.want); got != tt.result { + t.Errorf("Text(%v, %v): got=%q want=%q", tt.got, tt.want, got, tt.result) + } + }) + } +} + +func TestSubstring(t *testing.T) { + tests := []struct { + desc string + got error + want string + result string + }{ + {desc: "empty"}, + {desc: "subsring match", got: errors.New("abc"), want: "bc", result: ""}, + {desc: "exact match", got: errors.New("abc"), want: "abc", result: ""}, + {desc: "message no match", got: errors.New("ab"), want: "abc", result: `got err=ab, want err containing "abc"`}, + {desc: "want nil", got: errors.New("ab"), result: `got err=ab, want err=nil`}, + {desc: "got nil want message", want: "abc", result: `got err=nil, want err containing "abc"`}, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := Substring(tt.got, tt.want); got != tt.result { + t.Errorf("Substring(%v, %v): got=%q want=%q", tt.got, tt.want, got, tt.result) + } + if got := Check(tt.got, tt.want); got != tt.result { + t.Errorf("Check(%v, %v): got=%q want=%q", tt.got, tt.want, got, tt.result) + } + }) + } +} + +type elist []string + +func (e elist) Error() string { return strings.Join(e, ", ") } + +func TestCheck(t *testing.T) { + elist1 := elist{"error1a", "error1b"} + elist2 := elist{"error2a", "error2b"} + + tests := []struct { + desc string + got error + want interface{} + result string + }{ + {desc: "empty"}, + { + desc: "exact same error", + got: io.EOF, + want: io.EOF, + }, { + desc: "different errors with same string", + want: errors.New("an error"), + got: errors.New("an error"), + }, { + desc: "nil want", + got: io.EOF, + result: `got err=EOF, want err=nil`, + }, { + desc: "nil got", + want: io.EOF, + result: `got err=nil, want err=EOF`, + }, { + desc: "different error", + want: errors.New("this error"), + got: errors.New("that error"), + result: `got err=that error, want err=this error`, + }, { + desc: "do not want error", + want: false, + }, { + desc: "do want error", + want: true, + result: `did not get expected error`, + }, { + desc: "got err=error, want err=none", + got: io.EOF, + want: false, + result: `got err=EOF, want err=nil`, + }, { + desc: "got error, want error", + got: io.EOF, + want: true, + }, { + desc: "unexpected errlist", + got: elist1, + result: `got err=error1a, error1b, want err=nil`, + }, { + desc: "missing errlist", + want: elist1, + result: `got err=nil, want err=error1a, error1b`, + }, { + desc: "correct errlist", + got: elist1, + want: elist1, + }, { + desc: "wrong errlist", + got: elist1, + want: elist2, + result: `got err=error1a, error1b, want err=error2a, error2b`, + }, { + desc: "unsupported", + want: 5, + result: `unsupported type in Check: int`, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := Check(tt.got, tt.want); got != tt.result { + t.Errorf("Check(%v, %v): got=%q want=%q", tt.got, tt.want, got, tt.result) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/errlist/errlist.go b/deps/github.com/openconfig/gnmi/errlist/errlist.go new file mode 100644 index 0000000000000000000000000000000000000000..9fda2a5a2d0dd6ecda806e89af95b9fbd11a3964 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/errlist/errlist.go @@ -0,0 +1,156 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package errlist implents an error type that contains a list of other errors. +// The provided Add function correctly handles nil so it may be unconditionally +// called with an error, even if the error is nil. +// +// Package errlist exports two types, List and Error. List is always used to +// construct a list of errors, and Error is used when introspecting an error. +// List does not implement the error interface to prevent an empty list from +// being returned. +// +// EXAMPLE +// +// func checkStrings(source []string) error { +// var errs errlist.List +// for _, s := range source { +// errs.Add(check(s)) +// } +// return errs.Err() +// } +// +// func check(s string) error { +// if len(s) < 1 || len(s) > 5 { +// return fmt.Errorf("bad string: %q", s) +// } +// return nil +// } +// +// +// func errHasPrefix(err error, prefix string) bool { +// switch errs := err.(type) { +// case errlist.Error: +// for _, err := range errs.Errors() { +// if errHasPrefix(err, prefix) { +// return true +// } +// } +// default: +// return strings.HasPrefix(err.Error(), prefix) +// } +// return false +// } +package errlist + +import ( + "reflect" + "strings" +) + +// Separator is used to separate error messages when calling Error on a list. +// Only package main should set Separator. It should only be set in an init +// function defined in the main package. +var Separator = ", " + +// An Error is a list of errors and implements the error interface. An Error +// should never be declared directly, use a List and then it's Err +// method to return a proper error. +type Error struct { + List +} + +// List is the working representation of an Error, it does not implement +// the error interface. Use the Add method to add errors to a List. +// +// Separator may optionally be set as the string to separate errors when +// displayed. If not set, it defaults to the global Separator value. +type List struct { + Separator string + errors []error +} + +// Errors is implemented by error types that can return lists of errors. +type Errors interface { + // Errors returns the list of errors associated with the recevier. It + // returns nil if there are no errors associated with the recevier. + Errors() []error +} + +// etype is used for detecting types that are simply slices of error. +var etype = reflect.TypeOf([]error{}) + +// Add adds all non-nil errs to the list of errors in e and returns true if errs +// contains a non-nil error. If no non-nil errors are passed Add does nothing +// and returns false. Add will never add a nil error to the List. If err +// implementes the Errors interface or its underlying type is a slice of errors +// then e.Add is called on each individual error. +func (e *List) Add(errs ...error) bool { + added := false + for _, err := range errs { + if err != nil { + if el, ok := err.(Errors); ok { + errs := el.Errors() + if len(errs) > 0 { + e.errors = append(e.errors, errs...) + added = true + } + continue + } + if rv := reflect.ValueOf(err); rv.Type().AssignableTo(etype) { + a := false + n := rv.Len() + for i := 0; i < n; i++ { + errI, ok := rv.Index(i).Interface().(error) + if ok { + a = e.Add(errI) || a + } + } + added = added || a + continue + } + e.errors = append(e.errors, err) + added = true + } + } + return added +} + +// Err returns e as an error of type Error if e has errors, or nil. +func (e List) Err() error { + if len(e.errors) == 0 { + return nil + } + return Error{e} +} + +// Error implements the error interface. +func (e Error) Error() string { + sep := e.Separator + if sep == "" { + sep = Separator + } + msgs := make([]string, len(e.errors)) + for x, err := range e.errors { + msgs[x] = err.Error() + } + return strings.Join(msgs, sep) +} + +// Errors returns the list of errors in e. +func (e Error) Errors() []error { + return e.errors +} diff --git a/deps/github.com/openconfig/gnmi/errlist/errlist_test.go b/deps/github.com/openconfig/gnmi/errlist/errlist_test.go new file mode 100644 index 0000000000000000000000000000000000000000..91370f332154d6f26c9810b62c6dfbec364cfb6f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/errlist/errlist_test.go @@ -0,0 +1,159 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errlist_test + +// Use package errlist_test rather than errlist to prevent the tests from having +// any internal access to the actual List or Error types. + +import ( + "errors" + "strings" + "testing" + + "github.com/openconfig/gnmi/errlist" +) + +func TestNil(t *testing.T) { + var err errlist.List + + func(e error) { + if e != nil { + t.Error("did not get expected nil") + } + }(err.Err()) +} + +func TestInteface(t *testing.T) { + var err errlist.List + var i interface{} + i = err + + if _, ok := i.(error); ok { + t.Error("List implements error, it should not") + } + err.Add(errors.New("some error")) + i = err.Err() + if _, ok := i.(error); !ok { + t.Error("List.Err does not implement error, it should") + } +} + +func errsEqual(a, b []error) bool { + if len(a) != len(b) { + return false + } + for x, s := range a { + if b[x] != s { + return false + } + } + return true +} + +func TestAdd(t *testing.T) { + var err errlist.List + errs := []error{ + errors.New("error 1"), + errors.New("error 2"), + errors.New("error 3"), + } + + check := func(i int, err error) { + switch { + case err == nil && i == 0: + case err == nil: + t.Errorf("#%d: got nil, expected errors", i) + case i == 0: + t.Errorf("#%d: got unexpected errors: %v", i, err) + default: + e := err.(errlist.Error).Errors() + if !errsEqual(errs[:i], e) { + t.Errorf("#%d: got %v, want %v", i, e, errs[:i]) + } + } + } + err.Add(nil) // should be a no-op + check(0, err.Err()) + for i, e := range errs { + err.Add(e) + err.Add(nil) // should be a no-op + check(i+1, err.Err()) + } +} + +// elist implements the error interface. +type elist []error + +func (e elist) Err() error { return e } +func (e elist) Error() string { + var m []string + for _, err := range e { + m = append(m, err.Error()) + } + // We use :: to join to be different from what errlist.Error will + // use to join. + return strings.Join(m, "::") +} + +func TestAddList(t *testing.T) { + var err, err1 errlist.List + err.Add(err1.Err()) + if e := err.Err(); e != nil { + t.Fatalf("got error %v, want nil", e) + } + err1.Add(errors.New("error1")) + err1.Add(errors.New("error2")) + err.Add(err1.Err()) + + er := err.Err() + + switch e := er.(type) { + case errlist.Error: + if n := len(e.Errors()); n != 2 { + t.Fatalf("got %d errors, want 2", n) + } + default: + t.Fatalf("got error type %T, want errlist.Error", er) + } + + var errNil error + el := elist{nil, errors.New("error3"), errors.New("error4"), errNil} + err.Add(el.Err()) + + if got, want := err.Err().Error(), "error1, error2, error3, error4"; got != want { + t.Fatalf("got error %q, want %q", got, want) + } +} + +func TestSep(t *testing.T) { + var list errlist.List + defer func(s string) { errlist.Separator = s }(errlist.Separator) + errlist.Separator = ":" + + list.Add(errors.New("one")) + list.Add(errors.New("two")) + err := list.Err() + if got, want := err.Error(), "one:two"; got != want { + t.Errorf("got %q, want %q", got, want) + } + + list.Separator = "-" + err = list.Err() + if got, want := err.Error(), "one-two"; got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/deps/github.com/openconfig/gnmi/gnmi_deps.bzl b/deps/github.com/openconfig/gnmi/gnmi_deps.bzl new file mode 100644 index 0000000000000000000000000000000000000000..b662a55aee09b3f02c9cd840472064e6b4dbe9f2 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/gnmi_deps.bzl @@ -0,0 +1,51 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Dependencies to build gnmi.""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def gnmi_deps(): + """Declare the third-party dependencies necessary to build gnmi""" + if not native.existing_rule("com_github_grpc_grpc"): + http_archive( + name = "com_github_grpc_grpc", + url = "https://github.com/grpc/grpc/archive/refs/tags/v1.38.0.tar.gz", + strip_prefix = "grpc-1.38.0", + sha256 = "abd9e52c69000f2c051761cfa1f12d52d8b7647b6c66828a91d462e796f2aede", + ) + if not native.existing_rule("com_google_protobuf"): + http_archive( + name = "com_google_protobuf", + url = "https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protobuf-all-3.17.1.tar.gz", + strip_prefix = "protobuf-3.17.1", + sha256 = "7dd46c0fef046c056adc7a1bf7dfd063f19cbcb206133441ca315ab73572d8a8", + ) + if not native.existing_rule("com_google_googleapis"): + http_archive( + name = "com_google_googleapis", + url = "https://github.com/googleapis/googleapis/archive/f405c718d60484124808adb7fb5963974d654bb4.zip", + strip_prefix = "googleapis-f405c718d60484124808adb7fb5963974d654bb4", + sha256 = "406b64643eede84ce3e0821a1d01f66eaf6254e79cb9c4f53be9054551935e79", + ) + if not native.existing_rule("rules_proto"): + http_archive( + name = "rules_proto", + sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208", + strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", + "https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", + ], + ) diff --git a/deps/github.com/openconfig/gnmi/go.mod b/deps/github.com/openconfig/gnmi/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..123f2a88ccc0fe2d21cd627f395f16ba1ca151c8 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/go.mod @@ -0,0 +1,21 @@ +module github.com/openconfig/gnmi + +go 1.12 + +require ( + github.com/cenkalti/backoff/v4 v4.1.0 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/protobuf v1.4.3 + github.com/google/go-cmp v0.5.0 + github.com/kylelemons/godebug v1.1.0 + github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea // indirect + github.com/openconfig/grpctunnel v0.0.0-20210610163803-fde4a9dc048d + github.com/openconfig/ygot v0.6.0 + golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 + golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 + golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e // indirect + golang.org/x/text v0.3.4 // indirect + google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d // indirect + google.golang.org/grpc v1.34.0 + google.golang.org/protobuf v1.25.0 +) diff --git a/deps/github.com/openconfig/gnmi/go.sum b/deps/github.com/openconfig/gnmi/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..0f2862d2e8fb125df2793702f184962303670ac6 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/go.sum @@ -0,0 +1,111 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= +github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea h1:5MyIz4bN4vpH6aHDN339bkWXAjTkhg1ZKMhR4aIi5Rk= +github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea/go.mod h1:dhXaV0JgHJzdrHi2l+w0fZrwArtXL7jEFoiqLEdmkvU= +github.com/openconfig/grpctunnel v0.0.0-20210610163803-fde4a9dc048d h1:zrs4U92QEAadFotQyidT4U8iZDJO67pXsS4r64uAHik= +github.com/openconfig/grpctunnel v0.0.0-20210610163803-fde4a9dc048d/go.mod h1:x9tAZ4EwqCQ0jI8D6S8Yhw9Z0ee7/BxWQX0k0Uib5Q8= +github.com/openconfig/ygot v0.6.0 h1:kJJFPBrczC6TDnz/HMlFTJEdW2CuyUftV13XveIukg0= +github.com/openconfig/ygot v0.6.0/go.mod h1:o30svNf7O0xK+R35tlx95odkDmZWS9JyWWQSmIhqwAs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d h1:HV9Z9qMhQEsdlvxNFELgQ11RkMzO3CMkjEySjCtuLes= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/deps/github.com/openconfig/gnmi/latency/latency.go b/deps/github.com/openconfig/gnmi/latency/latency.go new file mode 100644 index 0000000000000000000000000000000000000000..298a3849bc52b55b8e8c83d7887077f7fffd4dc1 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/latency/latency.go @@ -0,0 +1,352 @@ +/* +Copyright 2021 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package latency supports exporting latency stats (avg/max/min) of a set +// of time windows as metadata. +package latency + +import ( + "fmt" + "sync" + "time" + + "github.com/openconfig/gnmi/metadata" +) + +const ( + // ElemLatency is the container of all latency metadata about a target. + ElemLatency = "latency" + // ElemWindow contains latency metadatas (avg, max, min) of a particular + // window size. + ElemWindow = "window" + // ElemAvg is the average latency during a time window. + ElemAvg = "avg" + // ElemMax is the maximum latency during a time window. + ElemMax = "max" + // ElemMin is the minimum latency during a time window. + ElemMin = "min" + metaName = "LatencyWindow" +) + +var now = time.Now + +// StatType is the type of latency statistics supported for a time window. +type StatType int + +const ( + // Avg is the average latency of a time window. + Avg = StatType(iota) + // Max is the maximum latency of a time window. + Max + // Min is the minimum latency of a time window. + Min +) + +// CompactDurationString returns a compact string for a time window d. It +// removes unnecessary suffixes like "0m0s" and "0s" from the Golang +// fmt.Sprint generated string of a time.Duration. +func CompactDurationString(d time.Duration) string { + s := fmt.Sprint(d) + switch n := len(s); { + case n >= 6 && s[n-5:] == "h0m0s": + return s[:n-4] + case n >= 4 && s[n-3:] == "m0s": + return s[:n-2] + } + return s +} + +// String returns the string representation of the StatType. +func (st StatType) String() string { + switch st { + case Avg: + return ElemAvg + case Max: + return ElemMax + case Min: + return ElemMin + } + return "unknown" +} + +// stat represents a latency statistic of a time window. +type stat struct { + window time.Duration // Window size. + typ StatType // Type of the latency stats. +} + +// metaName returns the metadata name of the stat s. +func (s stat) metaName() string { + return fmt.Sprintf("%s%s%s", s.typ, metaName, CompactDurationString(s.window)) +} + +// metaPath returns the metadata path corresponding to the Stat s. +func (s stat) metaPath() []string { + return []string{metadata.Root, ElemLatency, ElemWindow, CompactDurationString(s.window), s.typ.String()} +} + +// Path returns the metadata path for the latency statistics of window w +// and type typ. +func Path(w time.Duration, typ StatType) []string { + return stat{window: w, typ: typ}.metaPath() +} + +// MetadataName returns the metadata name for the latency statistics +// of window w and type typ. +func MetadataName(w time.Duration, typ StatType) string { + return stat{window: w, typ: typ}.metaName() +} + +type slot struct { + total time.Duration // cumulative latency of this time slot + max time.Duration // maximum latency of this time slot + min time.Duration // minimum latency of this time slot + count int64 // number of updates + start time.Time // the start time of the time slot + end time.Time // the end time of the time slot +} + +type window struct { + stats map[string]func(string, *metadata.Metadata) + size time.Duration // window size + total time.Duration // cumulative latency of this time window + sf int64 // scaling factor of total + count int64 // number of updates + slots []*slot // time slots of latencies + covered bool // have received latencies covering a full window +} + +func newWindow(size time.Duration, sf int64) *window { + w := &window{ + stats: map[string]func(string, *metadata.Metadata){}, + size: size, + sf: sf, + } + for st, f := range map[StatType]func(string, *metadata.Metadata){ + Avg: w.setAvg, + Max: w.setMax, + Min: w.setMin} { + stat := stat{window: size, typ: st} + w.stats[stat.metaName()] = f + } + return w +} + +func (w *window) add(ls *slot) { + if ls == nil || ls.count == 0 { + return + } + w.total = w.total + ls.total + w.count = w.count + ls.count + w.slots = append(w.slots, ls) +} + +func (w *window) setAvg(name string, m *metadata.Metadata) { + if w.count == 0 { + return + } + avg := w.total / time.Duration(w.count) + if n := avg.Nanoseconds(); n != 0 { + m.SetInt(name, n*w.sf) + } +} + +func (w *window) setMax(name string, m *metadata.Metadata) { + var max time.Duration + for _, slot := range w.slots { + if slot.max > max { + max = slot.max + } + } + if n := max.Nanoseconds(); n != 0 { + m.SetInt(name, n) + } +} + +func (w *window) setMin(name string, m *metadata.Metadata) { + if len(w.slots) == 0 { + return + } + min := w.slots[0].min + for _, slot := range w.slots[1:] { + if slot.min < min { + min = slot.min + } + } + if n := min.Nanoseconds(); n != 0 { + m.SetInt(name, n) + } +} + +func (w *window) slide(ts time.Time) { + cutoff := ts.Add(-w.size) + start := 0 + for _, s := range w.slots { + if !s.end.After(cutoff) { + w.count = w.count - s.count + w.total = w.total - s.total + start++ + } + } + w.slots = w.slots[start:] +} + +func (w *window) isCovered(ts time.Time) bool { + if w.covered { + return true + } + if len(w.slots) == 0 { // no updates received + return false + } + if ts.Sub(w.slots[0].start) >= w.size { + w.covered = true + return true + } + return false +} + +func (w *window) updateMeta(m *metadata.Metadata, ts time.Time) { + if !w.isCovered(ts) { + return + } + w.slide(ts) + for name, f := range w.stats { + f(name, m) + } +} + +// Latency supports calculating and exporting latency stats for a specified +// set of time windows. +type Latency struct { + mu sync.Mutex + start time.Time // start time of the current batch of cumulated latency stats + scaleFactor time.Duration // scaling factor of totalDiff + totalDiff time.Duration // cumulative difference in timestamps from device + count int64 // number of updates in latency count + min time.Duration // minimum latency + max time.Duration // maximum latency + windows []*window +} + +// Options contains the options for creating a Latency. +type Options struct { + // Precision for the avg stats. If unspecified, the precision is nanoseconds. + // The exported latency stats are always in nanoseconds no matter what + // precision is set here. Setting precision at a more coarse time duration + // than nanosecond is to avoid overflowing of int64 for the accumulated time + // durations needed to calculate averages. The precision of the Max and Min + // stats are not affected by this setting. + AvgPrecision time.Duration +} + +// New returns a Latency object supporting latency stats for time windows +// specified in windowSizes. +func New(windowSizes []time.Duration, opts *Options) *Latency { + precision := time.Nanosecond + if opts != nil && opts.AvgPrecision.Nanoseconds() != 0 { + precision = opts.AvgPrecision + } + sf := precision / time.Nanosecond + var windows []*window + for _, size := range windowSizes { + windows = append(windows, newWindow(size, sf.Nanoseconds())) + } + return &Latency{windows: windows, scaleFactor: sf} +} + +// Compute calculates the time difference between now and ts (the timestamp +// of an update) and updates the latency stats saved in Latency. +func (l *Latency) Compute(ts time.Time) { + l.mu.Lock() + defer l.mu.Unlock() + nowTime := now() + lat := nowTime.Sub(ts) + l.totalDiff += lat / l.scaleFactor + l.count++ + if lat > l.max { + l.max = lat + } + if lat < l.min || l.min == 0 { + l.min = lat + } + if l.start.IsZero() { + l.start = nowTime + } +} + +// UpdateReset use the latencies saved during the last interval to update +// the latency stats of all the supported time windows. And then it updates +// the corresponding stats in Metadata m. +// UpdateReset is expected to be called periodically at a fixed interval +// (e.g. 2s) of which the time windows should be multiples of this interval. +func (l *Latency) UpdateReset(m *metadata.Metadata) { + l.mu.Lock() + defer l.mu.Unlock() + ts := now() + defer func() { + for _, window := range l.windows { + window.updateMeta(m, ts) + } + l.start = ts + }() + if l.count == 0 { + return + } + s := &slot{ + total: l.totalDiff, + count: l.count, + max: l.max, + min: l.min, + start: l.start, + end: ts, + } + for _, window := range l.windows { + window.add(s) + } + l.totalDiff = 0 + l.count = 0 + l.min = 0 + l.max = 0 +} + +// RegisterMetadata registers latency stats metadata for time windows +// specified in windowSizes. RegisterMetadata is not thread-safe and +// should be called before any metadata.Metadata is instantiated. +func RegisterMetadata(windowSizes []time.Duration) { + for _, size := range windowSizes { + for _, typ := range []StatType{Avg, Max, Min} { + st := stat{window: size, typ: typ} + metadata.RegisterIntValue(st.metaName(), &metadata.IntValue{Path: st.metaPath()}) + } + } +} + +// ParseWindows parses the time durations of latency windows and verify they +// are multiples of the metadata update period. +func ParseWindows(tds []string, metaUpdatePeriod time.Duration) ([]time.Duration, error) { + var durs []time.Duration + for _, td := range tds { + dur, err := time.ParseDuration(td) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", td, err) + } + if dur.Nanoseconds()%metaUpdatePeriod.Nanoseconds() != 0 { + return nil, fmt.Errorf("latency stats window %s is not a multiple of metadata update period %v", td, metaUpdatePeriod) + } + durs = append(durs, dur) + } + return durs, nil +} diff --git a/deps/github.com/openconfig/gnmi/latency/latency_test.go b/deps/github.com/openconfig/gnmi/latency/latency_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f1655032a4c7c7c5ea1cc11bd2621a24d9c43e00 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/latency/latency_test.go @@ -0,0 +1,320 @@ +package latency + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/openconfig/gnmi/errdiff" + "github.com/openconfig/gnmi/metadata" +) + +func TestLatencyWithoutWindows(t *testing.T) { + defer func() { + now = time.Now + }() + var windows []time.Duration + RegisterMetadata(windows) + lat := New(windows, nil) + m := metadata.New() + compute := func(ts, nts time.Time) { + now = func() time.Time { return nts } + lat.Compute(ts) + } + updateReset := func(nts time.Time) { + now = func() time.Time { return nts } + lat.UpdateReset(m) + } + // Make sure it is still ok to call Compute and UpdateReset functions + // even if no latency windows are set. + compute(time.Unix(97, 0), time.Unix(98, 0)) // 1 second + compute(time.Unix(96, 0), time.Unix(99, 0)) // 3 second + updateReset(time.Unix(100, 0)) + compute(time.Unix(96, 0), time.Unix(101, 0)) // 5 second + compute(time.Unix(94, 0), time.Unix(101, 0)) // 7 second + updateReset(time.Unix(102, 0)) +} + +func TestAvgLatency(t *testing.T) { + defer func() { + now = time.Now + }() + win := 2 * time.Second + windows := []time.Duration{win} + RegisterMetadata(windows) + lat := New(windows, &Options{AvgPrecision: time.Microsecond}) + m := metadata.New() + compute := func(ts, nts time.Time) { + now = func() time.Time { return nts } + lat.Compute(ts) + } + updateReset := func(nts time.Time) { + now = func() time.Time { return nts } + lat.UpdateReset(m) + } + compute(time.Unix(96, 999398800), time.Unix(98, 0)) // 1 second 601200 ns + compute(time.Unix(96, 0), time.Unix(99, 803400)) // 3 second 803400 ns + updateReset(time.Unix(100, 0)) + for name, want := range map[string]int64{ + MetadataName(win, Avg): 2000702000, + MetadataName(win, Max): 3000803400, + MetadataName(win, Min): 1000601200, + } { + val, err := m.GetInt(name) + if err != nil { + t.Fatalf("metadata %q: got unexpected error %v", name, err) + } + if val != want { + t.Errorf("metadata %q: got %d, want %d", name, val, want) + } + } +} + +func TestLatency(t *testing.T) { + defer func() { + now = time.Now + }() + smWin, mdWin, lgWin := 2*time.Second, 4*time.Second, 8*time.Second + windows := []time.Duration{smWin, mdWin, lgWin} + RegisterMetadata(windows) + meta := func(w time.Duration, typ StatType) string { + return stat{window: w, typ: typ}.metaName() + } + var latStats []string + for _, w := range windows { + for _, typ := range []StatType{Avg, Max, Min} { + latStats = append(latStats, meta(w, typ)) + } + } + tests := []struct { + desc string + opts *Options + }{{ + desc: "default nanosecond", + opts: nil, + }, { + desc: "microsecond", + opts: &Options{AvgPrecision: time.Microsecond}, + }, { + desc: "millisecond", + opts: &Options{AvgPrecision: time.Millisecond}, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + lat := New(windows, tt.opts) + m := metadata.New() + checkLatency := func(desc string, lm map[string]time.Duration) { + for name, want := range lm { + val, err := m.GetInt(name) + if err != nil { + t.Fatalf("%s: metadata %q: got unexpected error %v", desc, name, err) + } + if val != want.Nanoseconds() { + t.Fatalf("%s: metadata %q: got %d, want %d", desc, name, val, want.Nanoseconds()) + } + } + for _, name := range latStats { + if _, ok := lm[name]; ok { + continue + } + if _, err := m.GetInt(name); err == nil { + t.Fatalf("%s: metadata %q: didn't get expected error", desc, name) + } + } + } + checkLatency("initial state", nil) + + compute := func(ts, nts time.Time) { + now = func() time.Time { return nts } + lat.Compute(ts) + } + updateReset := func(nts time.Time) { + now = func() time.Time { return nts } + lat.UpdateReset(m) + } + + compute(time.Unix(97, 0), time.Unix(98, 0)) // 1 second + compute(time.Unix(96, 0), time.Unix(99, 0)) // 3 second + updateReset(time.Unix(100, 0)) + checkLatency("after interval 1", map[string]time.Duration{ + meta(smWin, Avg): 2 * time.Second, + meta(smWin, Max): 3 * time.Second, + meta(smWin, Min): 1 * time.Second}) + + compute(time.Unix(96, 0), time.Unix(101, 0)) // 5 second + compute(time.Unix(94, 0), time.Unix(101, 0)) // 7 second + updateReset(time.Unix(102, 0)) + checkLatency("after interval 2", map[string]time.Duration{ + meta(smWin, Avg): 6 * time.Second, + meta(smWin, Max): 7 * time.Second, + meta(smWin, Min): 5 * time.Second, + meta(mdWin, Avg): 4 * time.Second, + meta(mdWin, Max): 7 * time.Second, + meta(mdWin, Min): 1 * time.Second}) + + compute(time.Unix(98, 1000), time.Unix(103, 1000)) // 5 second + compute(time.Unix(100, 2000), time.Unix(103, 2000)) // 3 second + updateReset(time.Unix(104, 0)) + checkLatency("after interval 3", map[string]time.Duration{ + meta(smWin, Avg): 4 * time.Second, + meta(smWin, Max): 5 * time.Second, + meta(smWin, Min): 3 * time.Second, + meta(mdWin, Avg): 5 * time.Second, + meta(mdWin, Max): 7 * time.Second, + meta(mdWin, Min): 3 * time.Second}) + + compute(time.Unix(101, 0), time.Unix(105, 0)) // 4 second + updateReset(time.Unix(106, 0)) + checkLatency("after interval 4", map[string]time.Duration{ + meta(smWin, Avg): 4 * time.Second, + meta(smWin, Max): 4 * time.Second, + meta(smWin, Min): 4 * time.Second, + meta(mdWin, Avg): 4 * time.Second, + meta(mdWin, Max): 5 * time.Second, + meta(mdWin, Min): 3 * time.Second, + meta(lgWin, Avg): 4 * time.Second, + meta(lgWin, Max): 7 * time.Second, + meta(lgWin, Min): 1 * time.Second}) + + compute(time.Unix(104, 1000), time.Unix(107, 1000)) // 3 second + compute(time.Unix(105, 2000), time.Unix(107, 2000)) // 2 second + compute(time.Unix(106, 3000), time.Unix(107, 3000)) // 1 second + updateReset(time.Unix(108, 0)) + checkLatency("after interval 5", map[string]time.Duration{ + meta(smWin, Avg): 2 * time.Second, + meta(smWin, Max): 3 * time.Second, + meta(smWin, Min): 1 * time.Second, + meta(mdWin, Avg): 2500 * time.Millisecond, + meta(mdWin, Max): 4 * time.Second, + meta(mdWin, Min): 1 * time.Second, + meta(lgWin, Avg): 3750 * time.Millisecond, + meta(lgWin, Max): 7 * time.Second, + meta(lgWin, Min): 1 * time.Second}) + + updateReset(time.Unix(110, 0)) + checkLatency("after interval 6", map[string]time.Duration{ + meta(smWin, Avg): 2 * time.Second, + meta(smWin, Max): 3 * time.Second, + meta(smWin, Min): 1 * time.Second, + meta(mdWin, Avg): 2 * time.Second, + meta(mdWin, Max): 3 * time.Second, + meta(mdWin, Min): 1 * time.Second, + meta(lgWin, Avg): 3 * time.Second, + meta(lgWin, Max): 5 * time.Second, + meta(lgWin, Min): 1 * time.Second}) + + updateReset(time.Unix(112, 0)) + checkLatency("after interval 7", map[string]time.Duration{ + meta(smWin, Avg): 2 * time.Second, + meta(smWin, Max): 3 * time.Second, + meta(smWin, Min): 1 * time.Second, + meta(mdWin, Avg): 2 * time.Second, + meta(mdWin, Max): 3 * time.Second, + meta(mdWin, Min): 1 * time.Second, + meta(lgWin, Avg): 2500 * time.Millisecond, + meta(lgWin, Max): 4 * time.Second, + meta(lgWin, Min): 1 * time.Second}) + + compute(time.Unix(110, 0), time.Unix(113, 0)) // 3 second + compute(time.Unix(108, 0), time.Unix(113, 0)) // 5 second + updateReset(time.Unix(114, 0)) + checkLatency("after interval 8", map[string]time.Duration{ + meta(smWin, Avg): 4 * time.Second, + meta(smWin, Max): 5 * time.Second, + meta(smWin, Min): 3 * time.Second, + meta(mdWin, Avg): 4 * time.Second, + meta(mdWin, Max): 5 * time.Second, + meta(mdWin, Min): 3 * time.Second, + meta(lgWin, Avg): 2800 * time.Millisecond, + meta(lgWin, Max): 5 * time.Second, + meta(lgWin, Min): 1 * time.Second}) + }) + } +} + +func TestParseWindows(t *testing.T) { + tests := []struct { + desc string + windows []string + period time.Duration + want []time.Duration + err interface{} + }{{ + desc: "wrong time Duration", + windows: []string{"abc"}, + period: 2 * time.Second, + err: true, + }, { + desc: "window is not a multiple of update period", + windows: []string{"2s", "5s"}, + period: 2 * time.Second, + err: "not a multiple of metadata update period", + }, { + desc: "success", + windows: []string{"2s", "30s", "5m", "3h"}, + period: 2 * time.Second, + want: []time.Duration{2 * time.Second, 30 * time.Second, 5 * time.Minute, 3 * time.Hour}, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got, err := ParseWindows(tt.windows, tt.period) + if diff := errdiff.Check(err, tt.err); diff != "" { + t.Fatalf("ParseWindows(%v) got error diff: %v", tt.windows, diff) + } + if err != nil { + return + } + if diff := cmp.Diff(got, tt.want, nil); diff != "" { + t.Errorf("ParseWindows(%v): got %v, want %v\ndiff: %s", tt.windows, got, tt.want, diff) + } + }) + } +} + +func TestCompactDurationString(t *testing.T) { + tests := []struct { + desc string + in string + out string + }{{ + desc: "remove ending 0m0s from 24h0m0s", + in: "24h", + out: "24h", + }, { + desc: "remove ending 0m0s from 1h0m0s", + in: "1h", + out: "1h", + }, { + desc: "remove ending 0s from 10m0s", + in: "10m", + out: "10m", + }, { + desc: "remove ending 0s from 1h10m0s", + in: "1h10m", + out: "1h10m", + }, { + desc: "normal duration with hour, minute and second", + in: "1h10m30s", + out: "1h10m30s", + }, { + desc: "normal duration with minute and second", + in: "10m30s", + out: "10m30s", + }, { + desc: "normal duration with seconds", + in: "30s", + out: "30s", + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + d, err := time.ParseDuration(tt.in) + if err != nil { + t.Fatalf("error parsing input duration %s: %v", tt.in, err) + } + s := CompactDurationString(d) + if s != tt.out { + t.Errorf("durationString(%s): got %q, want %q", tt.in, s, tt.out) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/manager/manager.go b/deps/github.com/openconfig/gnmi/manager/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..0a6504735d03c85acac6a58ba85cca058b0d75ba --- /dev/null +++ b/deps/github.com/openconfig/gnmi/manager/manager.go @@ -0,0 +1,397 @@ +/* +Copyright 2020 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package manager provides functions to start or stop monitoring of a target. +package manager + +import ( + "context" + "errors" + "fmt" + "strings" + "sync" + "time" + + log "github.com/golang/glog" + "github.com/cenkalti/backoff/v4" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "github.com/golang/protobuf/proto" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + tpb "github.com/openconfig/gnmi/proto/target" +) + +// AddrSeparator delimits the chain of addresses used to connect to a target. +const AddrSeparator = ";" + +var ( + // ErrPending indicates a pending subscription attempt for the target exists. + ErrPending = errors.New("subscription already pending") + + // RetryBaseDelay is the initial retry interval for target connections. + RetryBaseDelay = time.Second + // RetryMaxDelay caps the retry interval for target connections. + RetryMaxDelay = time.Minute + // RetryRandomization is the randomization factor applied to the retry + // interval. + RetryRandomization = 0.5 +) + +// CredentialsClient is an interface for credentials lookup. +type CredentialsClient interface { + Lookup(ctx context.Context, key string) (string, error) +} + +// Config is used by Manager to handle updates. +type Config struct { + // Connect will be invoked when a target connects. + Connect func(string) + // Credentials is an optional client used to lookup passwords for targets + // with a specified username and password ID. + Credentials CredentialsClient + // Reset will be invoked when a target disconnects. + Reset func(string) + // Sync will be invoked in response to gNMI sync updates. + Sync func(string) + // Timeout defines the optional duration to wait for a gRPC dial. + Timeout time.Duration + // Update will be invoked in response to gNMI updates. + Update func(*gpb.Notification) + // ConnectionManager is used to create gRPC connections. + ConnectionManager ConnectionManager + // ConnectError record error from subcribe connections. + ConnectError func(string, error) +} + +type target struct { + name string + t *tpb.Target + sr *gpb.SubscribeRequest + cancel func() + finished chan struct{} + mu sync.Mutex + reconnect func() +} + +// Manager provides functionality for making gNMI subscriptions to targets and +// handling updates. +type Manager struct { + backoff *backoff.ExponentialBackOff + connect func(string) + connectError func(string, error) + connectionManager ConnectionManager + cred CredentialsClient + reset func(string) + sync func(string) + testSync func() // exposed for test synchronization + timeout time.Duration + update func(*gpb.Notification) + + mu sync.Mutex + targets map[string]*target +} + +// ConnectionManager defines a function to create a *grpc.ClientConn along with +// a done function that will be called when the returned connection handle is +// unused. +type ConnectionManager interface { + Connection(ctx context.Context, addr string) (conn *grpc.ClientConn, done func(), err error) +} + +// NewManager returns a new target Manager. +func NewManager(cfg Config) (*Manager, error) { + if cfg.ConnectionManager == nil { + return nil, errors.New("nil Config.ConnectionManager supplied") + } + e := backoff.NewExponentialBackOff() + e.MaxElapsedTime = 0 // Retry target connections indefinitely. + e.InitialInterval = RetryBaseDelay + e.MaxInterval = RetryMaxDelay + e.RandomizationFactor = RetryRandomization + e.Reset() + return &Manager{ + backoff: e, + connect: cfg.Connect, + connectError: cfg.ConnectError, + connectionManager: cfg.ConnectionManager, + cred: cfg.Credentials, + reset: cfg.Reset, + sync: cfg.Sync, + targets: make(map[string]*target), + testSync: func() {}, + timeout: cfg.Timeout, + update: cfg.Update, + }, nil +} + +func (m *Manager) handleGNMIUpdate(name string, resp *gpb.SubscribeResponse) error { + if log.V(2) { + log.Info(resp) + } + if resp.Response == nil { + return fmt.Errorf("nil Response") + } + switch v := resp.Response.(type) { + case *gpb.SubscribeResponse_Update: + if m.update != nil { + m.update(v.Update) + } + case *gpb.SubscribeResponse_SyncResponse: + if m.sync != nil { + m.sync(name) + } + case *gpb.SubscribeResponse_Error: + return fmt.Errorf("received error response: %s", v) + default: + return fmt.Errorf("received unknown response %T: %s", v, v) + } + return nil +} + +func addrChains(addrs []string) [][]string { + ac := make([][]string, len(addrs)) + for idx, addrLine := range addrs { + ac[idx] = strings.Split(addrLine, AddrSeparator) + } + return ac +} + +func (m *Manager) createConn(ctx context.Context, name string, t *tpb.Target) (conn *grpc.ClientConn, done func(), err error) { + nhs := addrChains(t.GetAddresses()) + if len(nhs) == 0 { + return nil, func() {}, errors.New("target has no addresses for next hop connection") + } + // A single next-hop dial is assumed. + nh := nhs[0][0] + select { + case <-ctx.Done(): + return nil, func() {}, ctx.Err() + default: + connCtx := ctx + if m.timeout > 0 { + c, cancel := context.WithTimeout(ctx, m.timeout) + connCtx = c + defer cancel() + } + return m.connectionManager.Connection(connCtx, nh) + } +} + +func (m *Manager) handleUpdates(name string, sc gpb.GNMI_SubscribeClient) error { + defer m.testSync() + connected := false + for { + resp, err := sc.Recv() + if err != nil { + if m.reset != nil { + m.reset(name) + } + return err + } + if !connected { + if m.connect != nil { + m.connect(name) + } + connected = true + log.Infof("Target %q successfully subscribed", name) + } + if err := m.handleGNMIUpdate(name, resp); err != nil { + log.Errorf("Error processing request %v for target %q: %v", resp, name, err) + } + m.testSync() + } +} + +// subscribeClient is exposed for testing. +var subscribeClient = func(ctx context.Context, conn *grpc.ClientConn) (gpb.GNMI_SubscribeClient, error) { + return gpb.NewGNMIClient(conn).Subscribe(ctx) +} + +func (m *Manager) subscribe(ctx context.Context, name string, conn *grpc.ClientConn, sr *gpb.SubscribeRequest) error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + log.Infof("Attempting to open stream to target %q", name) + sc, err := subscribeClient(ctx, conn) + if err != nil { + return fmt.Errorf("error opening stream to target %q: %v", name, err) + } + cr := customizeRequest(name, sr) + log.V(2).Infof("Sending subscription request to target %q: %v", name, cr) + if err := sc.Send(cr); err != nil { + return fmt.Errorf("error sending subscription request to target %q: %v", name, err) + } + if err = m.handleUpdates(name, sc); err != nil { + return fmt.Errorf("stream failed for target %q: %v", name, err) + } + return nil +} + +// customizeRequest clones the request template and customizes target prefix. +func customizeRequest(target string, sr *gpb.SubscribeRequest) *gpb.SubscribeRequest { + cl := proto.Clone(sr).(*gpb.SubscribeRequest) + if s := cl.GetSubscribe(); s != nil { + if p := s.GetPrefix(); p != nil { + p.Target = target + } else { + s.Prefix = &gpb.Path{Target: target} + } + } + return cl +} + +func (m *Manager) retryMonitor(ctx context.Context, ta *target) { + log.Infof("Starting monitoring of %q", ta.name) + timer := time.NewTimer(0) + + defer func() { + timer.Stop() + log.Infof("Finished monitoring %q", ta.name) + close(ta.finished) + // Ensure the cancelFunc for the subcontext is called. + m.Reconnect(ta.name) + }() + + // Create a subcontext that can be independently cancelled to force reconnect. + sCtx := m.reconnectCtx(ctx, ta) + for { + select { + case <-ctx.Done(): + // Avoid blocking on backoff timer when removing target. + return + case <-timer.C: + select { + case <-sCtx.Done(): + // Create a new subcontext if the target was forcibly reconnected. + sCtx = m.reconnectCtx(ctx, ta) + default: + } + t0 := time.Now() + err := m.monitor(sCtx, ta) + if time.Since(t0) > 2*RetryMaxDelay { + m.backoff.Reset() + } + delay := m.backoff.NextBackOff() + log.Errorf("Retrying monitoring of %q in %v due to error: %v", ta.name, delay, err) + timer.Reset(delay) + } + } +} + +func (m *Manager) monitor(ctx context.Context, ta *target) (err error) { + defer func() { + if err != nil && m.connectError != nil { + m.connectError(ta.name, err) + } + }() + + meta, err := gRPCMeta(ctx, ta.name, ta.t, m.cred) + if err != nil { + return fmt.Errorf("error creating gRPC metadata for %q: %v", ta.name, err) + } + sCtx := metadata.NewOutgoingContext(ctx, meta) + log.Infof("Attempting to connect %q", ta.name) + conn, done, err := m.createConn(sCtx, ta.name, ta.t) + if err != nil { + return + } + defer done() + return m.subscribe(sCtx, ta.name, conn, ta.sr) + +} + +// Add adds the target to Manager and starts a streaming subscription that +// continues until Remove is called. Failed subscriptions are retried using +// exponential backoff. +// +// After Add returns successfully, re-adding a target returns an error, +// unless first removed. +func (m *Manager) Add(name string, t *tpb.Target, sr *gpb.SubscribeRequest) error { + if name == "" { + return errors.New("empty name") + } + if sr == nil { + return fmt.Errorf("nil subscribe request for target %q", name) + } + + defer m.mu.Unlock() + m.mu.Lock() + if _, ok := m.targets[name]; ok { + return fmt.Errorf("target %q already added", name) + } + if len(t.GetAddresses()) == 0 { + return fmt.Errorf("no addresses for target %q", name) + } + ctx, cancel := context.WithCancel(context.Background()) + ta := &target{ + name: name, + t: t, + sr: sr, + cancel: cancel, + finished: make(chan struct{}), + } + m.targets[name] = ta + go m.retryMonitor(ctx, ta) + return nil +} + +// Remove stops the streaming connection to the target. Removing a target not +// added returns an error. Remove does not return until the subscription is +// completed and no further updates will occur. +func (m *Manager) Remove(name string) error { + m.mu.Lock() + defer m.mu.Unlock() + t, ok := m.targets[name] + if !ok { + return fmt.Errorf("attempt to remove target %q that is not added", name) + } + t.cancel() + <-t.finished + delete(m.targets, name) + log.Infof("Target %q removed", name) + return nil +} + +// reconnectCtx creates a cancellable subcontext for a target that can be +// forcibly reconnected by calling Reconnect. +func (m *Manager) reconnectCtx(ctx context.Context, t *target) context.Context { + sCtx, reconnect := context.WithCancel(ctx) + t.mu.Lock() + t.reconnect = reconnect + t.mu.Unlock() + return sCtx +} + +// Reconnect triggers a reconnection for the specified target. +func (m *Manager) Reconnect(name string) error { + m.mu.Lock() + t, ok := m.targets[name] + m.mu.Unlock() + if !ok { + return fmt.Errorf("no such target: %q", name) + } + t.mu.Lock() + if t.reconnect != nil { + t.reconnect() + t.reconnect = nil + } + t.mu.Unlock() + return nil +} diff --git a/deps/github.com/openconfig/gnmi/manager/manager_test.go b/deps/github.com/openconfig/gnmi/manager/manager_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bdd94276919798e7d04755e76cd5693dfa321b6f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/manager/manager_test.go @@ -0,0 +1,805 @@ +/* +Copyright 2020 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manager + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "net" + "reflect" + "sort" + "sync" + "sync/atomic" + "testing" + "time" + + log "github.com/golang/glog" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/connection" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + tpb "github.com/openconfig/gnmi/proto/target" +) + +var ( + validSubscribeRequest = &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + } +) + +type fakeCreds struct { + failNext bool +} + +func (f *fakeCreds) stageErr() { + f.failNext = true +} + +func (f *fakeCreds) Lookup(ctx context.Context, key string) (string, error) { + if f.failNext { + f.failNext = false + return "", errors.New("error occurred") + } + return fmt.Sprintf("pass_%s", key), nil +} + +type fakeServer struct{} + +func (f *fakeServer) Capabilities(context.Context, *gpb.CapabilityRequest) (*gpb.CapabilityResponse, error) { + return nil, nil +} + +func (f *fakeServer) Get(context.Context, *gpb.GetRequest) (*gpb.GetResponse, error) { + return nil, nil +} + +func (f *fakeServer) Set(context.Context, *gpb.SetRequest) (*gpb.SetResponse, error) { + return nil, nil +} + +func (f *fakeServer) Subscribe(gpb.GNMI_SubscribeServer) error { + return nil +} + +func newDevice(t *testing.T, s gpb.GNMIServer) (string, func()) { + t.Helper() + lis, err := net.Listen("tcp", "") + if err != nil { + t.Fatalf("failed to listen: %v", err) + } + srv := grpc.NewServer() + if srv == nil { + t.Fatalf("could not create test server") + } + gpb.RegisterGNMIServer(srv, s) + go srv.Serve(lis) + + return lis.Addr().String(), srv.Stop +} + +func TestNewManager(t *testing.T) { + tests := []struct { + desc string + c Config + wantErr bool + }{ + { + desc: "missing creds", + c: Config{ConnectionManager: newFakeConnection(t)}, + }, { + desc: "missing connection manager", + c: Config{Credentials: &fakeCreds{}}, + wantErr: true, + }, { + desc: "valid", + c: Config{Credentials: &fakeCreds{}, ConnectionManager: newFakeConnection(t)}, + }, + } + + for _, tt := range tests { + _, err := NewManager(tt.c) + switch { + case err == nil && !tt.wantErr: + case err == nil && tt.wantErr: + t.Errorf("%v: got no error, want error.", tt.desc) + case err != nil && !tt.wantErr: + t.Errorf("%v: got error, want no error.", tt.desc) + } + } +} + +func TestRemoveErr(t *testing.T) { + m, err := NewManager(Config{Credentials: &fakeCreds{}, ConnectionManager: newFakeConnection(t)}) + if err != nil { + t.Fatal("could not initialize Manager") + } + + if err := m.Remove("not added"); err == nil { + t.Fatalf("got no error removing target, want error") + } +} + +func assertTargets(t *testing.T, m *Manager, want int) { + if l := len(m.targets); l != want { + t.Fatalf("got %v targets, want %v", l, want) + } +} + +func TestAddRemove(t *testing.T) { + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + m, err := NewManager(Config{ + Credentials: &fakeCreds{}, + ConnectionManager: newFakeConnection(t), + Timeout: time.Minute, + }) + if err != nil { + t.Fatal("could not initialize Manager") + } + name := "device1" + + err = m.Add(name, &tpb.Target{Addresses: []string{addr}}, validSubscribeRequest) + if err != nil { + t.Fatalf("got error adding: %v, want no error", err) + } + assertTargets(t, m, 1) + if err := m.Remove(name); err != nil { + t.Fatalf("got error removing: %v, want no error", err) + } + assertTargets(t, m, 0) +} + +func TestAdd(t *testing.T) { + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + + tests := []struct { + desc string + name string + t *tpb.Target + sr *gpb.SubscribeRequest + wantErr bool + }{ + { + desc: "valid", + name: "device1", + t: &tpb.Target{Addresses: []string{addr}}, + sr: validSubscribeRequest, + }, { + desc: "missing address", + name: "device1", + t: &tpb.Target{}, + sr: validSubscribeRequest, + wantErr: true, + }, { + desc: "missing name", + t: &tpb.Target{Addresses: []string{addr}}, + sr: validSubscribeRequest, + wantErr: true, + }, { + desc: "nil request", + name: "device1", + t: &tpb.Target{Addresses: []string{addr}}, + wantErr: true, + }, + } + + for _, tt := range tests { + m, err := NewManager(Config{Credentials: &fakeCreds{}, ConnectionManager: newFakeConnection(t)}) + if err != nil { + t.Fatal("could not initialize Manager") + } + err = m.Add(tt.name, tt.t, tt.sr) + if err == nil { + m.Remove(tt.name) + } + switch { + case err == nil && !tt.wantErr: + case err == nil && tt.wantErr: + t.Errorf("%v: got no error, want error.", tt.desc) + case err != nil && !tt.wantErr: + t.Errorf("%v: got error, want no error.", tt.desc) + } + } +} + +func waitForAdd(m *Manager, name string) { + m.mu.Lock() + ta := m.targets[name] + m.mu.Unlock() + if ta == nil { + return + } + for { + if func() bool { + ta.mu.Lock() + defer ta.mu.Unlock() + return ta.reconnect != nil + }() { + break + } + time.Sleep(10 * time.Millisecond) + } +} + +func TestReconnect(t *testing.T) { + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + + m, err := NewManager(Config{Credentials: &fakeCreds{}, ConnectionManager: newFakeConnection(t)}) + if err != nil { + t.Fatal("could not initialize Manager") + } + if err := m.Add("target", &tpb.Target{Addresses: []string{addr}}, validSubscribeRequest); err != nil { + t.Fatal("could not add target") + } + defer m.Remove("target") + for _, tt := range []struct { + desc string + name string + err bool + }{ + {desc: "no such target", name: "badname", err: true}, + {desc: "reconnect once", name: "target"}, + {desc: "reconnect twice", name: "target"}, + } { + t.Run(tt.desc, func(t *testing.T) { + waitForAdd(m, tt.name) + err := m.Reconnect(tt.name) + switch { + case err != nil && !tt.err: + t.Errorf("got error %v, want nil", err) + case err == nil && tt.err: + t.Error("got nil, want error") + } + }) + } +} + +func TestConcurrentAdd(t *testing.T) { + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + m, err := NewManager(Config{Credentials: &fakeCreds{}, ConnectionManager: newFakeConnection(t)}) + if err != nil { + t.Fatal("could not initialize Manager") + } + name := "device1" + lim := 10 + var wg sync.WaitGroup + wg.Add(lim) + var errs uint32 + for i := 0; i < lim; i++ { + go func() { + if err := m.Add(name, &tpb.Target{Addresses: []string{addr}}, validSubscribeRequest); err != nil { + atomic.AddUint32(&errs, 1) + } + wg.Done() + }() + } + + wg.Wait() + // Only the first Add succeeds. + if errsFinal := atomic.LoadUint32(&errs); errsFinal != uint32(lim-1) { + t.Errorf("got %v errors, want %v", errsFinal, lim-1) + } + assertTargets(t, m, 1) + m.Remove(name) +} + +// fakeSubscribeClient implements GNMI_SubscribeClient. +type fakeSubscribeClient struct { + ch chan *gpb.SubscribeResponse + errCh chan error + failNextSend bool + grpc.ClientStream // Unused, satisfies interface. +} + +func (f *fakeSubscribeClient) stageSendErr() { + f.failNextSend = true +} + +func newFakeSubscribeClient() *fakeSubscribeClient { + f := &fakeSubscribeClient{ + ch: make(chan *gpb.SubscribeResponse), + errCh: make(chan error), + } + return f +} + +func (f *fakeSubscribeClient) Send(_ *gpb.SubscribeRequest) error { + if f.failNextSend { + f.failNextSend = false + return errors.New("error occurred") + } + return nil +} + +func (f *fakeSubscribeClient) Recv() (*gpb.SubscribeResponse, error) { + select { + case s := <-f.ch: + return s, nil + case err := <-f.errCh: + return nil, err + } +} + +func (f *fakeSubscribeClient) sendRecvErr() { + f.errCh <- errors.New("Recv error") +} + +func (f *fakeSubscribeClient) sendErr() { + f.ch <- &gpb.SubscribeResponse{Response: &gpb.SubscribeResponse_Error{}} +} + +func (f *fakeSubscribeClient) sendSync() { + f.ch <- &gpb.SubscribeResponse{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }} +} + +func (f *fakeSubscribeClient) sendUpdate() { + f.ch <- &gpb.SubscribeResponse{Response: &gpb.SubscribeResponse_Update{}} +} + +func (f *fakeSubscribeClient) sendCustomUpdate(u *gpb.SubscribeResponse) { + f.ch <- u +} + +type record struct { + connects []string + resets []string + syncs []string + updates []*gpb.Notification +} + +func (r *record) assertLast(t *testing.T, desc string, wantConnect, wantSync, wantReset []string, wantUpdate int) { + t.Helper() + sort.Strings(r.connects) + sort.Strings(r.syncs) + sort.Strings(r.resets) + switch { + case !reflect.DeepEqual(r.connects, wantConnect): + t.Fatalf("%s: Mismatched connects: got %v, want %v", desc, r.connects, wantConnect) + case !reflect.DeepEqual(r.syncs, wantSync): + t.Fatalf("%s: Mismatched syncs: got %v, want %v", desc, r.syncs, wantSync) + case !reflect.DeepEqual(r.resets, wantReset): + t.Fatalf("%s: Mismatched resets: got %v, want %v", desc, r.resets, wantReset) + case len(r.updates) != wantUpdate: + t.Fatalf("%s: Mismatched number of updates: got %v, want %v", desc, len(r.updates), wantUpdate) + } + r.connects = nil + r.resets = nil + r.syncs = nil + r.updates = nil + log.Infof("Successfully checked %v", desc) +} + +type fakeConnection struct { + m *connection.Manager + failNext bool +} + +func (f *fakeConnection) stageErr() { + f.failNext = true +} + +func (f *fakeConnection) Connection(ctx context.Context, addr string) (conn *grpc.ClientConn, done func(), err error) { + if f.failNext { + f.failNext = false + return nil, func() {}, errors.New("could not connect") + } + return f.m.Connection(ctx, addr) +} + +func newFakeConnection(t *testing.T) *fakeConnection { + t.Helper() + m, err := connection.NewManager(grpc.WithTransportCredentials( + credentials.NewTLS(&tls.Config{ + InsecureSkipVerify: true, + }))) + if err != nil { + t.Fatalf("failed to initialize Manager: %v", err) + } + return &fakeConnection{ + m: m, + } +} + +func TestRetrySubscribe(t *testing.T) { + origBaseDelay, origMaxDelay := RetryBaseDelay, RetryMaxDelay + defer func() { RetryBaseDelay, RetryMaxDelay = origBaseDelay, origMaxDelay }() + RetryBaseDelay, RetryMaxDelay = 0, 10*time.Millisecond + + f := newFakeConnection(t) + f.stageErr() // Initial failure causing retry. + + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + + origSubscribeClient := subscribeClient + defer func() { subscribeClient = origSubscribeClient }() + fc := newFakeSubscribeClient() + fc.stageSendErr() // Initial failure causing retry. + subscribeClient = func(ctx context.Context, conn *grpc.ClientConn) (gpb.GNMI_SubscribeClient, error) { + go func() { + select { + case <-ctx.Done(): + fc.sendRecvErr() // Simulate stream closure. + } + }() + return fc, nil + } + + r := record{} + creds := &fakeCreds{} + creds.stageErr() // Initial failure causing retry. + m, err := NewManager(Config{ + Credentials: creds, + Timeout: time.Minute, + Connect: func(s string) { + r.connects = append(r.connects, s) + }, + ConnectionManager: f, + Sync: func(s string) { + r.syncs = append(r.syncs, s) + }, + Reset: func(s string) { + r.resets = append(r.resets, s) + }, + Update: func(g *gpb.Notification) { + r.updates = append(r.updates, g) + }, + }) + if err != nil { + t.Fatal("could not initialize Manager") + } + chHandleUpdate := make(chan struct{}, 1) + m.testSync = func() { + chHandleUpdate <- struct{}{} + } + + name := "device1" + ta := &tpb.Target{ + Addresses: []string{addr}, + Credentials: &tpb.Credentials{ + Username: "user1", + PasswordId: "pass1", + }, + } + err = m.Add(name, ta, validSubscribeRequest) + if err != nil { + t.Fatalf("Add returned error, want no error") + } + defer m.Remove(name) + mTarget, ok := m.targets[name] + if !ok { + t.Fatalf("missing internal target") + } + + assertTargets(t, m, 1) + r.assertLast(t, "initial state", nil, nil, nil, 0) + + fc.sendSync() + <-chHandleUpdate + r.assertLast(t, "initial sync", []string{name}, []string{name}, nil, 0) + + lim := 10 + for i := 0; i < lim; i++ { + fc.sendUpdate() + <-chHandleUpdate + } + r.assertLast(t, "updates sent", nil, nil, nil, 10) + + fc.sendErr() + <-chHandleUpdate + r.assertLast(t, "error update", nil, nil, nil, 0) + + fc.sendCustomUpdate(&gpb.SubscribeResponse{}) + <-chHandleUpdate + r.assertLast(t, "nil update", nil, nil, nil, 0) + + fc.sendUpdate() + <-chHandleUpdate + r.assertLast(t, "update after errors", nil, nil, nil, 1) + + fc.sendRecvErr() + <-chHandleUpdate + r.assertLast(t, "recv error", nil, nil, []string{name}, 0) + + fc.sendSync() + <-chHandleUpdate + r.assertLast(t, "stream recovers", []string{name}, []string{name}, nil, 0) + + fc.sendUpdate() + <-chHandleUpdate + r.assertLast(t, "update after recovery", nil, nil, nil, 1) + + m.Remove(name) + <-chHandleUpdate + r.assertLast(t, "recv error after remove", nil, nil, []string{name}, 0) + + assertTargets(t, m, 0) + + go fc.sendUpdate() + <-mTarget.finished + r.assertLast(t, "no recovery after remove", nil, nil, nil, 0) +} + +func TestRemoveDuringBackoff(t *testing.T) { + origBaseDelay, origMaxDelay := RetryBaseDelay, RetryMaxDelay + defer func() { RetryBaseDelay, RetryMaxDelay = origBaseDelay, origMaxDelay }() + RetryBaseDelay, RetryMaxDelay = time.Hour, time.Hour // High delay exceeding test timeout. + + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + + origSubscribeClient := subscribeClient + defer func() { subscribeClient = origSubscribeClient }() + fc := newFakeSubscribeClient() + subscribeClient = func(ctx context.Context, conn *grpc.ClientConn) (gpb.GNMI_SubscribeClient, error) { + go func() { + select { + case <-ctx.Done(): + fc.sendRecvErr() // Simulate stream closure. + } + }() + return fc, nil + } + + m, err := NewManager(Config{ + Credentials: &fakeCreds{}, + ConnectionManager: newFakeConnection(t), + }) + if err != nil { + t.Fatal("could not initialize Manager") + } + chHandleUpdate := make(chan struct{}, 1) + m.testSync = func() { + chHandleUpdate <- struct{}{} + } + + name := "device1" + err = m.Add(name, &tpb.Target{Addresses: []string{addr}}, validSubscribeRequest) + if err != nil { + t.Fatalf("got error adding: %v, want no error", err) + } + assertTargets(t, m, 1) + + // Induce failure and backoff. + fc.sendRecvErr() + <-chHandleUpdate + + if err := m.Remove(name); err != nil { + t.Fatalf("got error removing: %v, want no error", err) + } + assertTargets(t, m, 0) +} + +func TestCancelSubscribe(t *testing.T) { + origSubscribeClient := subscribeClient + defer func() { subscribeClient = origSubscribeClient }() + fc := newFakeSubscribeClient() + subscribeClient = func(ctx context.Context, conn *grpc.ClientConn) (gpb.GNMI_SubscribeClient, error) { + return fc, nil + } + + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + + m, err := NewManager(Config{ + Credentials: &fakeCreds{}, + ConnectionManager: newFakeConnection(t), + }) + if err != nil { + t.Fatal("could not initialize Manager") + } + + cCtx, cancel := context.WithCancel(context.Background()) + cancel() + conn, done, err := m.connectionManager.Connection(context.Background(), addr) + if err != nil { + t.Fatalf("error creating connection: %v", err) + } + defer done() + if err := m.subscribe(cCtx, "device", conn, &gpb.SubscribeRequest{}); err == nil { + t.Fatalf("got no error, want error") + } +} + +func TestCustomizeRequest(t *testing.T) { + dev := "device" + tests := []struct { + desc string + sr *gpb.SubscribeRequest + want *gpb.SubscribeRequest + }{ + { + desc: "no existing prefix", + sr: &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + }, + want: &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Prefix: &gpb.Path{Target: dev}, + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + }, + }, + { + desc: "existing prefix", + sr: &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Prefix: &gpb.Path{Origin: "openconfig"}, + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + }, + want: &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Prefix: &gpb.Path{Origin: "openconfig", Target: dev}, + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + }, + }, + { + desc: "existing prefix with target", + sr: &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Prefix: &gpb.Path{Origin: "openconfig", Target: "unknown target"}, + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + }, + want: &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Prefix: &gpb.Path{Origin: "openconfig", Target: dev}, + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path1"}}, + }, + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + got := customizeRequest(dev, tt.sr) + if !proto.Equal(got, tt.want) { + t.Errorf("%s: got %v, want %v", tt.desc, got, tt.want) + } + } +} + +func TestCreateConn(t *testing.T) { + addr, stop := newDevice(t, &fakeServer{}) + defer stop() + + tests := []struct { + desc string + ta *tpb.Target + makeCtx func() context.Context + wantErr bool + }{ + { + desc: "missing address", + ta: &tpb.Target{}, + makeCtx: func() context.Context { + return context.Background() + }, + wantErr: true, + }, + { + desc: "canceled context", + ta: &tpb.Target{ + Addresses: []string{addr}, + }, + makeCtx: func() context.Context { + cCtx, cancel := context.WithCancel(context.Background()) + cancel() + return cCtx + }, + wantErr: true, + }, + { + desc: "valid", + ta: &tpb.Target{ + Addresses: []string{addr}, + }, + makeCtx: func() context.Context { + return context.Background() + }, + }, + } + + for _, tt := range tests { + m, err := NewManager(Config{ + Credentials: &fakeCreds{}, + ConnectionManager: newFakeConnection(t), + }) + if err != nil { + t.Fatal("could not initialize Manager") + } + _, _, err = m.createConn(tt.makeCtx(), "device", tt.ta) + switch { + case err == nil && !tt.wantErr: + case err == nil && tt.wantErr: + t.Errorf("%v: got no error, want error.", tt.desc) + case err != nil && !tt.wantErr: + t.Errorf("%v: got error, want no error.", tt.desc) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/manager/meta.go b/deps/github.com/openconfig/gnmi/manager/meta.go new file mode 100644 index 0000000000000000000000000000000000000000..ff84d25f9fd9adae576c02fb49cea9f5c5cf35af --- /dev/null +++ b/deps/github.com/openconfig/gnmi/manager/meta.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manager + +import ( + "context" + "fmt" + "strings" + + log "github.com/golang/glog" + "google.golang.org/grpc/metadata" + + tpb "github.com/openconfig/gnmi/proto/target" +) + +// gRPC metadata keys. +const ( + // Address is deprecated, proxies should support multiple addresses via the + // Addresses metadata key. + Address = "address" + Addresses = "addresses" + Password = "password" + Target = "target" + Username = "username" +) + +func gRPCMeta(ctx context.Context, name string, t *tpb.Target, cred CredentialsClient) (metadata.MD, error) { + meta := metadata.MD{} + c := t.GetCredentials() + if user := c.GetUsername(); user != "" { + if id := c.GetPasswordId(); id != "" { + if cred == nil { + return nil, fmt.Errorf("nil CredentialsClient used to lookup password ID for target %q", name) + } + p, err := cred.Lookup(ctx, id) + if err != nil { + return nil, fmt.Errorf("failed loading credentials for target %q: %v", name, err) + } + if p != "" { + meta.Set(Password, p) + } + } else if p := c.GetPassword(); p != "" { + meta.Set(Password, p) + } else { + return nil, fmt.Errorf("username has no associated password/password ID for target %q", name) + } + meta.Set(Username, user) + } + if name != "" { + meta.Set(Target, name) + } + for idx, ac := range addrChains(t.GetAddresses()) { + if len(ac) == 0 { + return nil, fmt.Errorf("empty address chain for target %q", name) + } + if idx == 0 { + meta.Set(Address, ac[1:]...) + } + next := strings.Join(ac[1:], AddrSeparator) + if next == "" { + continue + } + meta.Append(Addresses, next) + } + for k, v := range t.GetMeta() { + if _, ok := meta[k]; ok { + log.Warningf("Prefer generated metadata key over existing: %v", k) + continue + } + meta.Set(k, v) + } + return meta, nil +} diff --git a/deps/github.com/openconfig/gnmi/manager/meta_test.go b/deps/github.com/openconfig/gnmi/manager/meta_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bb3b48d1ccb466d3d60c8254b5207dc0a0a089d7 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/manager/meta_test.go @@ -0,0 +1,160 @@ +/* +Copyright 2020 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manager + +import ( + "context" + "reflect" + "testing" + + "google.golang.org/grpc/metadata" + + tpb "github.com/openconfig/gnmi/proto/target" +) + +func TestGRPCMeta(t *testing.T) { + tests := []struct { + name string + t *tpb.Target + c CredentialsClient + want metadata.MD + wantErr bool + }{ + { + name: "valid password ID", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + PasswordId: "passwordID1", + }, + Addresses: []string{"111.11.11.111"}, + }, + c: &fakeCreds{}, + want: metadata.Pairs( + Target, "valid password ID", + Username, "user1", + Password, "pass_passwordID1", + ), + }, { + name: "password ID with nil CredentialsClient", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + PasswordId: "passwordID1", + }, + Addresses: []string{"111.11.11.111"}, + }, + wantErr: true, + }, { + name: "valid password", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + Password: "password1", + }, + Addresses: []string{"111.11.11.111"}, + }, + want: metadata.Pairs( + Target, "valid password", + Username, "user1", + Password, "password1", + ), + }, { + name: "username with no password", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + }, + Addresses: []string{"111.11.11.111"}, + }, + c: &fakeCreds{}, + wantErr: true, + }, { + name: "valid proxied", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + PasswordId: "passwordID1", + }, + Addresses: []string{"proxy1;proxy1a;111.11.11.111", "proxy1;proxy1a;222.22.22.222"}, + }, + c: &fakeCreds{}, + want: metadata.Pairs( + Target, "valid proxied", + Username, "user1", + Password, "pass_passwordID1", + Address, "proxy1a", + Address, "111.11.11.111", + Addresses, "proxy1a;111.11.11.111", + Addresses, "proxy1a;222.22.22.222", + ), + }, { + name: "no username", + t: &tpb.Target{ + Addresses: []string{"111.11.11.111"}, + }, + want: metadata.Pairs( + Target, "no username", + ), + }, { + name: "credentials fetch error", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + PasswordId: "passwordID1", + }, + Addresses: []string{"111.11.11.111"}, + }, + c: &fakeCreds{failNext: true}, + wantErr: true, + }, { + name: "replaced meta", + t: &tpb.Target{ + Credentials: &tpb.Credentials{ + Username: "user1", + PasswordId: "passwordID1", + }, + Addresses: []string{"111.11.11.111"}, + Meta: map[string]string{ + "metaKey1": "foo", + Password: "will be replaced", + }, + }, + c: &fakeCreds{}, + want: metadata.Pairs( + Target, "replaced meta", + Username, "user1", + Password, "pass_passwordID1", + "metaKey1", "foo", + ), + }, + } + + for _, tt := range tests { + got, err := gRPCMeta(context.Background(), tt.name, tt.t, tt.c) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("%s: got: %+v, want: %+v", tt.name, got, tt.want) + } + switch { + case err == nil && !tt.wantErr: + case err == nil && tt.wantErr: + t.Errorf("%s: got no error, want error.", tt.name) + case err != nil && !tt.wantErr: + t.Errorf("%s: got error, want no error.", tt.name) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/match/match.go b/deps/github.com/openconfig/gnmi/match/match.go new file mode 100644 index 0000000000000000000000000000000000000000..bd23c9edb381f329b70bc0807ad6abf581199c66 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/match/match.go @@ -0,0 +1,161 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package match builds a tree of active subscriptions that is matched against +// all incoming updates. +package match + +import ( + "sync" +) + +// Glob is a special string used to indicate a match of any path node. +const Glob = "*" + +// Client is a interface for a callback invoked for all matching updates. +type Client interface { + Update(interface{}) +} + +type branch struct { + clients map[Client]struct{} + children map[string]*branch +} + +// Match is a structure that will invoke registered query callbacks for each +// matching update. +type Match struct { + mu sync.RWMutex + tree *branch +} + +// New creates a new Match structure. +func New() *Match { + return &Match{tree: &branch{}} +} + +// AddQuery registers a client callback for all matching nodes. It returns a +// callback to remove the query. The remove function is idempotent. +func (m *Match) AddQuery(query []string, client Client) (remove func()) { + defer m.mu.Unlock() + m.mu.Lock() + m.tree.addQuery(query, client) + return func() { + defer m.mu.Unlock() + m.mu.Lock() + m.tree.removeQuery(query, client) + } +} + +func (b *branch) addQuery(query []string, client Client) { + if len(query) == 0 { + if b.clients == nil { + b.clients = map[Client]struct{}{} + } + b.clients[client] = struct{}{} + return + } + if b.children == nil { + b.children = map[string]*branch{} + } + sb, ok := b.children[query[0]] + if !ok { + sb = &branch{} + b.children[query[0]] = sb + } + sb.addQuery(query[1:], client) +} + +func (b *branch) removeQuery(query []string, client Client) (empty bool) { + defer func() { + empty = (len(b.clients) == 0 && len(b.children) == 0) + }() + if len(query) == 0 { + if b.clients != nil { + delete(b.clients, client) + } + return + } + sb, ok := b.children[query[0]] + if !ok { + return + } + if sb.removeQuery(query[1:], client) { + delete(b.children, query[0]) + } + return +} + +// Update invokes all client callbacks for queries that match the supplied node. +func (m *Match) Update(n interface{}, p []string) { + defer m.mu.RUnlock() + m.mu.RLock() + m.tree.update(n, p, nil) +} + +// UpdateOnce invokes the callback of each client whose query matches the +// supplied node and if the client hasn't been updated (in which case it +// is not in the updated). If the callback of a client has been invoked in +// this call, the client will be added to updated if updated is not nil. +// Therefore, in order to track updated clients, updated must not be nil. +func (m *Match) UpdateOnce(n interface{}, p []string, updated map[Client]struct{}) { + defer m.mu.RUnlock() + m.mu.RLock() + m.tree.update(n, p, updated) +} + +func (b *branch) update(n interface{}, path []string, updated map[Client]struct{}) { + // Update all clients at this level. + for client := range b.clients { + if updated == nil { + client.Update(n) + continue + } + if _, ok := updated[client]; !ok { + client.Update(n) + updated[client] = struct{}{} + } + } + // Terminate recursion. + if len(b.children) == 0 { + return + } + // Implicit recursion for intermediate deletes. + if len(path) == 0 { + for _, c := range b.children { + c.update(n, nil, updated) + } + return + } + // This captures only target delete gnmi.Notification as it is going + // to have Glob in the path elem in addition to device name. + // For target delete client.Notification, device name is sufficient, + // so it will not satisfy this case. + if path[0] == Glob { + for _, c := range b.children { + c.update(n, path[1:], updated) + } + } else { + // Update all glob clients. + if sb, ok := b.children[Glob]; ok { + sb.update(n, path[1:], updated) + } + // Update all explicit clients. + if sb, ok := b.children[path[0]]; ok { + sb.update(n, path[1:], updated) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/match/match_test.go b/deps/github.com/openconfig/gnmi/match/match_test.go new file mode 100644 index 0000000000000000000000000000000000000000..00b52efef69ef5712730c8ef1c231652babb7643 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/match/match_test.go @@ -0,0 +1,307 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package match + +import ( + "reflect" + "testing" +) + +type stored struct { + ph []string +} + +type testClient struct { + updates []stored +} + +type testQuery struct { + client testClient + query []string + match []stored + noMatch []stored + remove func() +} + +func (t *testClient) Update(n interface{}) { + t.updates = append(t.updates, n.(stored)) +} + +func TestClient(t *testing.T) { + tests := []*testQuery{ + { + query: []string{}, + match: []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + }, { + query: []string{Glob}, + match: []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + }, { + query: []string{"a"}, + match: []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + noMatch: []stored{ + {ph: []string{"b"}}, + {ph: []string{"c"}}, + {ph: []string{"d", "e", "f", "g"}}, + }, + }, { + query: []string{"a", "b"}, + match: []stored{ + {ph: []string{"a"}}, // Target delete. + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + noMatch: []stored{ + {ph: []string{"a", "c"}}, + {ph: []string{"b"}}, + {ph: []string{"c"}}, + {ph: []string{"d", "e", "f", "g"}}, + }, + }, { + query: []string{"a", "b", Glob}, + match: []stored{ + {ph: []string{"a"}}, // Target delete. + {ph: []string{"a", "b"}}, // Intermediate delete. + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + noMatch: []stored{ + {ph: []string{"a", "c"}}, + {ph: []string{"b"}}, + {ph: []string{"c"}}, + {ph: []string{"d", "e", "f", "g"}}, + }, + }, { + query: []string{Glob, "b"}, + match: []stored{ + {ph: []string{"a"}}, // Target delete. + {ph: []string{"b"}}, // Target delete. + {ph: []string{"c"}}, // Target delete. + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + {ph: []string{"e", "b", "y"}}, + }, + noMatch: []stored{ + {ph: []string{"a", "c"}}, + {ph: []string{"d", "e", "f", "g"}}, + }, + }, { + query: []string{"a", "b", "*", "d", "e", "f", "g"}, + match: []stored{ + {ph: []string{"a"}}, // Target delete. + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c"}}, + {ph: []string{"a", "b", "c", "d"}}, + {ph: []string{"a", "b", "c", "d", "e"}}, + {ph: []string{"a", "b", "c", "d", "e", "f"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + noMatch: []stored{ + {ph: []string{"a", "c"}}, + {ph: []string{"d", "e", "f", "g"}}, + }, + }, + } + for _, tt := range tests { + match := New() + remove := match.AddQuery(tt.query, &tt.client) + for _, m := range tt.match { + match.Update(m, m.ph) + } + for _, m := range tt.noMatch { + match.Update(m, m.ph) + } + if !reflect.DeepEqual(tt.client.updates, tt.match) { + t.Errorf("query %q got updates %q, want %q", tt.query, tt.client.updates, tt.match) + } + remove() + tt.client.updates = nil + for _, m := range tt.match { + match.Update(m, m.ph) + } + for _, m := range tt.noMatch { + match.Update(m, m.ph) + } + if tt.client.updates != nil { + t.Errorf("removed query %q got updates %q, want nil", tt.query, tt.client.updates) + } + } +} + +func TestMultipleClients(t *testing.T) { + updates := []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + {ph: []string{"b"}}, + {ph: []string{"c"}}, + {ph: []string{"d", "e", "f", "g"}}, + {ph: []string{"e", "b", "y"}}, + } + tests := []*testQuery{ + { + query: []string{}, + match: []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + {ph: []string{"b"}}, + {ph: []string{"c"}}, + {ph: []string{"d", "e", "f", "g"}}, + {ph: []string{"e", "b", "y"}}, + }, + }, { + query: []string{Glob}, + match: []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + {ph: []string{"b"}}, + {ph: []string{"c"}}, + {ph: []string{"d", "e", "f", "g"}}, + {ph: []string{"e", "b", "y"}}, + }, + }, { + query: []string{"a"}, + match: []stored{ + {ph: []string{"a"}}, + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + }, { + query: []string{"a", "b"}, + match: []stored{ + {ph: []string{"a"}}, // Target delete. + {ph: []string{"a", "b"}}, + {ph: []string{"a", "b", "c", "d", "e", "f", "g"}}, + }, + }, { + query: []string{"b"}, + match: []stored{ + {ph: []string{"b"}}, + }, + }, { + query: []string{"q"}, + }, + } + match := New() + // Add one query at a time. + for x, tt := range tests { + for _, tt := range tests { + tt.client.updates = nil + } + tt.remove = match.AddQuery(tt.query, &tt.client) + for _, u := range updates { + match.Update(u, u.ph) + } + for _, tt := range tests[:x+1] { + if !reflect.DeepEqual(tt.client.updates, tt.match) { + t.Errorf("query %q got updates %q, want %q", tt.query, tt.client.updates, tt.match) + } + } + } + // Remove one query at a time. + for x, tt := range tests { + for _, tt := range tests { + tt.client.updates = nil + } + tt.remove() + for _, u := range updates { + match.Update(u, u.ph) + } + for _, tt := range tests[x+1:] { + if !reflect.DeepEqual(tt.client.updates, tt.match) { + t.Errorf("query %q got updates %q, want %q", tt.query, tt.client.updates, tt.match) + } + } + } +} + +type globTestClient struct { + match bool +} + +type globQuery struct { + client globTestClient + query []string + expect bool + remove func() +} + +func (t *globTestClient) Update(n interface{}) { + t.match = true +} + +func TestGlobUpdates(t *testing.T) { + tests := []struct { + update stored + queries []*globQuery + }{ + // test updates with Glob at the end of the path + { + update: stored{ph: []string{"a", Glob}}, + queries: []*globQuery{ + &globQuery{query: []string{"a"}, expect: true}, + &globQuery{query: []string{"a", Glob}, expect: true}, + &globQuery{query: []string{"a", "b"}, expect: true}, + &globQuery{query: []string{"b"}, expect: false}, + }, + }, + // test updates with Glob in the middle of the path + { + update: stored{ph: []string{"a", Glob, "b"}}, + queries: []*globQuery{ + &globQuery{query: []string{"a"}, expect: true}, + &globQuery{query: []string{"a", Glob}, expect: true}, + &globQuery{query: []string{"a", "b"}, expect: true}, + &globQuery{query: []string{"a", "c", "b"}, expect: true}, + &globQuery{query: []string{"a", "c", "c"}, expect: false}, + &globQuery{query: []string{"a", Glob, "b"}, expect: true}, + &globQuery{query: []string{"a", Glob, "c"}, expect: false}, + &globQuery{query: []string{"b"}, expect: false}, + }, + }, + } + + for i, tt := range tests { + match := New() + // register all the queries + for _, q := range tt.queries { + q.remove = match.AddQuery(q.query, &q.client) + } + match.Update(tt.update, tt.update.ph) + for j, q := range tt.queries { + if q.client.match != q.expect { + t.Errorf("#%d update message %v; #%d query path %v; got %v, want %v", i, tt.update.ph, j, q.query, q.client.match, q.expect) + } + } + for _, q := range tt.queries { + q.remove() + } + } +} diff --git a/deps/github.com/openconfig/gnmi/metadata/metadata.go b/deps/github.com/openconfig/gnmi/metadata/metadata.go new file mode 100644 index 0000000000000000000000000000000000000000..22dbd9cbecde024b5bd081d2d13187bc50c39e56 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/metadata/metadata.go @@ -0,0 +1,316 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package metadata describes metadata paths stored in the cache per target. +package metadata + +import ( + "errors" + "fmt" + "sync" +) + +const ( + // Root node where metadata is cached. + Root = "meta" + + // Per-target metadata + + // Sync is a boolean that reports whether all target state is cached. + Sync = "sync" + // Connected is a boolean that reports whether updates are being received. + Connected = "connected" + // ConnectedAddr is a string denoting the last-hop IP address of a connected + // target. + ConnectedAddr = "connectedAddress" + // AddCount is the total number of leaves that have been added. + AddCount = "targetLeavesAdded" + // DelCount is the total number of leaves that have been deleted. + DelCount = "targetLeavesDeleted" + // EmptyCount is the total number of notifications delivered that contain no + // updates or deletes. + EmptyCount = "targetLeavesEmpty" + // LeafCount is the current total leaves stored in the cache. + LeafCount = "targetLeaves" + // UpdateCount is the total number of leaf updates received. + UpdateCount = "targetLeavesUpdated" + // StaleCount is the total number of leaf updates that had timestamp older + // than that cached. + StaleCount = "targetLeavesStale" + // SuppressedCount is the total number of leaf updates that were suppressed + // because the update had the same value as already cached. + SuppressedCount = "targetLeavesSuppressed" + // Size is the total number of bytes used to store all values. This count + // excludes all indexing overhead. + Size = "targetSize" + // LatestTimestamp is the latest timestamp for any update received for the + // target. + LatestTimestamp = "latestTimestamp" + // ConnectError is the error related to connection failure. + ConnectError = "connectError" +) + +// IntValue contains the path and other options for an int64 metadata. +type IntValue struct { + Path []string // Path of the int64 metadata + InitZero bool // Whether to initiate to 0 (for counters starting from 0). +} + +// RegisterIntValue registers an int64 type metadata whose path and options +// are in val. +func RegisterIntValue(name string, val *IntValue) { + TargetIntValues[name] = val +} + +// UnregisterIntValue unregisters an int64 type metadata name. +func UnregisterIntValue(name string) { + delete(TargetIntValues, name) +} + +// StrValue contains the valid and the option to reset to emptry string. +type StrValue struct { + Valid bool + InitEmptyStr bool // Whether to initiate to "". +} + +var ( + // TargetBoolValues is the list of all bool metadata fields. + TargetBoolValues = map[string]bool{ + Sync: true, + Connected: true, + } + + // TargetIntValues is the list of all int64 metadata fields. + TargetIntValues = map[string]*IntValue{ + AddCount: {[]string{Root, AddCount}, true}, + DelCount: {[]string{Root, DelCount}, true}, + EmptyCount: {[]string{Root, EmptyCount}, true}, + LeafCount: {[]string{Root, LeafCount}, true}, + UpdateCount: {[]string{Root, UpdateCount}, true}, + StaleCount: {[]string{Root, StaleCount}, true}, + SuppressedCount: {[]string{Root, SuppressedCount}, true}, + Size: {[]string{Root, Size}, true}, + LatestTimestamp: {[]string{Root, LatestTimestamp}, true}, + } + + // TargetStrValues is the list of all string metadata fields. + TargetStrValues = map[string]*StrValue{ + ConnectedAddr: {Valid: true, InitEmptyStr: true}, + ConnectError: {Valid: true, InitEmptyStr: false}, + } +) + +// Metadata is the container for all target specific metadata. +type Metadata struct { + mu sync.Mutex + valuesInt map[string]int64 + valuesBool map[string]bool + valuesStr map[string]string +} + +// Path is a convenience function that will return the full metadata path for +// any valid metadata value. Only metadata values registered above in +// TargetBoolValues, TargetIntValues, and TargetStrValues will return a path. +// An invalid metadata value will return nil. +func Path(value string) []string { + if TargetBoolValues[value] { + return []string{Root, value} + } + if val, ok := TargetStrValues[value]; ok && val.Valid { + return []string{Root, value} + } + + if val := TargetIntValues[value]; val != nil { + return val.Path + } + return nil +} + +// New returns an initialized Metadata structure. Integer values are +// initialized to 0. Boolean values are initialized to false. String values are +// initialized to empty string. +func New() *Metadata { + m := Metadata{ + valuesInt: make(map[string]int64, len(TargetIntValues)), + valuesBool: make(map[string]bool, len(TargetBoolValues)), + valuesStr: make(map[string]string, len(TargetStrValues)), + } + m.Clear() + return &m +} + +// ErrInvalidValue is returned when a metadata operation is attempted on a value +// that does not exist. +var ErrInvalidValue = errors.New("invalid metadata value") + +func validInt(value string) error { + if val := TargetIntValues[value]; val == nil { + return ErrInvalidValue + } + return nil +} + +func validBool(value string) error { + if valid := TargetBoolValues[value]; !valid { + return ErrInvalidValue + } + return nil +} + +func validStr(value string) error { + if valid, ok := TargetStrValues[value]; !ok || !valid.Valid { + return ErrInvalidValue + } + return nil +} + +// ResetEntry resets metadata entry to zero value. It will be deleted +// if it is Int with InitZero as false, or Str with InitEmptyStr as false. +func (m *Metadata) ResetEntry(entry string) error { + if validBool(entry) == nil { + m.SetBool(entry, false) + return nil + } + + if validInt(entry) == nil { + val := TargetIntValues[entry] + if val.InitZero { + m.SetInt(entry, 0) + } else { + m.mu.Lock() + delete(m.valuesInt, entry) + m.mu.Unlock() + } + return nil + } + + if validStr(entry) == nil { + val := TargetStrValues[entry] + if val.InitEmptyStr { + m.SetStr(entry, "") + } else { + m.mu.Lock() + delete(m.valuesStr, entry) + m.mu.Unlock() + } + return nil + } + + return fmt.Errorf("unsupported entry %q", entry) +} + +// Clear sets all metadata values to zero values, except that ConnectError is set to EmptyError. +func (m *Metadata) Clear() { + for k := range TargetBoolValues { + m.ResetEntry(k) + } + for k := range TargetIntValues { + m.ResetEntry(k) + } + for k := range TargetStrValues { + m.ResetEntry(k) + } +} + +// AddInt atomically increments the metadata value specified by i. +func (m *Metadata) AddInt(value string, i int64) error { + if err := validInt(value); err != nil { + return err + } + m.mu.Lock() + m.valuesInt[value] += i + m.mu.Unlock() + return nil +} + +// SetInt atomically sets the metadata value specified to v. +func (m *Metadata) SetInt(value string, v int64) error { + if err := validInt(value); err != nil { + return err + } + m.mu.Lock() + m.valuesInt[value] = v + m.mu.Unlock() + return nil +} + +// ErrUnsetValue is returned when a metadata Get is attempted on a value that +// has not been Set (or Added). +var ErrUnsetValue = errors.New("unset value") + +// GetInt atomically retrieves the metadata value specified. +func (m *Metadata) GetInt(value string) (int64, error) { + if err := validInt(value); err != nil { + return 0, err + } + m.mu.Lock() + v, ok := m.valuesInt[value] + m.mu.Unlock() + if !ok { + return 0, ErrUnsetValue + } + return v, nil +} + +// SetBool atomically sets the metadata value specified to v. +func (m *Metadata) SetBool(value string, v bool) error { + if err := validBool(value); err != nil { + return err + } + m.mu.Lock() + m.valuesBool[value] = v + m.mu.Unlock() + return nil +} + +// GetBool atomically retrieves the metadata value specified. +func (m *Metadata) GetBool(value string) (bool, error) { + if err := validBool(value); err != nil { + return false, err + } + m.mu.Lock() + v, ok := m.valuesBool[value] + m.mu.Unlock() + if !ok { + return false, ErrUnsetValue + } + return v, nil +} + +// SetStr atomically sets the metadata value specified to v. +func (m *Metadata) SetStr(value, v string) error { + if err := validStr(value); err != nil { + return err + } + m.mu.Lock() + m.valuesStr[value] = v + m.mu.Unlock() + return nil +} + +// GetStr atomically retrieves the metadata value specified. +func (m *Metadata) GetStr(value string) (string, error) { + if err := validStr(value); err != nil { + return "", err + } + m.mu.Lock() + v, ok := m.valuesStr[value] + m.mu.Unlock() + if !ok { + return "", ErrUnsetValue + } + return v, nil +} diff --git a/deps/github.com/openconfig/gnmi/metadata/metadata_test.go b/deps/github.com/openconfig/gnmi/metadata/metadata_test.go new file mode 100644 index 0000000000000000000000000000000000000000..54c5164e6e1086870fe8d66ad04a7e4a372cb5a4 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/metadata/metadata_test.go @@ -0,0 +1,289 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package metadata + +import "testing" + +func TestPath(t *testing.T) { + if path := Path("invalid"); path != nil { + t.Errorf("Path(%q) returned %v for invalid value.", "invalid", path) + } + for value := range TargetBoolValues { + if path := Path(value); path == nil { + t.Errorf("Path(%q) returned nil for valid value.", value) + } + } + for value := range TargetIntValues { + if path := Path(value); path == nil { + t.Errorf("Path(%q) returned nil for valid value.", value) + } + } + for value := range TargetStrValues { + if path := Path(value); path == nil { + t.Errorf("Path(%q) returned nil for valid value.", value) + } + } +} + +func TestGetInt(t *testing.T) { + m := New() + for value := range TargetIntValues { + v, err := m.GetInt(value) + switch { + case err != nil: + t.Errorf("GetInt(%q) got error: %q, want nil", value, err) + case v != 0: + t.Errorf("GetInt(%q) got %d for uninitialized value, want 0", value, v) + } + } +} + +func TestGetBool(t *testing.T) { + m := New() + for value := range TargetBoolValues { + v, err := m.GetBool(value) + switch { + case err != nil: + t.Errorf("GetBool(%q) got error: %q, want nil", value, err) + case v != false: + t.Errorf("GetBool(%q) got %t for uninitialized value, want false", value, v) + } + } +} + +func TestGetStr(t *testing.T) { + m := New() + for k, val := range TargetStrValues { + if !val.InitEmptyStr { + m.SetStr(k, "") + } + v, err := m.GetStr(k) + switch { + case err != nil: + t.Errorf("GetStr(%q) got error: %q, want nil", k, err) + case v != "": + t.Errorf("GetStr(%q) got %q for uninitialized value, want empty string", k, v) + } + } +} + +func TestAddGetInt(t *testing.T) { + m := New() + if err := m.AddInt("invalid", 1); err != ErrInvalidValue { + t.Error("AddInt accepted invalid metadata value.") + } + for i := 1; i < 6; i++ { + for value := range TargetIntValues { + if err := m.AddInt(value, 1); err != nil { + t.Errorf("AddInt(%q, 1) returned error: %v", value, err) + } + got, err := m.GetInt(value) + if err != nil { + t.Errorf("GetInt(%q) returned error: %v", value, err) + continue + } + want := int64(i) + if got != want { + t.Errorf("%d: GetInt(%q): got %d, want %d", i, value, got, want) + } + } + } + if _, err := m.GetInt("invalid"); err != ErrInvalidValue { + t.Error("GetInt accepted invalid metadata value.") + } +} + +func TestSetGetInt(t *testing.T) { + m := New() + if err := m.SetInt("invalid", 1); err != ErrInvalidValue { + t.Error("SetInt accepted invalid metadata value.") + } + for i := 0; i < 10; i++ { + var x int + for value := range TargetIntValues { + x++ + want := int64(x + i) + if err := m.SetInt(value, want); err != nil { + t.Errorf("SetInt(%q, %d) returned error: %v", value, want, err) + } + got, err := m.GetInt(value) + if err != nil { + t.Errorf("GetInt(%q) returned error: %v", value, err) + } + if got != want { + t.Errorf("GetInt(%q): got %d, want %d", value, got, want) + } + } + } +} + +func TestSetGetBool(t *testing.T) { + m := New() + if err := m.SetBool("invalid", true); err != ErrInvalidValue { + t.Error("SetBool accepted invalid metadata value.") + } + for _, want := range []bool{true, false, true, false} { + for value := range TargetBoolValues { + if err := m.SetBool(value, want); err != nil { + t.Errorf("SetBool(%q, %t) returned error: %v", value, want, err) + } + got, err := m.GetBool(value) + if err != nil { + t.Errorf("GetBool(%q) returned error: %v", value, err) + } + if got != want { + t.Errorf("GetBool(%q): got %t, want %t", value, got, want) + } + } + } + if _, err := m.GetBool("invalid"); err != ErrInvalidValue { + t.Error("GetBool accepted invalid metadata value.") + } +} + +func TestSetGetStr(t *testing.T) { + m := New() + if err := m.SetStr("invalid", "invalidValue"); err != ErrInvalidValue { + t.Error("SetStr accepted invalid metadata value.") + } + for _, want := range []string{"value1", "value2", "value3", "value4"} { + for value := range TargetStrValues { + if err := m.SetStr(value, want); err != nil { + t.Errorf("SetStr(%q, %q) returned error: %v", value, want, err) + } + got, err := m.GetStr(value) + if err != nil { + t.Errorf("GetStr(%q) returned error: %v", value, err) + } + if got != want { + t.Errorf("GetStr(%q): got %q, want %q", value, got, want) + } + } + } + if _, err := m.GetStr("invalid"); err != ErrInvalidValue { + t.Error("GetStr accepted invalid metadata value.") + } +} + +func TestResetEntry(t *testing.T) { + m := New() + + for k := range TargetBoolValues { + m.SetBool(k, true) + m.ResetEntry(k) + v, err := m.GetBool(k) + if err != nil { + t.Errorf("ResetEntry for %q failed with error: %v", k, err) + } + if v != false { + t.Errorf("ResetEntry for %q failed. got %t, want %t", k, false, v) + } + } + + for k, val := range TargetIntValues { + m.SetInt(k, 1) + m.ResetEntry(k) + v, err := m.GetInt(k) + + if !val.InitZero { + if err == nil { + t.Errorf("ResetEntry for %q failed. Expect not exist, but found.", k) + } + } else { + if err != nil { + t.Errorf("ResetEntry for %q failed with error: %v", k, err) + } + if v != 0 { + t.Errorf("ResetEntry for %q failed. got %q, want %q", k, 0, v) + } + } + + } + + for k, val := range TargetStrValues { + m.SetStr(k, "xx") + m.ResetEntry(k) + v, err := m.GetStr(k) + + if !val.InitEmptyStr { + if err == nil { + t.Errorf("ResetEntry for %q failed. Expect not exist, but found.", k) + } + } else { + if err != nil { + t.Errorf("ResetEntry for %q failed with error: %v", k, err) + } + if v != "" { + t.Errorf("ResetEntry for %q failed. got %q, want %q", k, "", v) + } + } + + } + + if err := m.ResetEntry("unsupported"); err == nil { + t.Errorf("ResetEntry expected error, but got nil") + } +} + +func TestClear(t *testing.T) { + m := New() + m.Clear() + + for k := range TargetBoolValues { + v, err := m.GetBool(k) + if err != nil { + t.Errorf("ResetEntry for %q failed with error %t", k, err) + } + if v { + t.Errorf("ResetEntry for %q failed. got %t, want %t", k, false, v) + } + } + + for k, val := range TargetIntValues { + v, err := m.GetInt(k) + if !val.InitZero { + if err == nil { + t.Errorf("ResetEntry for %q failed. Expect not exist, but found.", k) + } + } else { + if err != nil { + t.Errorf("ResetEntry for %q failed with error: %v", k, err) + } + if v != 0 { + t.Errorf("ResetEntry for %q failed. got %q, want %q", k, 0, v) + } + } + + } + + for k, val := range TargetStrValues { + v, err := m.GetStr(k) + if !val.InitEmptyStr { + if err == nil { + t.Errorf("ResetEntry for %q failed. Expect not exist, but found.", k) + } + } else { + if err != nil { + t.Errorf("ResetEntry for %q failed with error: %v", k, err) + } + if v != "" { + t.Errorf("ResetEntry for %q failed. got %q, want %q", k, "", v) + } + } + + } +} diff --git a/deps/github.com/openconfig/gnmi/metadata/yang/gnmi-collector-metadata.yang b/deps/github.com/openconfig/gnmi/metadata/yang/gnmi-collector-metadata.yang new file mode 100644 index 0000000000000000000000000000000000000000..19fe450ce9e110b6828cae8d2683536761b20aa6 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/metadata/yang/gnmi-collector-metadata.yang @@ -0,0 +1,205 @@ +module gnmi-collector-metadata { + yang-version 1; + + namespace "http://github.com/openconfig/gnmi/yang/collector-metadata"; + + prefix "gnmi-coll-metadata"; + + container meta { + leaf sync { + type boolean; + description + "sync indicates that at least one copy of the target's entire tree has + been received, as indicated by the sync_response field in a gNMI + SubscribeResponse message + (https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#3514-the-subscriberesponse-message)."; + } + + leaf connected { + type boolean; + description + "connected reports whether the target has an active gRPC session with + the target device; it requires at least 1 update delivered over the + connection before being set to true."; + } + + leaf connectedAddress { + type string; + description + "connectedAddress denotes the last-hop IP address of a connected target + in IP:Port format (e.g., '10.1.1.1:12345', + '[123:123:123:123::1]:12345')."; + } + + leaf targetLeavesAdded { + type int64; + description + "targetLeavesAdded is the total number of leaves that have been added. + This number may be larger than meta/targetLeaves due to deletes."; + } + + leaf targetLeavesDeleted { + type int64; + description + "targetLeavesDeleted is the total number of leaves that have been + deleted."; + } + + leaf targetLeavesEmpty { + type int64; + description + "targetLeavesEmpty is the total number of empty notifications received + from the target."; + } + + leaf targetLeaves { + type int64; + description + "targetLeaves is the total number of leaves available for the target. + Note that this does not include any intermediate nodes."; + } + + leaf targetLeavesUpdated { + type int64; + description + "targetLeavesUpdated is the total number of leaf updates that have been + received from the target by the collector"; + } + + leaf targetLeavesStale { + type int64; + description + "targetLeavesStale is the total number of leaf updates that were received + with a timestamp older than the latest timestamp reported for the target."; + } + + leaf targetLeavesSuppressed { + type int64; + description + "targetLeavesSuppressed is the total number of leaf updates that were not + forwarded to subscribers because the value had not changed."; + } + + container latency { + description + "latency statistics between target timestamp and cache reception for a + list of time window sizes - latency being calculated by (timestamp + of arrival) - (timestamp in update). These statistics are reported + per update period. Both the update period and window sizes are + configurable via commandline flags."; + + list window { + key "size"; + + description + "latency statistics for a time window."; + + leaf size { + type leafref { + path "../state/size"; + } + description + "A reference to a a unique string identifying the time window + size (e.g. 2s, 5m)."; + } + + leaf avg { + type int64; + units nanoseconds; + description + "avg is the average latency in nanoseconds of the time window. + It is calculated and reported per update period."; + } + + leaf max { + type int64; + units nanoseconds; + description + "max is the maximum latency in nanoseconds of the time window. + It is calculated and reported per update period."; + } + + leaf min { + type int64; + units nanoseconds; + description + "min is the minimum latency in nanoseconds of the time window. + It is calculated and reported per update period."; + } + + container state { + config false; + description + "Operational state parameters relating to the latency time window."; + uses meta-latency-window-state; + } + } + } + + leaf latencyAvg { + type int64; + units nanoseconds; + description + "latencyAvg is the average latency in nanoseconds between target + timestamp and cache reception - latency being calculated by (timestamp + of arrival) - (timestamp in update). It is reported per update + window based on the commandline flag for metadata updates."; + } + + leaf latencyMax { + type int64; + units nanoseconds; + description + "latencyMax is the maximum latency in nanoseconds between target + timestamp and cache reception - latency being calculated by (timestamp + of arrival) - (timestamp in update). It is reported per update + window based on the commandline flag for metadata updates."; + } + + leaf latencyMin { + type int64; + units nanoseconds; + description + "latencyMin is the minimum latency in nanoseconds - latency being + calculated by (timestamp of arrival) - (timestamp in update). It is + reported per update window based on the commandline flag for metadata + updates."; + } + + leaf targetSize { + type int64; + units bytes; + description + "targetSize is the total number of bytes used to store all values. This + count excludes all indexing overhead. This value is updated periodically + and may not be up to date at all times."; + } + + leaf latestTimestamp { + type int64; + units nanoseconds; + description + "latestTimestamp is the latest timestamp in nanoseconds since Epoch time + of the latest update received from the target. This value is updated + periodically so it may lag behind the actual target updates."; + } + + leaf connectError { + type string; + description + "connectError is the error related to connection failure."; + } + + } + + grouping meta-latency-window-state { + description + "Parameters relating to a latency time window."; + + leaf size { + type string; + description + "A unique string identifying the time window size (e.g. 2s, 5m)."; + } + } +} \ No newline at end of file diff --git a/deps/github.com/openconfig/gnmi/path/path.go b/deps/github.com/openconfig/gnmi/path/path.go new file mode 100644 index 0000000000000000000000000000000000000000..04c659d029db6f84a45bc1e0cc650e3d2f8e149d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/path/path.go @@ -0,0 +1,122 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package path provides utility functions to convert a given gnmi.Path +// into index strings. +package path + +import ( + "errors" + "sort" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +// ToStrings converts gnmi.Path to index strings. When index strings are generated, +// gnmi.Path will be irreversibly lost. Index strings will be built by using name field +// in gnmi.PathElem. If gnmi.PathElem has key field, values will be included in +// alphabetical order of the keys. +// E.g. <target>/<origin>/a/b[b:d, a:c]/e will be returned as <target>/<origin>/a/b/c/d/e +// If prefix parameter is set to true, <target> and <origin> fields of +// the gnmi.Path will be prepended in the index strings unless they are empty string. +// gnmi.Path.Element field is deprecated, but being gracefully handled by this function +// in the absence of gnmi.Path.Elem. +func ToStrings(p *gpb.Path, prefix bool) []string { + if p == nil { + return []string{} + } + // maxPathLen is chosen to be larger than the longest path in OpenConfig + // modeled data. Correctness is achieved regardless of this value but + // there is a performance benefit of not resizing allocations on the slice + // during the appends in this function. + const maxPathLen = 20 + is := make([]string, 0, maxPathLen) + if prefix { + // add target to the list of index strings + if t := p.GetTarget(); t != "" { + is = append(is, t) + } + // add origin to the list of index strings + if o := p.GetOrigin(); o != "" { + is = append(is, o) + } + } + if len(p.GetElem()) == 0 { + // gnmi.Path.Element is deprecated, but being gracefully handled + // when gnmi.PathElem doesn't exist + return append(is, p.GetElement()...) + } + for _, e := range p.GetElem() { + is = append(is, e.GetName()) + keys := e.GetKey() + switch len(keys) { + case 0: + // No keys, don't do anything. + case 1: + // Special case single key lists, append the only value. + for _, v := range keys { + is = append(is, v) + } + default: + is = append(is, sortedVals(keys)...) + } + } + return is +} + +func sortedVals(m map[string]string) []string { + // Return deterministic ordering of values from multi-key lists. + ks := make([]string, 0, len(m)) + for k := range m { + ks = append(ks, k) + } + sort.Strings(ks) + vs := make([]string, 0, len(m)) + for _, k := range ks { + vs = append(vs, m[k]) + } + return vs +} + +// CompletePath joins provided prefix and subscription paths. Also, verifies +// whether origin is set properly according to: +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/mixed-schema.md +// Note that this function doesn't add "openconfig" default origin if neither +// prefix nor path specifies the origin. Also, target field isn't prepended in +// the returned path. +func CompletePath(prefix, path *gpb.Path) ([]string, error) { + oPre, oPath := prefix.GetOrigin(), path.GetOrigin() + + var fullPrefix []string + indexedPrefix := ToStrings(prefix, false) + switch { + case oPre != "" && oPath != "": + return nil, errors.New("origin is set both in prefix and path") + case oPre != "": + fullPrefix = append(fullPrefix, oPre) + fullPrefix = append(fullPrefix, indexedPrefix...) + case oPath != "": + if len(indexedPrefix) > 0 { + return nil, errors.New("path elements in prefix are set even though origin is set in path") + } + fullPrefix = append(fullPrefix, oPath) + default: + // Neither prefix nor path specified an origin. Include the path elements in prefix. + fullPrefix = append(fullPrefix, indexedPrefix...) + } + + return append(fullPrefix, ToStrings(path, false)...), nil +} diff --git a/deps/github.com/openconfig/gnmi/path/path_test.go b/deps/github.com/openconfig/gnmi/path/path_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ce2c0f105b0cac5a3b243615035cb09b3cf96338 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/path/path_test.go @@ -0,0 +1,199 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package path + +import ( + "reflect" + "testing" + + "github.com/openconfig/gnmi/errdiff" + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +func TestSortedValues(t *testing.T) { + tests := []struct { + inp map[string]string + exp []string + }{ + { + inp: map[string]string{}, + exp: []string{}, + }, + { + inp: map[string]string{"a": "1", "b": "2"}, + exp: []string{"1", "2"}, + }, + { + inp: map[string]string{"c": "1", "a": "2", "d": "3"}, + exp: []string{"2", "1", "3"}, + }, + } + + for _, tt := range tests { + ret := sortedVals(tt.inp) + if !reflect.DeepEqual(ret, tt.exp) { + t.Errorf("sortedVals(%v) = got %v, want %v", tt.inp, ret, tt.exp) + } + } +} + +func TestToStrings(t *testing.T) { + tests := []struct { + p *gpb.Path + wOd bool + e []string + }{ + // test nil gnmi.Path + { + e: []string{}, + }, + // test non-nil gnmi.Path + { + p: &gpb.Path{}, + e: []string{}, + }, + // test deprecated gnmi.Path.Element + { + p: &gpb.Path{ + Element: []string{"x", "y"}, + }, + e: []string{"x", "y"}, + }, + // test <target> is prepended into index string list + { + p: &gpb.Path{ + Elem: []*gpb.PathElem{ + &gpb.PathElem{Name: "a"}, + &gpb.PathElem{Name: "b", Key: map[string]string{"n": "c"}}, + &gpb.PathElem{Name: "d"}, + }, + Target: "t", + }, + e: []string{"t", "a", "b", "c", "d"}, + wOd: true, + }, + // test missing <target> isn't added to the index string list + { + p: &gpb.Path{ + Elem: []*gpb.PathElem{ + &gpb.PathElem{Name: "a"}, + &gpb.PathElem{Name: "b", Key: map[string]string{"n": "c"}}, + &gpb.PathElem{Name: "d"}, + }, + }, + e: []string{"a", "b", "c", "d"}, + wOd: true, + }, + // test both <target> and <origin> are prepended into index string list + { + p: &gpb.Path{ + Elem: []*gpb.PathElem{ + &gpb.PathElem{Name: "a"}, + &gpb.PathElem{Name: "b", Key: map[string]string{"n": "c"}}, + &gpb.PathElem{Name: "d"}, + }, + Target: "t", + Origin: "o", + }, + e: []string{"t", "o", "a", "b", "c", "d"}, + wOd: true, + }, + // test having multiple keys in one gnmi.PathElem + { + p: &gpb.Path{ + Elem: []*gpb.PathElem{ + &gpb.PathElem{Name: "a"}, + &gpb.PathElem{Name: "b", Key: map[string]string{"b": "2", "a": "1", "c": "3"}}, + &gpb.PathElem{Name: "c"}, + }, + }, + e: []string{"a", "b", "1", "2", "3", "c"}, + }, + // multiple gnmi.PathElems have multiple keys in them + { + p: &gpb.Path{ + Elem: []*gpb.PathElem{ + &gpb.PathElem{Name: "a", Key: map[string]string{"l": "2", "k": "1"}}, + &gpb.PathElem{Name: "b", Key: map[string]string{"b": "2", "a": "1", "c": "3"}}, + &gpb.PathElem{Name: "c"}, + }, + }, + e: []string{"a", "1", "2", "b", "1", "2", "3", "c"}, + }, + // missign gnmi.PathElems, but have gnmi.Path.Element + { + p: &gpb.Path{ + Element: []string{"a", "b", "c", "d"}, + }, + e: []string{"a", "b", "c", "d"}, + }, + } + + for _, tt := range tests { + r := ToStrings(tt.p, tt.wOd) + if !reflect.DeepEqual(r, tt.e) { + t.Errorf("ToStrings(%v) = got %v, want %v", tt.p, r, tt.e) + } + } +} + +func TestCompletePath(t *testing.T) { + tests := []struct { + desc string + inPrefix *gpb.Path + inPath *gpb.Path + wantSlice []string + wantErrSubstr string + }{ + { + desc: "origin is just set in prefix", + inPrefix: &gpb.Path{Target: "t", Origin: "o"}, + wantSlice: []string{"o"}, + }, + { + desc: "origin is set both in prefix and path", + inPrefix: &gpb.Path{Target: "t", Origin: "o"}, + inPath: &gpb.Path{Origin: "o"}, + wantErrSubstr: "origin is set both in prefix and path", + }, + { + desc: "origin is set in path, but prefix has path elements", + inPrefix: &gpb.Path{Target: "t", Elem: []*gpb.PathElem{{Name: "e"}}}, + inPath: &gpb.Path{Origin: "o"}, + wantErrSubstr: "path elements in prefix are set even though origin is set in path", + }, + { + desc: "origin is set in path properly", + inPrefix: &gpb.Path{Target: "t"}, + inPath: &gpb.Path{Origin: "o", Elem: []*gpb.PathElem{{Name: "e"}}}, + wantSlice: []string{"o", "e"}, + }, + } + + for _, tt := range tests { + gotSlice, err := CompletePath(tt.inPrefix, tt.inPath) + if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" { + t.Errorf("%s: %v", tt.desc, diff) + } + if err != nil { + continue + } + if !reflect.DeepEqual(tt.wantSlice, gotSlice) { + t.Errorf("%s: got %v, want %v", tt.desc, gotSlice, tt.wantSlice) + } + } +} diff --git a/deps/github.com/openconfig/gnmi/proto/collector/BUILD.bazel b/deps/github.com/openconfig/gnmi/proto/collector/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..2ae4cb06c98788ee46bac9bb2543c819e895907d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/collector/BUILD.bazel @@ -0,0 +1,40 @@ +#Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Supporting infrastructure for implementing and testing PINS. +# + +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +proto_library( + name = "collector_proto", + srcs = ["collector.proto"], +) + +cc_proto_library( + name = "collector_cc_proto", + deps = [":collector_proto"], +) + +cc_grpc_library( + name = "collector_cc_grpc_proto", + srcs = [":collector_proto"], + grpc_only = True, + deps = [":collector_cc_proto"], +) diff --git a/deps/github.com/openconfig/gnmi/proto/collector/collector.pb.go b/deps/github.com/openconfig/gnmi/proto/collector/collector.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..28a75998ec7c78f48ff448224536fca69d3abc5c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/collector/collector.pb.go @@ -0,0 +1,220 @@ +// +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.12.4 +// source: github.com/openconfig/gnmi/proto/collector/collector.proto + +package gnmi + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ReconnectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Target []string `protobuf:"bytes,1,rep,name=target,proto3" json:"target,omitempty"` // List of targets to reconnect. +} + +func (x *ReconnectRequest) Reset() { + *x = ReconnectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReconnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReconnectRequest) ProtoMessage() {} + +func (x *ReconnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReconnectRequest.ProtoReflect.Descriptor instead. +func (*ReconnectRequest) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescGZIP(), []int{0} +} + +func (x *ReconnectRequest) GetTarget() []string { + if x != nil { + return x.Target + } + return nil +} + +type Nil struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Nil) Reset() { + *x = Nil{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Nil) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Nil) ProtoMessage() {} + +func (x *Nil) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Nil.ProtoReflect.Descriptor instead. +func (*Nil) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescGZIP(), []int{1} +} + +var File_github_com_openconfig_gnmi_proto_collector_collector_proto protoreflect.FileDescriptor + +var file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDesc = []byte{ + 0x0a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x67, 0x6e, + 0x6d, 0x69, 0x22, 0x2a, 0x0a, 0x10, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x05, + 0x0a, 0x03, 0x4e, 0x69, 0x6c, 0x32, 0x3d, 0x0a, 0x09, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, + 0x16, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x4e, + 0x69, 0x6c, 0x22, 0x00, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, + 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x3b, 0x67, 0x6e, 0x6d, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescOnce sync.Once + file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescData = file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDesc +) + +func file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescGZIP() []byte { + file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescOnce.Do(func() { + file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescData) + }) + return file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDescData +} + +var file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_github_com_openconfig_gnmi_proto_collector_collector_proto_goTypes = []interface{}{ + (*ReconnectRequest)(nil), // 0: gnmi.ReconnectRequest + (*Nil)(nil), // 1: gnmi.Nil +} +var file_github_com_openconfig_gnmi_proto_collector_collector_proto_depIdxs = []int32{ + 0, // 0: gnmi.Collector.Reconnect:input_type -> gnmi.ReconnectRequest + 1, // 1: gnmi.Collector.Reconnect:output_type -> gnmi.Nil + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_github_com_openconfig_gnmi_proto_collector_collector_proto_init() } +func file_github_com_openconfig_gnmi_proto_collector_collector_proto_init() { + if File_github_com_openconfig_gnmi_proto_collector_collector_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReconnectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Nil); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_github_com_openconfig_gnmi_proto_collector_collector_proto_goTypes, + DependencyIndexes: file_github_com_openconfig_gnmi_proto_collector_collector_proto_depIdxs, + MessageInfos: file_github_com_openconfig_gnmi_proto_collector_collector_proto_msgTypes, + }.Build() + File_github_com_openconfig_gnmi_proto_collector_collector_proto = out.File + file_github_com_openconfig_gnmi_proto_collector_collector_proto_rawDesc = nil + file_github_com_openconfig_gnmi_proto_collector_collector_proto_goTypes = nil + file_github_com_openconfig_gnmi_proto_collector_collector_proto_depIdxs = nil +} diff --git a/deps/github.com/openconfig/gnmi/proto/collector/collector.proto b/deps/github.com/openconfig/gnmi/proto/collector/collector.proto new file mode 100644 index 0000000000000000000000000000000000000000..db2c195998d7a01ce8d839d62880cd3199981cc5 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/collector/collector.proto @@ -0,0 +1,32 @@ +// +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +syntax = "proto3"; + +package gnmi; + +option go_package = "github.com/openconfig/gnmi/proto/collector;gnmi"; + +service Collector { + // Reconnect requests that the existing connections for one or more specified + // targets will be stopped and new connections established. + rpc Reconnect(ReconnectRequest) returns (Nil) {} +} + +message ReconnectRequest { + repeated string target = 1; // List of targets to reconnect. +} + +message Nil{} diff --git a/deps/github.com/openconfig/gnmi/proto/collector/collector_grpc.pb.go b/deps/github.com/openconfig/gnmi/proto/collector/collector_grpc.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..fce75c79f7ba7a57c4130ad489357dda186f6f45 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/collector/collector_grpc.pb.go @@ -0,0 +1,103 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package gnmi + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// CollectorClient is the client API for Collector service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CollectorClient interface { + // Reconnect requests that the existing connections for one or more specified + // targets will be stopped and new connections established. + Reconnect(ctx context.Context, in *ReconnectRequest, opts ...grpc.CallOption) (*Nil, error) +} + +type collectorClient struct { + cc grpc.ClientConnInterface +} + +func NewCollectorClient(cc grpc.ClientConnInterface) CollectorClient { + return &collectorClient{cc} +} + +func (c *collectorClient) Reconnect(ctx context.Context, in *ReconnectRequest, opts ...grpc.CallOption) (*Nil, error) { + out := new(Nil) + err := c.cc.Invoke(ctx, "/gnmi.Collector/Reconnect", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CollectorServer is the server API for Collector service. +// All implementations should embed UnimplementedCollectorServer +// for forward compatibility +type CollectorServer interface { + // Reconnect requests that the existing connections for one or more specified + // targets will be stopped and new connections established. + Reconnect(context.Context, *ReconnectRequest) (*Nil, error) +} + +// UnimplementedCollectorServer should be embedded to have forward compatible implementations. +type UnimplementedCollectorServer struct { +} + +func (UnimplementedCollectorServer) Reconnect(context.Context, *ReconnectRequest) (*Nil, error) { + return nil, status.Errorf(codes.Unimplemented, "method Reconnect not implemented") +} + +// UnsafeCollectorServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CollectorServer will +// result in compilation errors. +type UnsafeCollectorServer interface { + mustEmbedUnimplementedCollectorServer() +} + +func RegisterCollectorServer(s grpc.ServiceRegistrar, srv CollectorServer) { + s.RegisterService(&Collector_ServiceDesc, srv) +} + +func _Collector_Reconnect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReconnectRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CollectorServer).Reconnect(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.Collector/Reconnect", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CollectorServer).Reconnect(ctx, req.(*ReconnectRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Collector_ServiceDesc is the grpc.ServiceDesc for Collector service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Collector_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gnmi.Collector", + HandlerType: (*CollectorServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Reconnect", + Handler: _Collector_Reconnect_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/openconfig/gnmi/proto/collector/collector.proto", +} diff --git a/deps/github.com/openconfig/gnmi/proto/collector/collector_pb2.py b/deps/github.com/openconfig/gnmi/proto/collector/collector_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..a6b1a9be7671c411c0334af60cb86f0c7dfda56a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/collector/collector_pb2.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/collector/collector.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='proto/collector/collector.proto', + package='gnmi', + syntax='proto3', + serialized_options=b'Z/github.com/openconfig/gnmi/proto/collector;gnmi', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x1fproto/collector/collector.proto\x12\x04gnmi\"\"\n\x10ReconnectRequest\x12\x0e\n\x06target\x18\x01 \x03(\t\"\x05\n\x03Nil2=\n\tCollector\x12\x30\n\tReconnect\x12\x16.gnmi.ReconnectRequest\x1a\t.gnmi.Nil\"\x00\x42\x31Z/github.com/openconfig/gnmi/proto/collector;gnmib\x06proto3' +) + + + + +_RECONNECTREQUEST = _descriptor.Descriptor( + name='ReconnectRequest', + full_name='gnmi.ReconnectRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='target', full_name='gnmi.ReconnectRequest.target', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=41, + serialized_end=75, +) + + +_NIL = _descriptor.Descriptor( + name='Nil', + full_name='gnmi.Nil', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=77, + serialized_end=82, +) + +DESCRIPTOR.message_types_by_name['ReconnectRequest'] = _RECONNECTREQUEST +DESCRIPTOR.message_types_by_name['Nil'] = _NIL +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ReconnectRequest = _reflection.GeneratedProtocolMessageType('ReconnectRequest', (_message.Message,), { + 'DESCRIPTOR' : _RECONNECTREQUEST, + '__module__' : 'proto.collector.collector_pb2' + # @@protoc_insertion_point(class_scope:gnmi.ReconnectRequest) + }) +_sym_db.RegisterMessage(ReconnectRequest) + +Nil = _reflection.GeneratedProtocolMessageType('Nil', (_message.Message,), { + 'DESCRIPTOR' : _NIL, + '__module__' : 'proto.collector.collector_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Nil) + }) +_sym_db.RegisterMessage(Nil) + + +DESCRIPTOR._options = None + +_COLLECTOR = _descriptor.ServiceDescriptor( + name='Collector', + full_name='gnmi.Collector', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=84, + serialized_end=145, + methods=[ + _descriptor.MethodDescriptor( + name='Reconnect', + full_name='gnmi.Collector.Reconnect', + index=0, + containing_service=None, + input_type=_RECONNECTREQUEST, + output_type=_NIL, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_COLLECTOR) + +DESCRIPTOR.services_by_name['Collector'] = _COLLECTOR + +# @@protoc_insertion_point(module_scope) diff --git a/deps/github.com/openconfig/gnmi/proto/collector/collector_pb2_grpc.py b/deps/github.com/openconfig/gnmi/proto/collector/collector_pb2_grpc.py new file mode 100644 index 0000000000000000000000000000000000000000..cc93c0a9506ad162036316778ea7b7f02b87848a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/collector/collector_pb2_grpc.py @@ -0,0 +1,68 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from proto.collector import collector_pb2 as proto_dot_collector_dot_collector__pb2 + + +class CollectorStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Reconnect = channel.unary_unary( + '/gnmi.Collector/Reconnect', + request_serializer=proto_dot_collector_dot_collector__pb2.ReconnectRequest.SerializeToString, + response_deserializer=proto_dot_collector_dot_collector__pb2.Nil.FromString, + ) + + +class CollectorServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Reconnect(self, request, context): + """Reconnect requests that the existing connections for one or more specified + targets will be stopped and new connections established. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_CollectorServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Reconnect': grpc.unary_unary_rpc_method_handler( + servicer.Reconnect, + request_deserializer=proto_dot_collector_dot_collector__pb2.ReconnectRequest.FromString, + response_serializer=proto_dot_collector_dot_collector__pb2.Nil.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'gnmi.Collector', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class Collector(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Reconnect(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.Collector/Reconnect', + proto_dot_collector_dot_collector__pb2.ReconnectRequest.SerializeToString, + proto_dot_collector_dot_collector__pb2.Nil.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi/BUILD.bazel b/deps/github.com/openconfig/gnmi/proto/gnmi/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..f471488c95f4938b2ea9c101e948f4de836bbb6c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi/BUILD.bazel @@ -0,0 +1,47 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Supporting infrastructure for implementing and testing PINS. +# + +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +proto_library( + name = "gnmi_proto", + srcs = ["gnmi.proto"], + import_prefix = "github.com/openconfig/gnmi", + deps = [ + "//proto/gnmi_ext:gnmi_ext_proto", + "@com_google_protobuf//:any_proto", + "@com_google_protobuf//:descriptor_proto", + ], +) + +cc_proto_library( + name = "gnmi_cc_proto", + deps = [":gnmi_proto"], +) + +cc_grpc_library( + name = "gnmi_cc_grpc_proto", + srcs = [":gnmi_proto"], + generate_mocks = True, + grpc_only = True, + deps = [":gnmi_cc_proto"], +) diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi.pb.go b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..43914051230becff6a15db63aa148e96b1452505 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi.pb.go @@ -0,0 +1,3197 @@ +// +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.12.4 +// source: github.com/openconfig/gnmi/proto/gnmi/gnmi.proto + +// Package gNMI defines a service specification for the gRPC Network Management +// Interface. This interface is defined to be a standard interface via which +// a network management system ("client") can subscribe to state values, +// retrieve snapshots of state information, and manipulate the state of a data +// tree supported by a device ("target"). +// +// This document references the gNMI Specification which can be found at +// http://github.com/openconfig/reference/blob/master/rpc/gnmi + +package gnmi + +import ( + descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" + any "github.com/golang/protobuf/ptypes/any" + gnmi_ext "github.com/openconfig/gnmi/proto/gnmi_ext" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Encoding defines the value encoding formats that are supported by the gNMI +// protocol. These encodings are used by both the client (when sending Set +// messages to modify the state of the target) and the target when serializing +// data to be returned to the client (in both Subscribe and Get RPCs). +// Reference: gNMI Specification Section 2.3 +type Encoding int32 + +const ( + Encoding_JSON Encoding = 0 // JSON encoded text. + Encoding_BYTES Encoding = 1 // Arbitrarily encoded bytes. + Encoding_PROTO Encoding = 2 // Encoded according to out-of-band agreed Protobuf. + Encoding_ASCII Encoding = 3 // ASCII text of an out-of-band agreed format. + Encoding_JSON_IETF Encoding = 4 // JSON encoded text as per RFC7951. +) + +// Enum value maps for Encoding. +var ( + Encoding_name = map[int32]string{ + 0: "JSON", + 1: "BYTES", + 2: "PROTO", + 3: "ASCII", + 4: "JSON_IETF", + } + Encoding_value = map[string]int32{ + "JSON": 0, + "BYTES": 1, + "PROTO": 2, + "ASCII": 3, + "JSON_IETF": 4, + } +) + +func (x Encoding) Enum() *Encoding { + p := new(Encoding) + *p = x + return p +} + +func (x Encoding) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Encoding) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[0].Descriptor() +} + +func (Encoding) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[0] +} + +func (x Encoding) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Encoding.Descriptor instead. +func (Encoding) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{0} +} + +// SubscriptionMode is the mode of the subscription, specifying how the +// target must return values in a subscription. +// Reference: gNMI Specification Section 3.5.1.3 +type SubscriptionMode int32 + +const ( + SubscriptionMode_TARGET_DEFINED SubscriptionMode = 0 // The target selects the relevant mode for each element. + SubscriptionMode_ON_CHANGE SubscriptionMode = 1 // The target sends an update on element value change. + SubscriptionMode_SAMPLE SubscriptionMode = 2 // The target samples values according to the interval. +) + +// Enum value maps for SubscriptionMode. +var ( + SubscriptionMode_name = map[int32]string{ + 0: "TARGET_DEFINED", + 1: "ON_CHANGE", + 2: "SAMPLE", + } + SubscriptionMode_value = map[string]int32{ + "TARGET_DEFINED": 0, + "ON_CHANGE": 1, + "SAMPLE": 2, + } +) + +func (x SubscriptionMode) Enum() *SubscriptionMode { + p := new(SubscriptionMode) + *p = x + return p +} + +func (x SubscriptionMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SubscriptionMode) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[1].Descriptor() +} + +func (SubscriptionMode) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[1] +} + +func (x SubscriptionMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SubscriptionMode.Descriptor instead. +func (SubscriptionMode) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{1} +} + +// Mode of the subscription. +type SubscriptionList_Mode int32 + +const ( + SubscriptionList_STREAM SubscriptionList_Mode = 0 // Values streamed by the target (Sec. 3.5.1.5.2). + SubscriptionList_ONCE SubscriptionList_Mode = 1 // Values sent once-off by the target (Sec. 3.5.1.5.1). + SubscriptionList_POLL SubscriptionList_Mode = 2 // Values sent in response to a poll request (Sec. 3.5.1.5.3). +) + +// Enum value maps for SubscriptionList_Mode. +var ( + SubscriptionList_Mode_name = map[int32]string{ + 0: "STREAM", + 1: "ONCE", + 2: "POLL", + } + SubscriptionList_Mode_value = map[string]int32{ + "STREAM": 0, + "ONCE": 1, + "POLL": 2, + } +) + +func (x SubscriptionList_Mode) Enum() *SubscriptionList_Mode { + p := new(SubscriptionList_Mode) + *p = x + return p +} + +func (x SubscriptionList_Mode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SubscriptionList_Mode) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[2].Descriptor() +} + +func (SubscriptionList_Mode) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[2] +} + +func (x SubscriptionList_Mode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SubscriptionList_Mode.Descriptor instead. +func (SubscriptionList_Mode) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{12, 0} +} + +// The operation that was associated with the Path specified. +type UpdateResult_Operation int32 + +const ( + UpdateResult_INVALID UpdateResult_Operation = 0 + UpdateResult_DELETE UpdateResult_Operation = 1 // The result relates to a delete of Path. + UpdateResult_REPLACE UpdateResult_Operation = 2 // The result relates to a replace of Path. + UpdateResult_UPDATE UpdateResult_Operation = 3 // The result relates to an update of Path. +) + +// Enum value maps for UpdateResult_Operation. +var ( + UpdateResult_Operation_name = map[int32]string{ + 0: "INVALID", + 1: "DELETE", + 2: "REPLACE", + 3: "UPDATE", + } + UpdateResult_Operation_value = map[string]int32{ + "INVALID": 0, + "DELETE": 1, + "REPLACE": 2, + "UPDATE": 3, + } +) + +func (x UpdateResult_Operation) Enum() *UpdateResult_Operation { + p := new(UpdateResult_Operation) + *p = x + return p +} + +func (x UpdateResult_Operation) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UpdateResult_Operation) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[3].Descriptor() +} + +func (UpdateResult_Operation) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[3] +} + +func (x UpdateResult_Operation) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UpdateResult_Operation.Descriptor instead. +func (UpdateResult_Operation) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{19, 0} +} + +// Type of elements within the data tree. +type GetRequest_DataType int32 + +const ( + GetRequest_ALL GetRequest_DataType = 0 // All data elements. + GetRequest_CONFIG GetRequest_DataType = 1 // Config (rw) only elements. + GetRequest_STATE GetRequest_DataType = 2 // State (ro) only elements. + // Data elements marked in the schema as operational. This refers to data + // elements whose value relates to the state of processes or interactions + // running on the device. + GetRequest_OPERATIONAL GetRequest_DataType = 3 +) + +// Enum value maps for GetRequest_DataType. +var ( + GetRequest_DataType_name = map[int32]string{ + 0: "ALL", + 1: "CONFIG", + 2: "STATE", + 3: "OPERATIONAL", + } + GetRequest_DataType_value = map[string]int32{ + "ALL": 0, + "CONFIG": 1, + "STATE": 2, + "OPERATIONAL": 3, + } +) + +func (x GetRequest_DataType) Enum() *GetRequest_DataType { + p := new(GetRequest_DataType) + *p = x + return p +} + +func (x GetRequest_DataType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetRequest_DataType) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[4].Descriptor() +} + +func (GetRequest_DataType) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes[4] +} + +func (x GetRequest_DataType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetRequest_DataType.Descriptor instead. +func (GetRequest_DataType) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{20, 0} +} + +// Notification is a re-usable message that is used to encode data from the +// target to the client. A Notification carries two types of changes to the data +// tree: +// - Deleted values (delete) - a set of paths that have been removed from the +// data tree. +// - Updated values (update) - a set of path-value pairs indicating the path +// whose value has changed in the data tree. +// Reference: gNMI Specification Section 2.1 +type Notification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Timestamp in nanoseconds since Epoch. + Prefix *Path `protobuf:"bytes,2,opt,name=prefix,proto3" json:"prefix,omitempty"` // Prefix used for paths in the message. + // An alias for the path specified in the prefix field. + // Reference: gNMI Specification Section 2.4.2 + Alias string `protobuf:"bytes,3,opt,name=alias,proto3" json:"alias,omitempty"` + Update []*Update `protobuf:"bytes,4,rep,name=update,proto3" json:"update,omitempty"` // Data elements that have changed values. + Delete []*Path `protobuf:"bytes,5,rep,name=delete,proto3" json:"delete,omitempty"` // Data elements that have been deleted. + // This notification contains a set of paths that are always updated together + // referenced by a globally unique prefix. + Atomic bool `protobuf:"varint,6,opt,name=atomic,proto3" json:"atomic,omitempty"` +} + +func (x *Notification) Reset() { + *x = Notification{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Notification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Notification) ProtoMessage() {} + +func (x *Notification) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Notification.ProtoReflect.Descriptor instead. +func (*Notification) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{0} +} + +func (x *Notification) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Notification) GetPrefix() *Path { + if x != nil { + return x.Prefix + } + return nil +} + +func (x *Notification) GetAlias() string { + if x != nil { + return x.Alias + } + return "" +} + +func (x *Notification) GetUpdate() []*Update { + if x != nil { + return x.Update + } + return nil +} + +func (x *Notification) GetDelete() []*Path { + if x != nil { + return x.Delete + } + return nil +} + +func (x *Notification) GetAtomic() bool { + if x != nil { + return x.Atomic + } + return false +} + +// Update is a re-usable message that is used to store a particular Path, +// Value pair. +// Reference: gNMI Specification Section 2.1 +type Update struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path *Path `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // The path (key) for the update. + // Deprecated: Do not use. + Value *Value `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` // The value (value) for the update. + Val *TypedValue `protobuf:"bytes,3,opt,name=val,proto3" json:"val,omitempty"` // The explicitly typed update value. + Duplicates uint32 `protobuf:"varint,4,opt,name=duplicates,proto3" json:"duplicates,omitempty"` // Number of coalesced duplicates. +} + +func (x *Update) Reset() { + *x = Update{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Update) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Update) ProtoMessage() {} + +func (x *Update) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Update.ProtoReflect.Descriptor instead. +func (*Update) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{1} +} + +func (x *Update) GetPath() *Path { + if x != nil { + return x.Path + } + return nil +} + +// Deprecated: Do not use. +func (x *Update) GetValue() *Value { + if x != nil { + return x.Value + } + return nil +} + +func (x *Update) GetVal() *TypedValue { + if x != nil { + return x.Val + } + return nil +} + +func (x *Update) GetDuplicates() uint32 { + if x != nil { + return x.Duplicates + } + return 0 +} + +// TypedValue is used to encode a value being sent between the client and +// target (originated by either entity). +type TypedValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // One of the fields within the val oneof is populated with the value + // of the update. The type of the value being included in the Update + // determines which field should be populated. In the case that the + // encoding is a particular form of the base protobuf type, a specific + // field is used to store the value (e.g., json_val). + // + // Types that are assignable to Value: + // *TypedValue_StringVal + // *TypedValue_IntVal + // *TypedValue_UintVal + // *TypedValue_BoolVal + // *TypedValue_BytesVal + // *TypedValue_FloatVal + // *TypedValue_DecimalVal + // *TypedValue_LeaflistVal + // *TypedValue_AnyVal + // *TypedValue_JsonVal + // *TypedValue_JsonIetfVal + // *TypedValue_AsciiVal + // *TypedValue_ProtoBytes + Value isTypedValue_Value `protobuf_oneof:"value"` +} + +func (x *TypedValue) Reset() { + *x = TypedValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TypedValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TypedValue) ProtoMessage() {} + +func (x *TypedValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TypedValue.ProtoReflect.Descriptor instead. +func (*TypedValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{2} +} + +func (m *TypedValue) GetValue() isTypedValue_Value { + if m != nil { + return m.Value + } + return nil +} + +func (x *TypedValue) GetStringVal() string { + if x, ok := x.GetValue().(*TypedValue_StringVal); ok { + return x.StringVal + } + return "" +} + +func (x *TypedValue) GetIntVal() int64 { + if x, ok := x.GetValue().(*TypedValue_IntVal); ok { + return x.IntVal + } + return 0 +} + +func (x *TypedValue) GetUintVal() uint64 { + if x, ok := x.GetValue().(*TypedValue_UintVal); ok { + return x.UintVal + } + return 0 +} + +func (x *TypedValue) GetBoolVal() bool { + if x, ok := x.GetValue().(*TypedValue_BoolVal); ok { + return x.BoolVal + } + return false +} + +func (x *TypedValue) GetBytesVal() []byte { + if x, ok := x.GetValue().(*TypedValue_BytesVal); ok { + return x.BytesVal + } + return nil +} + +func (x *TypedValue) GetFloatVal() float32 { + if x, ok := x.GetValue().(*TypedValue_FloatVal); ok { + return x.FloatVal + } + return 0 +} + +func (x *TypedValue) GetDecimalVal() *Decimal64 { + if x, ok := x.GetValue().(*TypedValue_DecimalVal); ok { + return x.DecimalVal + } + return nil +} + +func (x *TypedValue) GetLeaflistVal() *ScalarArray { + if x, ok := x.GetValue().(*TypedValue_LeaflistVal); ok { + return x.LeaflistVal + } + return nil +} + +func (x *TypedValue) GetAnyVal() *any.Any { + if x, ok := x.GetValue().(*TypedValue_AnyVal); ok { + return x.AnyVal + } + return nil +} + +func (x *TypedValue) GetJsonVal() []byte { + if x, ok := x.GetValue().(*TypedValue_JsonVal); ok { + return x.JsonVal + } + return nil +} + +func (x *TypedValue) GetJsonIetfVal() []byte { + if x, ok := x.GetValue().(*TypedValue_JsonIetfVal); ok { + return x.JsonIetfVal + } + return nil +} + +func (x *TypedValue) GetAsciiVal() string { + if x, ok := x.GetValue().(*TypedValue_AsciiVal); ok { + return x.AsciiVal + } + return "" +} + +func (x *TypedValue) GetProtoBytes() []byte { + if x, ok := x.GetValue().(*TypedValue_ProtoBytes); ok { + return x.ProtoBytes + } + return nil +} + +type isTypedValue_Value interface { + isTypedValue_Value() +} + +type TypedValue_StringVal struct { + StringVal string `protobuf:"bytes,1,opt,name=string_val,json=stringVal,proto3,oneof"` // String value. +} + +type TypedValue_IntVal struct { + IntVal int64 `protobuf:"varint,2,opt,name=int_val,json=intVal,proto3,oneof"` // Integer value. +} + +type TypedValue_UintVal struct { + UintVal uint64 `protobuf:"varint,3,opt,name=uint_val,json=uintVal,proto3,oneof"` // Unsigned integer value. +} + +type TypedValue_BoolVal struct { + BoolVal bool `protobuf:"varint,4,opt,name=bool_val,json=boolVal,proto3,oneof"` // Bool value. +} + +type TypedValue_BytesVal struct { + BytesVal []byte `protobuf:"bytes,5,opt,name=bytes_val,json=bytesVal,proto3,oneof"` // Arbitrary byte sequence value. +} + +type TypedValue_FloatVal struct { + FloatVal float32 `protobuf:"fixed32,6,opt,name=float_val,json=floatVal,proto3,oneof"` // Floating point value. +} + +type TypedValue_DecimalVal struct { + DecimalVal *Decimal64 `protobuf:"bytes,7,opt,name=decimal_val,json=decimalVal,proto3,oneof"` // Decimal64 encoded value. +} + +type TypedValue_LeaflistVal struct { + LeaflistVal *ScalarArray `protobuf:"bytes,8,opt,name=leaflist_val,json=leaflistVal,proto3,oneof"` // Mixed type scalar array value. +} + +type TypedValue_AnyVal struct { + AnyVal *any.Any `protobuf:"bytes,9,opt,name=any_val,json=anyVal,proto3,oneof"` // protobuf.Any encoded bytes. +} + +type TypedValue_JsonVal struct { + JsonVal []byte `protobuf:"bytes,10,opt,name=json_val,json=jsonVal,proto3,oneof"` // JSON-encoded text. +} + +type TypedValue_JsonIetfVal struct { + JsonIetfVal []byte `protobuf:"bytes,11,opt,name=json_ietf_val,json=jsonIetfVal,proto3,oneof"` // JSON-encoded text per RFC7951. +} + +type TypedValue_AsciiVal struct { + AsciiVal string `protobuf:"bytes,12,opt,name=ascii_val,json=asciiVal,proto3,oneof"` // Arbitrary ASCII text. +} + +type TypedValue_ProtoBytes struct { + // Protobuf binary encoded bytes. The message type is not included. + // See the specification at + // github.com/openconfig/reference/blob/master/rpc/gnmi/protobuf-vals.md + // for a complete specification. + ProtoBytes []byte `protobuf:"bytes,13,opt,name=proto_bytes,json=protoBytes,proto3,oneof"` +} + +func (*TypedValue_StringVal) isTypedValue_Value() {} + +func (*TypedValue_IntVal) isTypedValue_Value() {} + +func (*TypedValue_UintVal) isTypedValue_Value() {} + +func (*TypedValue_BoolVal) isTypedValue_Value() {} + +func (*TypedValue_BytesVal) isTypedValue_Value() {} + +func (*TypedValue_FloatVal) isTypedValue_Value() {} + +func (*TypedValue_DecimalVal) isTypedValue_Value() {} + +func (*TypedValue_LeaflistVal) isTypedValue_Value() {} + +func (*TypedValue_AnyVal) isTypedValue_Value() {} + +func (*TypedValue_JsonVal) isTypedValue_Value() {} + +func (*TypedValue_JsonIetfVal) isTypedValue_Value() {} + +func (*TypedValue_AsciiVal) isTypedValue_Value() {} + +func (*TypedValue_ProtoBytes) isTypedValue_Value() {} + +// Path encodes a data tree path as a series of repeated strings, with +// each element of the path representing a data tree node name and the +// associated attributes. +// Reference: gNMI Specification Section 2.2.2. +type Path struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Elements of the path are no longer encoded as a string, but rather within + // the elem field as a PathElem message. + // + // Deprecated: Do not use. + Element []string `protobuf:"bytes,1,rep,name=element,proto3" json:"element,omitempty"` + Origin string `protobuf:"bytes,2,opt,name=origin,proto3" json:"origin,omitempty"` // Label to disambiguate path. + Elem []*PathElem `protobuf:"bytes,3,rep,name=elem,proto3" json:"elem,omitempty"` // Elements of the path. + Target string `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` // The name of the target +} + +func (x *Path) Reset() { + *x = Path{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Path) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Path) ProtoMessage() {} + +func (x *Path) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Path.ProtoReflect.Descriptor instead. +func (*Path) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{3} +} + +// Deprecated: Do not use. +func (x *Path) GetElement() []string { + if x != nil { + return x.Element + } + return nil +} + +func (x *Path) GetOrigin() string { + if x != nil { + return x.Origin + } + return "" +} + +func (x *Path) GetElem() []*PathElem { + if x != nil { + return x.Elem + } + return nil +} + +func (x *Path) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +// PathElem encodes an element of a gNMI path, along with any attributes (keys) +// that may be associated with it. +// Reference: gNMI Specification Section 2.2.2. +type PathElem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The name of the element in the path. + Key map[string]string `protobuf:"bytes,2,rep,name=key,proto3" json:"key,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Map of key (attribute) name to value. +} + +func (x *PathElem) Reset() { + *x = PathElem{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PathElem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PathElem) ProtoMessage() {} + +func (x *PathElem) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PathElem.ProtoReflect.Descriptor instead. +func (*PathElem) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{4} +} + +func (x *PathElem) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PathElem) GetKey() map[string]string { + if x != nil { + return x.Key + } + return nil +} + +// Value encodes a data tree node's value - along with the way in which +// the value is encoded. This message is deprecated by gNMI 0.3.0. +// Reference: gNMI Specification Section 2.2.3. +// +// Deprecated: Do not use. +type Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` // Value of the variable being transmitted. + Type Encoding `protobuf:"varint,2,opt,name=type,proto3,enum=gnmi.Encoding" json:"type,omitempty"` // Encoding used for the value field. +} + +func (x *Value) Reset() { + *x = Value{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Value) ProtoMessage() {} + +func (x *Value) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Value.ProtoReflect.Descriptor instead. +func (*Value) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{5} +} + +func (x *Value) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *Value) GetType() Encoding { + if x != nil { + return x.Type + } + return Encoding_JSON +} + +// Error message previously utilised to return errors to the client. Deprecated +// in favour of using the google.golang.org/genproto/googleapis/rpc/status +// message in the RPC response. +// Reference: gNMI Specification Section 2.5 +// +// Deprecated: Do not use. +type Error struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // Canonical gRPC error code. + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // Human readable error. + Data *any.Any `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` // Optional additional information. +} + +func (x *Error) Reset() { + *x = Error{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Error) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Error) ProtoMessage() {} + +func (x *Error) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Error.ProtoReflect.Descriptor instead. +func (*Error) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{6} +} + +func (x *Error) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Error) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Error) GetData() *any.Any { + if x != nil { + return x.Data + } + return nil +} + +// Decimal64 is used to encode a fixed precision decimal number. The value +// is expressed as a set of digits with the precision specifying the +// number of digits following the decimal point in the digit set. +type Decimal64 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Digits int64 `protobuf:"varint,1,opt,name=digits,proto3" json:"digits,omitempty"` // Set of digits. + Precision uint32 `protobuf:"varint,2,opt,name=precision,proto3" json:"precision,omitempty"` // Number of digits following the decimal point. +} + +func (x *Decimal64) Reset() { + *x = Decimal64{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Decimal64) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Decimal64) ProtoMessage() {} + +func (x *Decimal64) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Decimal64.ProtoReflect.Descriptor instead. +func (*Decimal64) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{7} +} + +func (x *Decimal64) GetDigits() int64 { + if x != nil { + return x.Digits + } + return 0 +} + +func (x *Decimal64) GetPrecision() uint32 { + if x != nil { + return x.Precision + } + return 0 +} + +// ScalarArray is used to encode a mixed-type array of values. +type ScalarArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of elements within the array. Each TypedValue message should + // specify only elements that have a field identifier of 1-7 (i.e., the + // values are scalar values). + Element []*TypedValue `protobuf:"bytes,1,rep,name=element,proto3" json:"element,omitempty"` +} + +func (x *ScalarArray) Reset() { + *x = ScalarArray{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ScalarArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ScalarArray) ProtoMessage() {} + +func (x *ScalarArray) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ScalarArray.ProtoReflect.Descriptor instead. +func (*ScalarArray) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{8} +} + +func (x *ScalarArray) GetElement() []*TypedValue { + if x != nil { + return x.Element + } + return nil +} + +// SubscribeRequest is the message sent by the client to the target when +// initiating a subscription to a set of paths within the data tree. The +// request field must be populated and the initial message must specify a +// SubscriptionList to initiate a subscription. The message is subsequently +// used to define aliases or trigger polled data to be sent by the target. +// Reference: gNMI Specification Section 3.5.1.1 +type SubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Request: + // *SubscribeRequest_Subscribe + // *SubscribeRequest_Poll + // *SubscribeRequest_Aliases + Request isSubscribeRequest_Request `protobuf_oneof:"request"` + // Extension messages associated with the SubscribeRequest. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,5,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *SubscribeRequest) Reset() { + *x = SubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeRequest) ProtoMessage() {} + +func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. +func (*SubscribeRequest) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{9} +} + +func (m *SubscribeRequest) GetRequest() isSubscribeRequest_Request { + if m != nil { + return m.Request + } + return nil +} + +func (x *SubscribeRequest) GetSubscribe() *SubscriptionList { + if x, ok := x.GetRequest().(*SubscribeRequest_Subscribe); ok { + return x.Subscribe + } + return nil +} + +func (x *SubscribeRequest) GetPoll() *Poll { + if x, ok := x.GetRequest().(*SubscribeRequest_Poll); ok { + return x.Poll + } + return nil +} + +func (x *SubscribeRequest) GetAliases() *AliasList { + if x, ok := x.GetRequest().(*SubscribeRequest_Aliases); ok { + return x.Aliases + } + return nil +} + +func (x *SubscribeRequest) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +type isSubscribeRequest_Request interface { + isSubscribeRequest_Request() +} + +type SubscribeRequest_Subscribe struct { + Subscribe *SubscriptionList `protobuf:"bytes,1,opt,name=subscribe,proto3,oneof"` // Specify the paths within a subscription. +} + +type SubscribeRequest_Poll struct { + Poll *Poll `protobuf:"bytes,3,opt,name=poll,proto3,oneof"` // Trigger a polled update. +} + +type SubscribeRequest_Aliases struct { + Aliases *AliasList `protobuf:"bytes,4,opt,name=aliases,proto3,oneof"` // Aliases to be created. +} + +func (*SubscribeRequest_Subscribe) isSubscribeRequest_Request() {} + +func (*SubscribeRequest_Poll) isSubscribeRequest_Request() {} + +func (*SubscribeRequest_Aliases) isSubscribeRequest_Request() {} + +// Poll is sent within a SubscribeRequest to trigger the device to +// send telemetry updates for the paths that are associated with the +// subscription. +// Reference: gNMI Specification Section Section 3.5.1.4 +type Poll struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Poll) Reset() { + *x = Poll{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Poll) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Poll) ProtoMessage() {} + +func (x *Poll) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Poll.ProtoReflect.Descriptor instead. +func (*Poll) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{10} +} + +// SubscribeResponse is the message used by the target within a Subscribe RPC. +// The target includes a Notification message which is used to transmit values +// of the path(s) that are associated with the subscription. The same message +// is to indicate that the target has sent all data values once (is +// synchronized). +// Reference: gNMI Specification Section 3.5.1.4 +type SubscribeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Response: + // *SubscribeResponse_Update + // *SubscribeResponse_SyncResponse + // *SubscribeResponse_Error + Response isSubscribeResponse_Response `protobuf_oneof:"response"` + // Extension messages associated with the SubscribeResponse. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,5,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *SubscribeResponse) Reset() { + *x = SubscribeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeResponse) ProtoMessage() {} + +func (x *SubscribeResponse) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead. +func (*SubscribeResponse) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{11} +} + +func (m *SubscribeResponse) GetResponse() isSubscribeResponse_Response { + if m != nil { + return m.Response + } + return nil +} + +func (x *SubscribeResponse) GetUpdate() *Notification { + if x, ok := x.GetResponse().(*SubscribeResponse_Update); ok { + return x.Update + } + return nil +} + +func (x *SubscribeResponse) GetSyncResponse() bool { + if x, ok := x.GetResponse().(*SubscribeResponse_SyncResponse); ok { + return x.SyncResponse + } + return false +} + +// Deprecated: Do not use. +func (x *SubscribeResponse) GetError() *Error { + if x, ok := x.GetResponse().(*SubscribeResponse_Error); ok { + return x.Error + } + return nil +} + +func (x *SubscribeResponse) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +type isSubscribeResponse_Response interface { + isSubscribeResponse_Response() +} + +type SubscribeResponse_Update struct { + Update *Notification `protobuf:"bytes,1,opt,name=update,proto3,oneof"` // Changed or sampled value for a path. +} + +type SubscribeResponse_SyncResponse struct { + // Indicate target has sent all values associated with the subscription + // at least once. + SyncResponse bool `protobuf:"varint,3,opt,name=sync_response,json=syncResponse,proto3,oneof"` +} + +type SubscribeResponse_Error struct { + // Deprecated in favour of google.golang.org/genproto/googleapis/rpc/status + // + // Deprecated: Do not use. + Error *Error `protobuf:"bytes,4,opt,name=error,proto3,oneof"` +} + +func (*SubscribeResponse_Update) isSubscribeResponse_Response() {} + +func (*SubscribeResponse_SyncResponse) isSubscribeResponse_Response() {} + +func (*SubscribeResponse_Error) isSubscribeResponse_Response() {} + +// SubscriptionList is used within a Subscribe message to specify the list of +// paths that the client wishes to subscribe to. The message consists of a +// list of (possibly prefixed) paths, and options that relate to the +// subscription. +// Reference: gNMI Specification Section 3.5.1.2 +type SubscriptionList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Prefix *Path `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` // Prefix used for paths. + Subscription []*Subscription `protobuf:"bytes,2,rep,name=subscription,proto3" json:"subscription,omitempty"` // Set of subscriptions to create. + // Whether target defined aliases are allowed within the subscription. + UseAliases bool `protobuf:"varint,3,opt,name=use_aliases,json=useAliases,proto3" json:"use_aliases,omitempty"` + Qos *QOSMarking `protobuf:"bytes,4,opt,name=qos,proto3" json:"qos,omitempty"` // DSCP marking to be used. + Mode SubscriptionList_Mode `protobuf:"varint,5,opt,name=mode,proto3,enum=gnmi.SubscriptionList_Mode" json:"mode,omitempty"` + // Whether elements of the schema that are marked as eligible for aggregation + // should be aggregated or not. + AllowAggregation bool `protobuf:"varint,6,opt,name=allow_aggregation,json=allowAggregation,proto3" json:"allow_aggregation,omitempty"` + // The set of schemas that define the elements of the data tree that should + // be sent by the target. + UseModels []*ModelData `protobuf:"bytes,7,rep,name=use_models,json=useModels,proto3" json:"use_models,omitempty"` + // The encoding that the target should use within the Notifications generated + // corresponding to the SubscriptionList. + Encoding Encoding `protobuf:"varint,8,opt,name=encoding,proto3,enum=gnmi.Encoding" json:"encoding,omitempty"` + // An optional field to specify that only updates to current state should be + // sent to a client. If set, the initial state is not sent to the client but + // rather only the sync message followed by any subsequent updates to the + // current state. For ONCE and POLL modes, this causes the server to send only + // the sync message (Sec. 3.5.2.3). + UpdatesOnly bool `protobuf:"varint,9,opt,name=updates_only,json=updatesOnly,proto3" json:"updates_only,omitempty"` +} + +func (x *SubscriptionList) Reset() { + *x = SubscriptionList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscriptionList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscriptionList) ProtoMessage() {} + +func (x *SubscriptionList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscriptionList.ProtoReflect.Descriptor instead. +func (*SubscriptionList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{12} +} + +func (x *SubscriptionList) GetPrefix() *Path { + if x != nil { + return x.Prefix + } + return nil +} + +func (x *SubscriptionList) GetSubscription() []*Subscription { + if x != nil { + return x.Subscription + } + return nil +} + +func (x *SubscriptionList) GetUseAliases() bool { + if x != nil { + return x.UseAliases + } + return false +} + +func (x *SubscriptionList) GetQos() *QOSMarking { + if x != nil { + return x.Qos + } + return nil +} + +func (x *SubscriptionList) GetMode() SubscriptionList_Mode { + if x != nil { + return x.Mode + } + return SubscriptionList_STREAM +} + +func (x *SubscriptionList) GetAllowAggregation() bool { + if x != nil { + return x.AllowAggregation + } + return false +} + +func (x *SubscriptionList) GetUseModels() []*ModelData { + if x != nil { + return x.UseModels + } + return nil +} + +func (x *SubscriptionList) GetEncoding() Encoding { + if x != nil { + return x.Encoding + } + return Encoding_JSON +} + +func (x *SubscriptionList) GetUpdatesOnly() bool { + if x != nil { + return x.UpdatesOnly + } + return false +} + +// Subscription is a single request within a SubscriptionList. The path +// specified is interpreted (along with the prefix) as the elements of the data +// tree that the client is subscribing to. The mode determines how the target +// should trigger updates to be sent. +// Reference: gNMI Specification Section 3.5.1.3 +type Subscription struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path *Path `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // The data tree path. + Mode SubscriptionMode `protobuf:"varint,2,opt,name=mode,proto3,enum=gnmi.SubscriptionMode" json:"mode,omitempty"` // Subscription mode to be used. + SampleInterval uint64 `protobuf:"varint,3,opt,name=sample_interval,json=sampleInterval,proto3" json:"sample_interval,omitempty"` // ns between samples in SAMPLE mode. + // Indicates whether values that have not changed should be sent in a SAMPLE + // subscription. + SuppressRedundant bool `protobuf:"varint,4,opt,name=suppress_redundant,json=suppressRedundant,proto3" json:"suppress_redundant,omitempty"` + // Specifies the maximum allowable silent period in nanoseconds when + // suppress_redundant is in use. The target should send a value at least once + // in the period specified. + HeartbeatInterval uint64 `protobuf:"varint,5,opt,name=heartbeat_interval,json=heartbeatInterval,proto3" json:"heartbeat_interval,omitempty"` +} + +func (x *Subscription) Reset() { + *x = Subscription{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Subscription) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Subscription) ProtoMessage() {} + +func (x *Subscription) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Subscription.ProtoReflect.Descriptor instead. +func (*Subscription) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{13} +} + +func (x *Subscription) GetPath() *Path { + if x != nil { + return x.Path + } + return nil +} + +func (x *Subscription) GetMode() SubscriptionMode { + if x != nil { + return x.Mode + } + return SubscriptionMode_TARGET_DEFINED +} + +func (x *Subscription) GetSampleInterval() uint64 { + if x != nil { + return x.SampleInterval + } + return 0 +} + +func (x *Subscription) GetSuppressRedundant() bool { + if x != nil { + return x.SuppressRedundant + } + return false +} + +func (x *Subscription) GetHeartbeatInterval() uint64 { + if x != nil { + return x.HeartbeatInterval + } + return 0 +} + +// QOSMarking specifies the DSCP value to be set on transmitted telemetry +// updates from the target. +// Reference: gNMI Specification Section 3.5.1.2 +type QOSMarking struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Marking uint32 `protobuf:"varint,1,opt,name=marking,proto3" json:"marking,omitempty"` +} + +func (x *QOSMarking) Reset() { + *x = QOSMarking{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QOSMarking) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QOSMarking) ProtoMessage() {} + +func (x *QOSMarking) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QOSMarking.ProtoReflect.Descriptor instead. +func (*QOSMarking) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{14} +} + +func (x *QOSMarking) GetMarking() uint32 { + if x != nil { + return x.Marking + } + return 0 +} + +// Alias specifies a data tree path, and an associated string which defines an +// alias which is to be used for this path in the context of the RPC. The alias +// is specified as a string which is prefixed with "#" to disambiguate it from +// data tree element paths. +// Reference: gNMI Specification Section 2.4.2 +type Alias struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path *Path `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // The path to be aliased. + Alias string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` // The alias value, a string prefixed by "#". +} + +func (x *Alias) Reset() { + *x = Alias{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Alias) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Alias) ProtoMessage() {} + +func (x *Alias) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Alias.ProtoReflect.Descriptor instead. +func (*Alias) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{15} +} + +func (x *Alias) GetPath() *Path { + if x != nil { + return x.Path + } + return nil +} + +func (x *Alias) GetAlias() string { + if x != nil { + return x.Alias + } + return "" +} + +// AliasList specifies a list of aliases. It is used in a SubscribeRequest for +// a client to create a set of aliases that the target is to utilize. +// Reference: gNMI Specification Section 3.5.1.6 +type AliasList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Alias []*Alias `protobuf:"bytes,1,rep,name=alias,proto3" json:"alias,omitempty"` // The set of aliases to be created. +} + +func (x *AliasList) Reset() { + *x = AliasList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AliasList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AliasList) ProtoMessage() {} + +func (x *AliasList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AliasList.ProtoReflect.Descriptor instead. +func (*AliasList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{16} +} + +func (x *AliasList) GetAlias() []*Alias { + if x != nil { + return x.Alias + } + return nil +} + +// SetRequest is sent from a client to the target to update values in the data +// tree. Paths are either deleted by the client, or modified by means of being +// updated, or replaced. Where a replace is used, unspecified values are +// considered to be replaced, whereas when update is used the changes are +// considered to be incremental. The set of changes that are specified within +// a single SetRequest are considered to be a transaction. +// Reference: gNMI Specification Section 3.4.1 +type SetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Prefix *Path `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` // Prefix used for paths in the message. + Delete []*Path `protobuf:"bytes,2,rep,name=delete,proto3" json:"delete,omitempty"` // Paths to be deleted from the data tree. + Replace []*Update `protobuf:"bytes,3,rep,name=replace,proto3" json:"replace,omitempty"` // Updates specifying elements to be replaced. + Update []*Update `protobuf:"bytes,4,rep,name=update,proto3" json:"update,omitempty"` // Updates specifying elements to updated. + // Extension messages associated with the SetRequest. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,5,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *SetRequest) Reset() { + *x = SetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRequest) ProtoMessage() {} + +func (x *SetRequest) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetRequest.ProtoReflect.Descriptor instead. +func (*SetRequest) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{17} +} + +func (x *SetRequest) GetPrefix() *Path { + if x != nil { + return x.Prefix + } + return nil +} + +func (x *SetRequest) GetDelete() []*Path { + if x != nil { + return x.Delete + } + return nil +} + +func (x *SetRequest) GetReplace() []*Update { + if x != nil { + return x.Replace + } + return nil +} + +func (x *SetRequest) GetUpdate() []*Update { + if x != nil { + return x.Update + } + return nil +} + +func (x *SetRequest) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +// SetResponse is the response to a SetRequest, sent from the target to the +// client. It reports the result of the modifications to the data tree that were +// specified by the client. Errors for this RPC should be reported using the +// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto +// message in the RPC return. The gnmi.Error message can be used to add additional +// details where required. +// Reference: gNMI Specification Section 3.4.2 +type SetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Prefix *Path `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` // Prefix used for paths. + // A set of responses specifying the result of the operations specified in + // the SetRequest. + Response []*UpdateResult `protobuf:"bytes,2,rep,name=response,proto3" json:"response,omitempty"` + // Deprecated: Do not use. + Message *Error `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` // The overall status of the transaction. + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Timestamp of transaction (ns since epoch). + // Extension messages associated with the SetResponse. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,5,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *SetResponse) Reset() { + *x = SetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetResponse) ProtoMessage() {} + +func (x *SetResponse) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetResponse.ProtoReflect.Descriptor instead. +func (*SetResponse) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{18} +} + +func (x *SetResponse) GetPrefix() *Path { + if x != nil { + return x.Prefix + } + return nil +} + +func (x *SetResponse) GetResponse() []*UpdateResult { + if x != nil { + return x.Response + } + return nil +} + +// Deprecated: Do not use. +func (x *SetResponse) GetMessage() *Error { + if x != nil { + return x.Message + } + return nil +} + +func (x *SetResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *SetResponse) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +// UpdateResult is used within the SetResponse message to communicate the +// result of an operation specified within a SetRequest message. +// Reference: gNMI Specification Section 3.4.2 +type UpdateResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Deprecated timestamp for the UpdateResult, this field has been + // replaced by the timestamp within the SetResponse message, since + // all mutations effected by a set should be applied as a single + // transaction. + // + // Deprecated: Do not use. + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Path *Path `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` // Path associated with the update. + // Deprecated: Do not use. + Message *Error `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` // Status of the update operation. + Op UpdateResult_Operation `protobuf:"varint,4,opt,name=op,proto3,enum=gnmi.UpdateResult_Operation" json:"op,omitempty"` // Update operation type. +} + +func (x *UpdateResult) Reset() { + *x = UpdateResult{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResult) ProtoMessage() {} + +func (x *UpdateResult) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateResult.ProtoReflect.Descriptor instead. +func (*UpdateResult) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{19} +} + +// Deprecated: Do not use. +func (x *UpdateResult) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *UpdateResult) GetPath() *Path { + if x != nil { + return x.Path + } + return nil +} + +// Deprecated: Do not use. +func (x *UpdateResult) GetMessage() *Error { + if x != nil { + return x.Message + } + return nil +} + +func (x *UpdateResult) GetOp() UpdateResult_Operation { + if x != nil { + return x.Op + } + return UpdateResult_INVALID +} + +// GetRequest is sent when a client initiates a Get RPC. It is used to specify +// the set of data elements for which the target should return a snapshot of +// data. The use_models field specifies the set of schema modules that are to +// be used by the target - where use_models is not specified then the target +// must use all schema models that it has. +// Reference: gNMI Specification Section 3.3.1 +type GetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Prefix *Path `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` // Prefix used for paths. + Path []*Path `protobuf:"bytes,2,rep,name=path,proto3" json:"path,omitempty"` // Paths requested by the client. + Type GetRequest_DataType `protobuf:"varint,3,opt,name=type,proto3,enum=gnmi.GetRequest_DataType" json:"type,omitempty"` // The type of data being requested. + Encoding Encoding `protobuf:"varint,5,opt,name=encoding,proto3,enum=gnmi.Encoding" json:"encoding,omitempty"` // Encoding to be used. + UseModels []*ModelData `protobuf:"bytes,6,rep,name=use_models,json=useModels,proto3" json:"use_models,omitempty"` // The schema models to be used. + // Extension messages associated with the GetRequest. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,7,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *GetRequest) Reset() { + *x = GetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRequest) ProtoMessage() {} + +func (x *GetRequest) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRequest.ProtoReflect.Descriptor instead. +func (*GetRequest) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{20} +} + +func (x *GetRequest) GetPrefix() *Path { + if x != nil { + return x.Prefix + } + return nil +} + +func (x *GetRequest) GetPath() []*Path { + if x != nil { + return x.Path + } + return nil +} + +func (x *GetRequest) GetType() GetRequest_DataType { + if x != nil { + return x.Type + } + return GetRequest_ALL +} + +func (x *GetRequest) GetEncoding() Encoding { + if x != nil { + return x.Encoding + } + return Encoding_JSON +} + +func (x *GetRequest) GetUseModels() []*ModelData { + if x != nil { + return x.UseModels + } + return nil +} + +func (x *GetRequest) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +// GetResponse is used by the target to respond to a GetRequest from a client. +// The set of Notifications corresponds to the data values that are requested +// by the client in the GetRequest. +// Reference: gNMI Specification Section 3.3.2 +type GetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Notification []*Notification `protobuf:"bytes,1,rep,name=notification,proto3" json:"notification,omitempty"` // Data values. + // Deprecated: Do not use. + Error *Error `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` // Errors that occurred in the Get. + // Extension messages associated with the GetResponse. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,3,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *GetResponse) Reset() { + *x = GetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetResponse) ProtoMessage() {} + +func (x *GetResponse) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetResponse.ProtoReflect.Descriptor instead. +func (*GetResponse) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{21} +} + +func (x *GetResponse) GetNotification() []*Notification { + if x != nil { + return x.Notification + } + return nil +} + +// Deprecated: Do not use. +func (x *GetResponse) GetError() *Error { + if x != nil { + return x.Error + } + return nil +} + +func (x *GetResponse) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +// CapabilityRequest is sent by the client in the Capabilities RPC to request +// that the target reports its capabilities. +// Reference: gNMI Specification Section 3.2.1 +type CapabilityRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Extension messages associated with the CapabilityRequest. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,1,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *CapabilityRequest) Reset() { + *x = CapabilityRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CapabilityRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CapabilityRequest) ProtoMessage() {} + +func (x *CapabilityRequest) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CapabilityRequest.ProtoReflect.Descriptor instead. +func (*CapabilityRequest) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{22} +} + +func (x *CapabilityRequest) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +// CapabilityResponse is used by the target to report its capabilities to the +// client within the Capabilities RPC. +// Reference: gNMI Specification Section 3.2.2 +type CapabilityResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SupportedModels []*ModelData `protobuf:"bytes,1,rep,name=supported_models,json=supportedModels,proto3" json:"supported_models,omitempty"` // Supported schema models. + SupportedEncodings []Encoding `protobuf:"varint,2,rep,packed,name=supported_encodings,json=supportedEncodings,proto3,enum=gnmi.Encoding" json:"supported_encodings,omitempty"` // Supported encodings. + GNMIVersion string `protobuf:"bytes,3,opt,name=gNMI_version,json=gNMIVersion,proto3" json:"gNMI_version,omitempty"` // Supported gNMI version. + // Extension messages associated with the CapabilityResponse. See the + // gNMI extension specification for further definition. + Extension []*gnmi_ext.Extension `protobuf:"bytes,4,rep,name=extension,proto3" json:"extension,omitempty"` +} + +func (x *CapabilityResponse) Reset() { + *x = CapabilityResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CapabilityResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CapabilityResponse) ProtoMessage() {} + +func (x *CapabilityResponse) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CapabilityResponse.ProtoReflect.Descriptor instead. +func (*CapabilityResponse) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{23} +} + +func (x *CapabilityResponse) GetSupportedModels() []*ModelData { + if x != nil { + return x.SupportedModels + } + return nil +} + +func (x *CapabilityResponse) GetSupportedEncodings() []Encoding { + if x != nil { + return x.SupportedEncodings + } + return nil +} + +func (x *CapabilityResponse) GetGNMIVersion() string { + if x != nil { + return x.GNMIVersion + } + return "" +} + +func (x *CapabilityResponse) GetExtension() []*gnmi_ext.Extension { + if x != nil { + return x.Extension + } + return nil +} + +// ModelData is used to describe a set of schema modules. It can be used in a +// CapabilityResponse where a target reports the set of modules that it +// supports, and within the SubscribeRequest and GetRequest messages to specify +// the set of models from which data tree elements should be reported. +// Reference: gNMI Specification Section 3.2.3 +type ModelData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Name of the model. + Organization string `protobuf:"bytes,2,opt,name=organization,proto3" json:"organization,omitempty"` // Organization publishing the model. + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` // Semantic version of the model. +} + +func (x *ModelData) Reset() { + *x = ModelData{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ModelData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ModelData) ProtoMessage() {} + +func (x *ModelData) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ModelData.ProtoReflect.Descriptor instead. +func (*ModelData) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP(), []int{24} +} + +func (x *ModelData) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ModelData) GetOrganization() string { + if x != nil { + return x.Organization + } + return "" +} + +func (x *ModelData) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +var file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*string)(nil), + Field: 1001, + Name: "gnmi.gnmi_service", + Tag: "bytes,1001,opt,name=gnmi_service", + Filename: "github.com/openconfig/gnmi/proto/gnmi/gnmi.proto", + }, +} + +// Extension fields to descriptor.FileOptions. +var ( + // The gNMI service semantic version. + // + // optional string gnmi_service = 1001; + E_GnmiService = &file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_extTypes[0] +) + +var File_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto protoreflect.FileDescriptor + +var file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x04, 0x67, 0x6e, 0x6d, 0x69, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, + 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, + 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xc8, 0x01, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x22, + 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x24, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x22, + 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x22, 0x93, 0x01, 0x0a, 0x06, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x22, 0x0a, 0x03, + 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x76, 0x61, 0x6c, + 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x22, 0xeb, 0x03, 0x0a, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x1f, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x12, 0x19, 0x0a, 0x07, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x06, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x08, 0x75, + 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, + 0x07, 0x75, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x08, 0x62, 0x6f, 0x6f, 0x6c, + 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x09, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x76, + 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x56, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x76, 0x61, + 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x48, 0x00, 0x52, 0x08, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x56, 0x61, 0x6c, 0x12, 0x32, 0x0a, 0x0b, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x76, + 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, + 0x44, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x36, 0x34, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x65, 0x63, + 0x69, 0x6d, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0c, 0x6c, 0x65, 0x61, 0x66, 0x6c, + 0x69, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x48, 0x00, 0x52, 0x0b, 0x6c, 0x65, 0x61, 0x66, 0x6c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x12, + 0x2f, 0x0a, 0x07, 0x61, 0x6e, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00, 0x52, 0x06, 0x61, 0x6e, 0x79, 0x56, 0x61, 0x6c, + 0x12, 0x1b, 0x0a, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x6a, 0x73, 0x6f, 0x6e, 0x56, 0x61, 0x6c, 0x12, 0x24, 0x0a, + 0x0d, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, 0x65, 0x74, 0x66, 0x5f, 0x76, 0x61, 0x6c, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x65, 0x74, 0x66, + 0x56, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x09, 0x61, 0x73, 0x63, 0x69, 0x69, 0x5f, 0x76, 0x61, 0x6c, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x61, 0x73, 0x63, 0x69, 0x69, 0x56, + 0x61, 0x6c, 0x12, 0x21, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x78, + 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x04, + 0x65, 0x6c, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x45, 0x6c, 0x65, 0x6d, 0x52, 0x04, 0x65, 0x6c, 0x65, 0x6d, + 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x81, 0x01, 0x0a, 0x08, 0x50, 0x61, 0x74, + 0x68, 0x45, 0x6c, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, + 0x74, 0x68, 0x45, 0x6c, 0x65, 0x6d, 0x2e, 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x1a, 0x36, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x45, 0x0a, 0x05, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x3a, + 0x02, 0x18, 0x01, 0x22, 0x63, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x41, 0x0a, 0x09, 0x44, 0x65, 0x63, 0x69, + 0x6d, 0x61, 0x6c, 0x36, 0x34, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x39, 0x0a, 0x0b, 0x53, + 0x63, 0x61, 0x6c, 0x61, 0x72, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x2a, 0x0a, 0x07, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6e, + 0x6d, 0x69, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0xd7, 0x01, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x09, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x70, 0x6f, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x6f, 0x6c, 0x6c, 0x48, 0x00, 0x52, + 0x04, 0x70, 0x6f, 0x6c, 0x6c, 0x12, 0x2b, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, + 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x06, 0x0a, 0x04, 0x50, 0x6f, 0x6c, 0x6c, 0x22, 0xd0, 0x01, 0x0a, 0x11, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, + 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0d, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, + 0x02, 0x18, 0x01, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x09, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x42, + 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb8, 0x03, 0x0a, 0x10, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x22, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x06, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x12, 0x36, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, + 0x75, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x22, 0x0a, + 0x03, 0x71, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x51, 0x4f, 0x53, 0x4d, 0x61, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x52, 0x03, 0x71, 0x6f, + 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1b, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, + 0x64, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2e, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x09, 0x75, 0x73, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x12, + 0x2a, 0x0a, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x0e, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x26, + 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, + 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x50, 0x4f, 0x4c, 0x4c, 0x10, 0x02, 0x22, 0xe1, 0x01, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, + 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2a, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, + 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x12, + 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x64, 0x75, 0x6e, 0x64, 0x61, + 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x64, 0x75, 0x6e, 0x64, 0x61, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x68, + 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, + 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x26, 0x0a, 0x0a, 0x51, 0x4f, + 0x53, 0x4d, 0x61, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x72, 0x6b, + 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x61, 0x72, 0x6b, 0x69, + 0x6e, 0x67, 0x22, 0x3d, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1e, 0x0a, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x61, + 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x22, 0x2e, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x21, + 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x22, 0xd5, 0x01, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x22, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x06, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x12, 0x22, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, + 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x12, 0x24, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x06, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xdd, 0x01, 0x0a, 0x0b, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x70, 0x72, 0x65, + 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x2e, 0x0a, + 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe8, 0x01, 0x0a, 0x0c, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1e, 0x0a, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x02, 0x6f, 0x70, 0x22, 0x3d, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, + 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, + 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, + 0x54, 0x45, 0x10, 0x03, 0x22, 0xcb, 0x02, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, + 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1e, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, + 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, + 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, + 0x6e, 0x67, 0x12, 0x2e, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x4d, 0x6f, + 0x64, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x52, 0x09, 0x75, 0x73, 0x65, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, + 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, + 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, + 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, + 0x10, 0x03, 0x22, 0x9f, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x46, 0x0a, 0x11, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, + 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe7, 0x01, 0x0a, + 0x12, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0f, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x12, + 0x3f, 0x0a, 0x13, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x67, + 0x6e, 0x6d, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x12, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x4e, 0x4d, 0x49, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x67, 0x4e, 0x4d, 0x49, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, + 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x09, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, + 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x44, 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, + 0x67, 0x12, 0x08, 0x0a, 0x04, 0x4a, 0x53, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x42, + 0x59, 0x54, 0x45, 0x53, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x10, + 0x02, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x43, 0x49, 0x49, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, + 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x45, 0x54, 0x46, 0x10, 0x04, 0x2a, 0x41, 0x0a, 0x10, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, + 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4f, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, + 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x10, 0x02, 0x32, 0xe3, + 0x01, 0x0a, 0x04, 0x67, 0x4e, 0x4d, 0x49, 0x12, 0x41, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x17, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x43, + 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x47, 0x65, + 0x74, 0x12, 0x10, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x53, 0x65, 0x74, 0x12, 0x10, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, + 0x16, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x28, 0x01, 0x30, 0x01, 0x3a, 0x40, 0x0a, 0x0c, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x67, 0x6e, 0x6d, 0x69, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x53, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, + 0x09, 0x47, 0x6e, 0x6d, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, + 0x6e, 0x6d, 0x69, 0xca, 0x3e, 0x05, 0x30, 0x2e, 0x37, 0x2e, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescOnce sync.Once + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescData = file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDesc +) + +func file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescGZIP() []byte { + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescOnce.Do(func() { + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescData) + }) + return file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDescData +} + +var file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_goTypes = []interface{}{ + (Encoding)(0), // 0: gnmi.Encoding + (SubscriptionMode)(0), // 1: gnmi.SubscriptionMode + (SubscriptionList_Mode)(0), // 2: gnmi.SubscriptionList.Mode + (UpdateResult_Operation)(0), // 3: gnmi.UpdateResult.Operation + (GetRequest_DataType)(0), // 4: gnmi.GetRequest.DataType + (*Notification)(nil), // 5: gnmi.Notification + (*Update)(nil), // 6: gnmi.Update + (*TypedValue)(nil), // 7: gnmi.TypedValue + (*Path)(nil), // 8: gnmi.Path + (*PathElem)(nil), // 9: gnmi.PathElem + (*Value)(nil), // 10: gnmi.Value + (*Error)(nil), // 11: gnmi.Error + (*Decimal64)(nil), // 12: gnmi.Decimal64 + (*ScalarArray)(nil), // 13: gnmi.ScalarArray + (*SubscribeRequest)(nil), // 14: gnmi.SubscribeRequest + (*Poll)(nil), // 15: gnmi.Poll + (*SubscribeResponse)(nil), // 16: gnmi.SubscribeResponse + (*SubscriptionList)(nil), // 17: gnmi.SubscriptionList + (*Subscription)(nil), // 18: gnmi.Subscription + (*QOSMarking)(nil), // 19: gnmi.QOSMarking + (*Alias)(nil), // 20: gnmi.Alias + (*AliasList)(nil), // 21: gnmi.AliasList + (*SetRequest)(nil), // 22: gnmi.SetRequest + (*SetResponse)(nil), // 23: gnmi.SetResponse + (*UpdateResult)(nil), // 24: gnmi.UpdateResult + (*GetRequest)(nil), // 25: gnmi.GetRequest + (*GetResponse)(nil), // 26: gnmi.GetResponse + (*CapabilityRequest)(nil), // 27: gnmi.CapabilityRequest + (*CapabilityResponse)(nil), // 28: gnmi.CapabilityResponse + (*ModelData)(nil), // 29: gnmi.ModelData + nil, // 30: gnmi.PathElem.KeyEntry + (*any.Any)(nil), // 31: google.protobuf.Any + (*gnmi_ext.Extension)(nil), // 32: gnmi_ext.Extension + (*descriptor.FileOptions)(nil), // 33: google.protobuf.FileOptions +} +var file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_depIdxs = []int32{ + 8, // 0: gnmi.Notification.prefix:type_name -> gnmi.Path + 6, // 1: gnmi.Notification.update:type_name -> gnmi.Update + 8, // 2: gnmi.Notification.delete:type_name -> gnmi.Path + 8, // 3: gnmi.Update.path:type_name -> gnmi.Path + 10, // 4: gnmi.Update.value:type_name -> gnmi.Value + 7, // 5: gnmi.Update.val:type_name -> gnmi.TypedValue + 12, // 6: gnmi.TypedValue.decimal_val:type_name -> gnmi.Decimal64 + 13, // 7: gnmi.TypedValue.leaflist_val:type_name -> gnmi.ScalarArray + 31, // 8: gnmi.TypedValue.any_val:type_name -> google.protobuf.Any + 9, // 9: gnmi.Path.elem:type_name -> gnmi.PathElem + 30, // 10: gnmi.PathElem.key:type_name -> gnmi.PathElem.KeyEntry + 0, // 11: gnmi.Value.type:type_name -> gnmi.Encoding + 31, // 12: gnmi.Error.data:type_name -> google.protobuf.Any + 7, // 13: gnmi.ScalarArray.element:type_name -> gnmi.TypedValue + 17, // 14: gnmi.SubscribeRequest.subscribe:type_name -> gnmi.SubscriptionList + 15, // 15: gnmi.SubscribeRequest.poll:type_name -> gnmi.Poll + 21, // 16: gnmi.SubscribeRequest.aliases:type_name -> gnmi.AliasList + 32, // 17: gnmi.SubscribeRequest.extension:type_name -> gnmi_ext.Extension + 5, // 18: gnmi.SubscribeResponse.update:type_name -> gnmi.Notification + 11, // 19: gnmi.SubscribeResponse.error:type_name -> gnmi.Error + 32, // 20: gnmi.SubscribeResponse.extension:type_name -> gnmi_ext.Extension + 8, // 21: gnmi.SubscriptionList.prefix:type_name -> gnmi.Path + 18, // 22: gnmi.SubscriptionList.subscription:type_name -> gnmi.Subscription + 19, // 23: gnmi.SubscriptionList.qos:type_name -> gnmi.QOSMarking + 2, // 24: gnmi.SubscriptionList.mode:type_name -> gnmi.SubscriptionList.Mode + 29, // 25: gnmi.SubscriptionList.use_models:type_name -> gnmi.ModelData + 0, // 26: gnmi.SubscriptionList.encoding:type_name -> gnmi.Encoding + 8, // 27: gnmi.Subscription.path:type_name -> gnmi.Path + 1, // 28: gnmi.Subscription.mode:type_name -> gnmi.SubscriptionMode + 8, // 29: gnmi.Alias.path:type_name -> gnmi.Path + 20, // 30: gnmi.AliasList.alias:type_name -> gnmi.Alias + 8, // 31: gnmi.SetRequest.prefix:type_name -> gnmi.Path + 8, // 32: gnmi.SetRequest.delete:type_name -> gnmi.Path + 6, // 33: gnmi.SetRequest.replace:type_name -> gnmi.Update + 6, // 34: gnmi.SetRequest.update:type_name -> gnmi.Update + 32, // 35: gnmi.SetRequest.extension:type_name -> gnmi_ext.Extension + 8, // 36: gnmi.SetResponse.prefix:type_name -> gnmi.Path + 24, // 37: gnmi.SetResponse.response:type_name -> gnmi.UpdateResult + 11, // 38: gnmi.SetResponse.message:type_name -> gnmi.Error + 32, // 39: gnmi.SetResponse.extension:type_name -> gnmi_ext.Extension + 8, // 40: gnmi.UpdateResult.path:type_name -> gnmi.Path + 11, // 41: gnmi.UpdateResult.message:type_name -> gnmi.Error + 3, // 42: gnmi.UpdateResult.op:type_name -> gnmi.UpdateResult.Operation + 8, // 43: gnmi.GetRequest.prefix:type_name -> gnmi.Path + 8, // 44: gnmi.GetRequest.path:type_name -> gnmi.Path + 4, // 45: gnmi.GetRequest.type:type_name -> gnmi.GetRequest.DataType + 0, // 46: gnmi.GetRequest.encoding:type_name -> gnmi.Encoding + 29, // 47: gnmi.GetRequest.use_models:type_name -> gnmi.ModelData + 32, // 48: gnmi.GetRequest.extension:type_name -> gnmi_ext.Extension + 5, // 49: gnmi.GetResponse.notification:type_name -> gnmi.Notification + 11, // 50: gnmi.GetResponse.error:type_name -> gnmi.Error + 32, // 51: gnmi.GetResponse.extension:type_name -> gnmi_ext.Extension + 32, // 52: gnmi.CapabilityRequest.extension:type_name -> gnmi_ext.Extension + 29, // 53: gnmi.CapabilityResponse.supported_models:type_name -> gnmi.ModelData + 0, // 54: gnmi.CapabilityResponse.supported_encodings:type_name -> gnmi.Encoding + 32, // 55: gnmi.CapabilityResponse.extension:type_name -> gnmi_ext.Extension + 33, // 56: gnmi.gnmi_service:extendee -> google.protobuf.FileOptions + 27, // 57: gnmi.gNMI.Capabilities:input_type -> gnmi.CapabilityRequest + 25, // 58: gnmi.gNMI.Get:input_type -> gnmi.GetRequest + 22, // 59: gnmi.gNMI.Set:input_type -> gnmi.SetRequest + 14, // 60: gnmi.gNMI.Subscribe:input_type -> gnmi.SubscribeRequest + 28, // 61: gnmi.gNMI.Capabilities:output_type -> gnmi.CapabilityResponse + 26, // 62: gnmi.gNMI.Get:output_type -> gnmi.GetResponse + 23, // 63: gnmi.gNMI.Set:output_type -> gnmi.SetResponse + 16, // 64: gnmi.gNMI.Subscribe:output_type -> gnmi.SubscribeResponse + 61, // [61:65] is the sub-list for method output_type + 57, // [57:61] is the sub-list for method input_type + 57, // [57:57] is the sub-list for extension type_name + 56, // [56:57] is the sub-list for extension extendee + 0, // [0:56] is the sub-list for field type_name +} + +func init() { file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_init() } +func file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_init() { + if File_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Notification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Update); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TypedValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Path); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PathElem); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Error); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Decimal64); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ScalarArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Poll); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscriptionList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Subscription); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QOSMarking); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Alias); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AliasList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CapabilityRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CapabilityResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ModelData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*TypedValue_StringVal)(nil), + (*TypedValue_IntVal)(nil), + (*TypedValue_UintVal)(nil), + (*TypedValue_BoolVal)(nil), + (*TypedValue_BytesVal)(nil), + (*TypedValue_FloatVal)(nil), + (*TypedValue_DecimalVal)(nil), + (*TypedValue_LeaflistVal)(nil), + (*TypedValue_AnyVal)(nil), + (*TypedValue_JsonVal)(nil), + (*TypedValue_JsonIetfVal)(nil), + (*TypedValue_AsciiVal)(nil), + (*TypedValue_ProtoBytes)(nil), + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[9].OneofWrappers = []interface{}{ + (*SubscribeRequest_Subscribe)(nil), + (*SubscribeRequest_Poll)(nil), + (*SubscribeRequest_Aliases)(nil), + } + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes[11].OneofWrappers = []interface{}{ + (*SubscribeResponse_Update)(nil), + (*SubscribeResponse_SyncResponse)(nil), + (*SubscribeResponse_Error)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDesc, + NumEnums: 5, + NumMessages: 26, + NumExtensions: 1, + NumServices: 1, + }, + GoTypes: file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_goTypes, + DependencyIndexes: file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_depIdxs, + EnumInfos: file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_enumTypes, + MessageInfos: file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_msgTypes, + ExtensionInfos: file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_extTypes, + }.Build() + File_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto = out.File + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_rawDesc = nil + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_goTypes = nil + file_github_com_openconfig_gnmi_proto_gnmi_gnmi_proto_depIdxs = nil +} diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi.proto b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi.proto new file mode 100644 index 0000000000000000000000000000000000000000..4742bfbcb20927174c0843d360eb161611d99140 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi.proto @@ -0,0 +1,461 @@ +// +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "google/protobuf/descriptor.proto"; +import "github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto"; + +// Package gNMI defines a service specification for the gRPC Network Management +// Interface. This interface is defined to be a standard interface via which +// a network management system ("client") can subscribe to state values, +// retrieve snapshots of state information, and manipulate the state of a data +// tree supported by a device ("target"). +// +// This document references the gNMI Specification which can be found at +// http://github.com/openconfig/reference/blob/master/rpc/gnmi +package gnmi; + +// Define a protobuf FileOption that defines the gNMI service version. +extend google.protobuf.FileOptions { + // The gNMI service semantic version. + string gnmi_service = 1001; +} + +// gNMI_service is the current version of the gNMI service, returned through +// the Capabilities RPC. +option (gnmi_service) = "0.7.0"; + +option go_package = "github.com/openconfig/gnmi/proto/gnmi"; +option java_multiple_files = true; +option java_outer_classname = "GnmiProto"; +option java_package = "com.github.gnmi.proto"; + +service gNMI { + // Capabilities allows the client to retrieve the set of capabilities that + // is supported by the target. This allows the target to validate the + // service version that is implemented and retrieve the set of models that + // the target supports. The models can then be specified in subsequent RPCs + // to restrict the set of data that is utilized. + // Reference: gNMI Specification Section 3.2 + rpc Capabilities(CapabilityRequest) returns (CapabilityResponse); + // Retrieve a snapshot of data from the target. A Get RPC requests that the + // target snapshots a subset of the data tree as specified by the paths + // included in the message and serializes this to be returned to the + // client using the specified encoding. + // Reference: gNMI Specification Section 3.3 + rpc Get(GetRequest) returns (GetResponse); + // Set allows the client to modify the state of data on the target. The + // paths to modified along with the new values that the client wishes + // to set the value to. + // Reference: gNMI Specification Section 3.4 + rpc Set(SetRequest) returns (SetResponse); + // Subscribe allows a client to request the target to send it values + // of particular paths within the data tree. These values may be streamed + // at a particular cadence (STREAM), sent one off on a long-lived channel + // (POLL), or sent as a one-off retrieval (ONCE). + // Reference: gNMI Specification Section 3.5 + rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeResponse); +} + +// Notification is a re-usable message that is used to encode data from the +// target to the client. A Notification carries two types of changes to the data +// tree: +// - Deleted values (delete) - a set of paths that have been removed from the +// data tree. +// - Updated values (update) - a set of path-value pairs indicating the path +// whose value has changed in the data tree. +// Reference: gNMI Specification Section 2.1 +message Notification { + int64 timestamp = 1; // Timestamp in nanoseconds since Epoch. + Path prefix = 2; // Prefix used for paths in the message. + // An alias for the path specified in the prefix field. + // Reference: gNMI Specification Section 2.4.2 + string alias = 3; + repeated Update update = 4; // Data elements that have changed values. + repeated Path delete = 5; // Data elements that have been deleted. + // This notification contains a set of paths that are always updated together + // referenced by a globally unique prefix. + bool atomic = 6; +} + +// Update is a re-usable message that is used to store a particular Path, +// Value pair. +// Reference: gNMI Specification Section 2.1 +message Update { + Path path = 1; // The path (key) for the update. + Value value = 2 [deprecated=true]; // The value (value) for the update. + TypedValue val = 3; // The explicitly typed update value. + uint32 duplicates = 4; // Number of coalesced duplicates. +} + +// TypedValue is used to encode a value being sent between the client and +// target (originated by either entity). +message TypedValue { + // One of the fields within the val oneof is populated with the value + // of the update. The type of the value being included in the Update + // determines which field should be populated. In the case that the + // encoding is a particular form of the base protobuf type, a specific + // field is used to store the value (e.g., json_val). + oneof value { + string string_val = 1; // String value. + int64 int_val = 2; // Integer value. + uint64 uint_val = 3; // Unsigned integer value. + bool bool_val = 4; // Bool value. + bytes bytes_val = 5; // Arbitrary byte sequence value. + float float_val = 6; // Floating point value. + Decimal64 decimal_val = 7; // Decimal64 encoded value. + ScalarArray leaflist_val = 8; // Mixed type scalar array value. + google.protobuf.Any any_val = 9; // protobuf.Any encoded bytes. + bytes json_val = 10; // JSON-encoded text. + bytes json_ietf_val = 11; // JSON-encoded text per RFC7951. + string ascii_val = 12; // Arbitrary ASCII text. + // Protobuf binary encoded bytes. The message type is not included. + // See the specification at + // github.com/openconfig/reference/blob/master/rpc/gnmi/protobuf-vals.md + // for a complete specification. + bytes proto_bytes = 13; + } +} + +// Path encodes a data tree path as a series of repeated strings, with +// each element of the path representing a data tree node name and the +// associated attributes. +// Reference: gNMI Specification Section 2.2.2. +message Path { + // Elements of the path are no longer encoded as a string, but rather within + // the elem field as a PathElem message. + repeated string element = 1 [deprecated=true]; + string origin = 2; // Label to disambiguate path. + repeated PathElem elem = 3; // Elements of the path. + string target = 4; // The name of the target + // (Sec. 2.2.2.1) +} + +// PathElem encodes an element of a gNMI path, along with any attributes (keys) +// that may be associated with it. +// Reference: gNMI Specification Section 2.2.2. +message PathElem { + string name = 1; // The name of the element in the path. + map<string, string> key = 2; // Map of key (attribute) name to value. +} + +// Value encodes a data tree node's value - along with the way in which +// the value is encoded. This message is deprecated by gNMI 0.3.0. +// Reference: gNMI Specification Section 2.2.3. +message Value { + option deprecated = true; + bytes value = 1; // Value of the variable being transmitted. + Encoding type = 2; // Encoding used for the value field. +} + +// Encoding defines the value encoding formats that are supported by the gNMI +// protocol. These encodings are used by both the client (when sending Set +// messages to modify the state of the target) and the target when serializing +// data to be returned to the client (in both Subscribe and Get RPCs). +// Reference: gNMI Specification Section 2.3 +enum Encoding { + JSON = 0; // JSON encoded text. + BYTES = 1; // Arbitrarily encoded bytes. + PROTO = 2; // Encoded according to out-of-band agreed Protobuf. + ASCII = 3; // ASCII text of an out-of-band agreed format. + JSON_IETF = 4; // JSON encoded text as per RFC7951. +} + +// Error message previously utilised to return errors to the client. Deprecated +// in favour of using the google.golang.org/genproto/googleapis/rpc/status +// message in the RPC response. +// Reference: gNMI Specification Section 2.5 +message Error { + option deprecated = true; + uint32 code = 1; // Canonical gRPC error code. + string message = 2; // Human readable error. + google.protobuf.Any data = 3; // Optional additional information. +} + +// Decimal64 is used to encode a fixed precision decimal number. The value +// is expressed as a set of digits with the precision specifying the +// number of digits following the decimal point in the digit set. +message Decimal64 { + int64 digits = 1; // Set of digits. + uint32 precision = 2; // Number of digits following the decimal point. +} + +// ScalarArray is used to encode a mixed-type array of values. +message ScalarArray { + // The set of elements within the array. Each TypedValue message should + // specify only elements that have a field identifier of 1-7 (i.e., the + // values are scalar values). + repeated TypedValue element = 1; +} + +// SubscribeRequest is the message sent by the client to the target when +// initiating a subscription to a set of paths within the data tree. The +// request field must be populated and the initial message must specify a +// SubscriptionList to initiate a subscription. The message is subsequently +// used to define aliases or trigger polled data to be sent by the target. +// Reference: gNMI Specification Section 3.5.1.1 +message SubscribeRequest { + oneof request { + SubscriptionList subscribe = 1; // Specify the paths within a subscription. + Poll poll = 3; // Trigger a polled update. + AliasList aliases = 4; // Aliases to be created. + } + // Extension messages associated with the SubscribeRequest. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 5; +} + +// Poll is sent within a SubscribeRequest to trigger the device to +// send telemetry updates for the paths that are associated with the +// subscription. +// Reference: gNMI Specification Section Section 3.5.1.4 +message Poll { +} + +// SubscribeResponse is the message used by the target within a Subscribe RPC. +// The target includes a Notification message which is used to transmit values +// of the path(s) that are associated with the subscription. The same message +// is to indicate that the target has sent all data values once (is +// synchronized). +// Reference: gNMI Specification Section 3.5.1.4 +message SubscribeResponse { + oneof response { + Notification update = 1; // Changed or sampled value for a path. + // Indicate target has sent all values associated with the subscription + // at least once. + bool sync_response = 3; + // Deprecated in favour of google.golang.org/genproto/googleapis/rpc/status + Error error = 4 [deprecated=true]; + } + // Extension messages associated with the SubscribeResponse. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 5; +} + +// SubscriptionList is used within a Subscribe message to specify the list of +// paths that the client wishes to subscribe to. The message consists of a +// list of (possibly prefixed) paths, and options that relate to the +// subscription. +// Reference: gNMI Specification Section 3.5.1.2 +message SubscriptionList { + Path prefix = 1; // Prefix used for paths. + repeated Subscription subscription = 2; // Set of subscriptions to create. + // Whether target defined aliases are allowed within the subscription. + bool use_aliases = 3; + QOSMarking qos = 4; // DSCP marking to be used. + // Mode of the subscription. + enum Mode { + STREAM = 0; // Values streamed by the target (Sec. 3.5.1.5.2). + ONCE = 1; // Values sent once-off by the target (Sec. 3.5.1.5.1). + POLL = 2; // Values sent in response to a poll request (Sec. 3.5.1.5.3). + } + Mode mode = 5; + // Whether elements of the schema that are marked as eligible for aggregation + // should be aggregated or not. + bool allow_aggregation = 6; + // The set of schemas that define the elements of the data tree that should + // be sent by the target. + repeated ModelData use_models = 7; + // The encoding that the target should use within the Notifications generated + // corresponding to the SubscriptionList. + Encoding encoding = 8; + // An optional field to specify that only updates to current state should be + // sent to a client. If set, the initial state is not sent to the client but + // rather only the sync message followed by any subsequent updates to the + // current state. For ONCE and POLL modes, this causes the server to send only + // the sync message (Sec. 3.5.2.3). + bool updates_only = 9; +} + +// Subscription is a single request within a SubscriptionList. The path +// specified is interpreted (along with the prefix) as the elements of the data +// tree that the client is subscribing to. The mode determines how the target +// should trigger updates to be sent. +// Reference: gNMI Specification Section 3.5.1.3 +message Subscription { + Path path = 1; // The data tree path. + SubscriptionMode mode = 2; // Subscription mode to be used. + uint64 sample_interval = 3; // ns between samples in SAMPLE mode. + // Indicates whether values that have not changed should be sent in a SAMPLE + // subscription. + bool suppress_redundant = 4; + // Specifies the maximum allowable silent period in nanoseconds when + // suppress_redundant is in use. The target should send a value at least once + // in the period specified. + uint64 heartbeat_interval = 5; +} + +// SubscriptionMode is the mode of the subscription, specifying how the +// target must return values in a subscription. +// Reference: gNMI Specification Section 3.5.1.3 +enum SubscriptionMode { + TARGET_DEFINED = 0; // The target selects the relevant mode for each element. + ON_CHANGE = 1; // The target sends an update on element value change. + SAMPLE = 2; // The target samples values according to the interval. +} + +// QOSMarking specifies the DSCP value to be set on transmitted telemetry +// updates from the target. +// Reference: gNMI Specification Section 3.5.1.2 +message QOSMarking { + uint32 marking = 1; +} + +// Alias specifies a data tree path, and an associated string which defines an +// alias which is to be used for this path in the context of the RPC. The alias +// is specified as a string which is prefixed with "#" to disambiguate it from +// data tree element paths. +// Reference: gNMI Specification Section 2.4.2 +message Alias { + Path path = 1; // The path to be aliased. + string alias = 2; // The alias value, a string prefixed by "#". +} + +// AliasList specifies a list of aliases. It is used in a SubscribeRequest for +// a client to create a set of aliases that the target is to utilize. +// Reference: gNMI Specification Section 3.5.1.6 +message AliasList { + repeated Alias alias = 1; // The set of aliases to be created. +} + +// SetRequest is sent from a client to the target to update values in the data +// tree. Paths are either deleted by the client, or modified by means of being +// updated, or replaced. Where a replace is used, unspecified values are +// considered to be replaced, whereas when update is used the changes are +// considered to be incremental. The set of changes that are specified within +// a single SetRequest are considered to be a transaction. +// Reference: gNMI Specification Section 3.4.1 +message SetRequest { + Path prefix = 1; // Prefix used for paths in the message. + repeated Path delete = 2; // Paths to be deleted from the data tree. + repeated Update replace = 3; // Updates specifying elements to be replaced. + repeated Update update = 4; // Updates specifying elements to updated. + // Extension messages associated with the SetRequest. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 5; +} + +// SetResponse is the response to a SetRequest, sent from the target to the +// client. It reports the result of the modifications to the data tree that were +// specified by the client. Errors for this RPC should be reported using the +// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto +// message in the RPC return. The gnmi.Error message can be used to add additional +// details where required. +// Reference: gNMI Specification Section 3.4.2 +message SetResponse { + Path prefix = 1; // Prefix used for paths. + // A set of responses specifying the result of the operations specified in + // the SetRequest. + repeated UpdateResult response = 2; + Error message = 3 [deprecated=true]; // The overall status of the transaction. + int64 timestamp = 4; // Timestamp of transaction (ns since epoch). + // Extension messages associated with the SetResponse. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 5; +} + +// UpdateResult is used within the SetResponse message to communicate the +// result of an operation specified within a SetRequest message. +// Reference: gNMI Specification Section 3.4.2 +message UpdateResult { + // The operation that was associated with the Path specified. + enum Operation { + INVALID = 0; + DELETE = 1; // The result relates to a delete of Path. + REPLACE = 2; // The result relates to a replace of Path. + UPDATE = 3; // The result relates to an update of Path. + } + // Deprecated timestamp for the UpdateResult, this field has been + // replaced by the timestamp within the SetResponse message, since + // all mutations effected by a set should be applied as a single + // transaction. + int64 timestamp = 1 [deprecated=true]; + Path path = 2; // Path associated with the update. + Error message = 3 [deprecated=true]; // Status of the update operation. + Operation op = 4; // Update operation type. +} + +// GetRequest is sent when a client initiates a Get RPC. It is used to specify +// the set of data elements for which the target should return a snapshot of +// data. The use_models field specifies the set of schema modules that are to +// be used by the target - where use_models is not specified then the target +// must use all schema models that it has. +// Reference: gNMI Specification Section 3.3.1 +message GetRequest { + Path prefix = 1; // Prefix used for paths. + repeated Path path = 2; // Paths requested by the client. + // Type of elements within the data tree. + enum DataType { + ALL = 0; // All data elements. + CONFIG = 1; // Config (rw) only elements. + STATE = 2; // State (ro) only elements. + // Data elements marked in the schema as operational. This refers to data + // elements whose value relates to the state of processes or interactions + // running on the device. + OPERATIONAL = 3; + } + DataType type = 3; // The type of data being requested. + Encoding encoding = 5; // Encoding to be used. + repeated ModelData use_models = 6; // The schema models to be used. + // Extension messages associated with the GetRequest. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 7; +} + +// GetResponse is used by the target to respond to a GetRequest from a client. +// The set of Notifications corresponds to the data values that are requested +// by the client in the GetRequest. +// Reference: gNMI Specification Section 3.3.2 +message GetResponse { + repeated Notification notification = 1; // Data values. + Error error = 2 [deprecated=true]; // Errors that occurred in the Get. + // Extension messages associated with the GetResponse. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 3; +} + +// CapabilityRequest is sent by the client in the Capabilities RPC to request +// that the target reports its capabilities. +// Reference: gNMI Specification Section 3.2.1 +message CapabilityRequest { + // Extension messages associated with the CapabilityRequest. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 1; +} + +// CapabilityResponse is used by the target to report its capabilities to the +// client within the Capabilities RPC. +// Reference: gNMI Specification Section 3.2.2 +message CapabilityResponse { + repeated ModelData supported_models = 1; // Supported schema models. + repeated Encoding supported_encodings = 2; // Supported encodings. + string gNMI_version = 3; // Supported gNMI version. + // Extension messages associated with the CapabilityResponse. See the + // gNMI extension specification for further definition. + repeated gnmi_ext.Extension extension = 4; +} + +// ModelData is used to describe a set of schema modules. It can be used in a +// CapabilityResponse where a target reports the set of modules that it +// supports, and within the SubscribeRequest and GetRequest messages to specify +// the set of models from which data tree elements should be reported. +// Reference: gNMI Specification Section 3.2.3 +message ModelData { + string name = 1; // Name of the model. + string organization = 2; // Organization publishing the model. + string version = 3; // Semantic version of the model. +} diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_grpc.pb.go b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_grpc.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..0ee6cbaec25193a2534fe3f3a422d0ddb1430c9d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_grpc.pb.go @@ -0,0 +1,280 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package gnmi + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// GNMIClient is the client API for GNMI service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GNMIClient interface { + // Capabilities allows the client to retrieve the set of capabilities that + // is supported by the target. This allows the target to validate the + // service version that is implemented and retrieve the set of models that + // the target supports. The models can then be specified in subsequent RPCs + // to restrict the set of data that is utilized. + // Reference: gNMI Specification Section 3.2 + Capabilities(ctx context.Context, in *CapabilityRequest, opts ...grpc.CallOption) (*CapabilityResponse, error) + // Retrieve a snapshot of data from the target. A Get RPC requests that the + // target snapshots a subset of the data tree as specified by the paths + // included in the message and serializes this to be returned to the + // client using the specified encoding. + // Reference: gNMI Specification Section 3.3 + Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) + // Set allows the client to modify the state of data on the target. The + // paths to modified along with the new values that the client wishes + // to set the value to. + // Reference: gNMI Specification Section 3.4 + Set(ctx context.Context, in *SetRequest, opts ...grpc.CallOption) (*SetResponse, error) + // Subscribe allows a client to request the target to send it values + // of particular paths within the data tree. These values may be streamed + // at a particular cadence (STREAM), sent one off on a long-lived channel + // (POLL), or sent as a one-off retrieval (ONCE). + // Reference: gNMI Specification Section 3.5 + Subscribe(ctx context.Context, opts ...grpc.CallOption) (GNMI_SubscribeClient, error) +} + +type gNMIClient struct { + cc grpc.ClientConnInterface +} + +func NewGNMIClient(cc grpc.ClientConnInterface) GNMIClient { + return &gNMIClient{cc} +} + +func (c *gNMIClient) Capabilities(ctx context.Context, in *CapabilityRequest, opts ...grpc.CallOption) (*CapabilityResponse, error) { + out := new(CapabilityResponse) + err := c.cc.Invoke(ctx, "/gnmi.gNMI/Capabilities", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gNMIClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { + out := new(GetResponse) + err := c.cc.Invoke(ctx, "/gnmi.gNMI/Get", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gNMIClient) Set(ctx context.Context, in *SetRequest, opts ...grpc.CallOption) (*SetResponse, error) { + out := new(SetResponse) + err := c.cc.Invoke(ctx, "/gnmi.gNMI/Set", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gNMIClient) Subscribe(ctx context.Context, opts ...grpc.CallOption) (GNMI_SubscribeClient, error) { + stream, err := c.cc.NewStream(ctx, &GNMI_ServiceDesc.Streams[0], "/gnmi.gNMI/Subscribe", opts...) + if err != nil { + return nil, err + } + x := &gNMISubscribeClient{stream} + return x, nil +} + +type GNMI_SubscribeClient interface { + Send(*SubscribeRequest) error + Recv() (*SubscribeResponse, error) + grpc.ClientStream +} + +type gNMISubscribeClient struct { + grpc.ClientStream +} + +func (x *gNMISubscribeClient) Send(m *SubscribeRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *gNMISubscribeClient) Recv() (*SubscribeResponse, error) { + m := new(SubscribeResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// GNMIServer is the server API for GNMI service. +// All implementations should embed UnimplementedGNMIServer +// for forward compatibility +type GNMIServer interface { + // Capabilities allows the client to retrieve the set of capabilities that + // is supported by the target. This allows the target to validate the + // service version that is implemented and retrieve the set of models that + // the target supports. The models can then be specified in subsequent RPCs + // to restrict the set of data that is utilized. + // Reference: gNMI Specification Section 3.2 + Capabilities(context.Context, *CapabilityRequest) (*CapabilityResponse, error) + // Retrieve a snapshot of data from the target. A Get RPC requests that the + // target snapshots a subset of the data tree as specified by the paths + // included in the message and serializes this to be returned to the + // client using the specified encoding. + // Reference: gNMI Specification Section 3.3 + Get(context.Context, *GetRequest) (*GetResponse, error) + // Set allows the client to modify the state of data on the target. The + // paths to modified along with the new values that the client wishes + // to set the value to. + // Reference: gNMI Specification Section 3.4 + Set(context.Context, *SetRequest) (*SetResponse, error) + // Subscribe allows a client to request the target to send it values + // of particular paths within the data tree. These values may be streamed + // at a particular cadence (STREAM), sent one off on a long-lived channel + // (POLL), or sent as a one-off retrieval (ONCE). + // Reference: gNMI Specification Section 3.5 + Subscribe(GNMI_SubscribeServer) error +} + +// UnimplementedGNMIServer should be embedded to have forward compatible implementations. +type UnimplementedGNMIServer struct { +} + +func (UnimplementedGNMIServer) Capabilities(context.Context, *CapabilityRequest) (*CapabilityResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Capabilities not implemented") +} +func (UnimplementedGNMIServer) Get(context.Context, *GetRequest) (*GetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedGNMIServer) Set(context.Context, *SetRequest) (*SetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Set not implemented") +} +func (UnimplementedGNMIServer) Subscribe(GNMI_SubscribeServer) error { + return status.Errorf(codes.Unimplemented, "method Subscribe not implemented") +} + +// UnsafeGNMIServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GNMIServer will +// result in compilation errors. +type UnsafeGNMIServer interface { + mustEmbedUnimplementedGNMIServer() +} + +func RegisterGNMIServer(s grpc.ServiceRegistrar, srv GNMIServer) { + s.RegisterService(&GNMI_ServiceDesc, srv) +} + +func _GNMI_Capabilities_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CapabilityRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GNMIServer).Capabilities(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.gNMI/Capabilities", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GNMIServer).Capabilities(ctx, req.(*CapabilityRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GNMI_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GNMIServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.gNMI/Get", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GNMIServer).Get(ctx, req.(*GetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GNMI_Set_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GNMIServer).Set(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.gNMI/Set", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GNMIServer).Set(ctx, req.(*SetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GNMI_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GNMIServer).Subscribe(&gNMISubscribeServer{stream}) +} + +type GNMI_SubscribeServer interface { + Send(*SubscribeResponse) error + Recv() (*SubscribeRequest, error) + grpc.ServerStream +} + +type gNMISubscribeServer struct { + grpc.ServerStream +} + +func (x *gNMISubscribeServer) Send(m *SubscribeResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *gNMISubscribeServer) Recv() (*SubscribeRequest, error) { + m := new(SubscribeRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// GNMI_ServiceDesc is the grpc.ServiceDesc for GNMI service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var GNMI_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gnmi.gNMI", + HandlerType: (*GNMIServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Capabilities", + Handler: _GNMI_Capabilities_Handler, + }, + { + MethodName: "Get", + Handler: _GNMI_Get_Handler, + }, + { + MethodName: "Set", + Handler: _GNMI_Set_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Subscribe", + Handler: _GNMI_Subscribe_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "github.com/openconfig/gnmi/proto/gnmi/gnmi.proto", +} diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_pb2.py b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..c482afd8e3e5f95c900606fd1f37bdcef55172f9 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_pb2.py @@ -0,0 +1,1958 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/gnmi/gnmi.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 +from github.com.openconfig.gnmi.proto.gnmi_ext import gnmi_ext_pb2 as github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='proto/gnmi/gnmi.proto', + package='gnmi', + syntax='proto3', + serialized_options=b'\n\025com.github.gnmi.protoB\tGnmiProtoP\001Z%github.com/openconfig/gnmi/proto/gnmi\312>\0050.7.0', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x15proto/gnmi/gnmi.proto\x12\x04gnmi\x1a\x19google/protobuf/any.proto\x1a google/protobuf/descriptor.proto\x1a\x38github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto\"\x96\x01\n\x0cNotification\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x1a\n\x06prefix\x18\x02 \x01(\x0b\x32\n.gnmi.Path\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x1c\n\x06update\x18\x04 \x03(\x0b\x32\x0c.gnmi.Update\x12\x1a\n\x06\x64\x65lete\x18\x05 \x03(\x0b\x32\n.gnmi.Path\x12\x0e\n\x06\x61tomic\x18\x06 \x01(\x08\"u\n\x06Update\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0b.gnmi.ValueB\x02\x18\x01\x12\x1d\n\x03val\x18\x03 \x01(\x0b\x32\x10.gnmi.TypedValue\x12\x12\n\nduplicates\x18\x04 \x01(\r\"\xe5\x02\n\nTypedValue\x12\x14\n\nstring_val\x18\x01 \x01(\tH\x00\x12\x11\n\x07int_val\x18\x02 \x01(\x03H\x00\x12\x12\n\x08uint_val\x18\x03 \x01(\x04H\x00\x12\x12\n\x08\x62ool_val\x18\x04 \x01(\x08H\x00\x12\x13\n\tbytes_val\x18\x05 \x01(\x0cH\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12&\n\x0b\x64\x65\x63imal_val\x18\x07 \x01(\x0b\x32\x0f.gnmi.Decimal64H\x00\x12)\n\x0cleaflist_val\x18\x08 \x01(\x0b\x32\x11.gnmi.ScalarArrayH\x00\x12\'\n\x07\x61ny_val\x18\t \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x12\x12\n\x08json_val\x18\n \x01(\x0cH\x00\x12\x17\n\rjson_ietf_val\x18\x0b \x01(\x0cH\x00\x12\x13\n\tascii_val\x18\x0c \x01(\tH\x00\x12\x15\n\x0bproto_bytes\x18\r \x01(\x0cH\x00\x42\x07\n\x05value\"Y\n\x04Path\x12\x13\n\x07\x65lement\x18\x01 \x03(\tB\x02\x18\x01\x12\x0e\n\x06origin\x18\x02 \x01(\t\x12\x1c\n\x04\x65lem\x18\x03 \x03(\x0b\x32\x0e.gnmi.PathElem\x12\x0e\n\x06target\x18\x04 \x01(\t\"j\n\x08PathElem\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x03key\x18\x02 \x03(\x0b\x32\x17.gnmi.PathElem.KeyEntry\x1a*\n\x08KeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"8\n\x05Value\x12\r\n\x05value\x18\x01 \x01(\x0c\x12\x1c\n\x04type\x18\x02 \x01(\x0e\x32\x0e.gnmi.Encoding:\x02\x18\x01\"N\n\x05\x45rror\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\"\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any:\x02\x18\x01\".\n\tDecimal64\x12\x0e\n\x06\x64igits\x18\x01 \x01(\x03\x12\x11\n\tprecision\x18\x02 \x01(\r\"0\n\x0bScalarArray\x12!\n\x07\x65lement\x18\x01 \x03(\x0b\x32\x10.gnmi.TypedValue\"\xb2\x01\n\x10SubscribeRequest\x12+\n\tsubscribe\x18\x01 \x01(\x0b\x32\x16.gnmi.SubscriptionListH\x00\x12\x1a\n\x04poll\x18\x03 \x01(\x0b\x32\n.gnmi.PollH\x00\x12\"\n\x07\x61liases\x18\x04 \x01(\x0b\x32\x0f.gnmi.AliasListH\x00\x12&\n\textension\x18\x05 \x03(\x0b\x32\x13.gnmi_ext.ExtensionB\t\n\x07request\"\x06\n\x04Poll\"\xa8\x01\n\x11SubscribeResponse\x12$\n\x06update\x18\x01 \x01(\x0b\x32\x12.gnmi.NotificationH\x00\x12\x17\n\rsync_response\x18\x03 \x01(\x08H\x00\x12 \n\x05\x65rror\x18\x04 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01H\x00\x12&\n\textension\x18\x05 \x03(\x0b\x32\x13.gnmi_ext.ExtensionB\n\n\x08response\"\xd7\x02\n\x10SubscriptionList\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12(\n\x0csubscription\x18\x02 \x03(\x0b\x32\x12.gnmi.Subscription\x12\x13\n\x0buse_aliases\x18\x03 \x01(\x08\x12\x1d\n\x03qos\x18\x04 \x01(\x0b\x32\x10.gnmi.QOSMarking\x12)\n\x04mode\x18\x05 \x01(\x0e\x32\x1b.gnmi.SubscriptionList.Mode\x12\x19\n\x11\x61llow_aggregation\x18\x06 \x01(\x08\x12#\n\nuse_models\x18\x07 \x03(\x0b\x32\x0f.gnmi.ModelData\x12 \n\x08\x65ncoding\x18\x08 \x01(\x0e\x32\x0e.gnmi.Encoding\x12\x14\n\x0cupdates_only\x18\t \x01(\x08\"&\n\x04Mode\x12\n\n\x06STREAM\x10\x00\x12\x08\n\x04ONCE\x10\x01\x12\x08\n\x04POLL\x10\x02\"\x9f\x01\n\x0cSubscription\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12$\n\x04mode\x18\x02 \x01(\x0e\x32\x16.gnmi.SubscriptionMode\x12\x17\n\x0fsample_interval\x18\x03 \x01(\x04\x12\x1a\n\x12suppress_redundant\x18\x04 \x01(\x08\x12\x1a\n\x12heartbeat_interval\x18\x05 \x01(\x04\"\x1d\n\nQOSMarking\x12\x0f\n\x07marking\x18\x01 \x01(\r\"0\n\x05\x41lias\x12\x18\n\x04path\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"\'\n\tAliasList\x12\x1a\n\x05\x61lias\x18\x01 \x03(\x0b\x32\x0b.gnmi.Alias\"\xa9\x01\n\nSetRequest\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x1a\n\x06\x64\x65lete\x18\x02 \x03(\x0b\x32\n.gnmi.Path\x12\x1d\n\x07replace\x18\x03 \x03(\x0b\x32\x0c.gnmi.Update\x12\x1c\n\x06update\x18\x04 \x03(\x0b\x32\x0c.gnmi.Update\x12&\n\textension\x18\x05 \x03(\x0b\x32\x13.gnmi_ext.Extension\"\xac\x01\n\x0bSetResponse\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12$\n\x08response\x18\x02 \x03(\x0b\x32\x12.gnmi.UpdateResult\x12 \n\x07message\x18\x03 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\x12&\n\textension\x18\x05 \x03(\x0b\x32\x13.gnmi_ext.Extension\"\xca\x01\n\x0cUpdateResult\x12\x15\n\ttimestamp\x18\x01 \x01(\x03\x42\x02\x18\x01\x12\x18\n\x04path\x18\x02 \x01(\x0b\x32\n.gnmi.Path\x12 \n\x07message\x18\x03 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12(\n\x02op\x18\x04 \x01(\x0e\x32\x1c.gnmi.UpdateResult.Operation\"=\n\tOperation\x12\x0b\n\x07INVALID\x10\x00\x12\n\n\x06\x44\x45LETE\x10\x01\x12\x0b\n\x07REPLACE\x10\x02\x12\n\n\x06UPDATE\x10\x03\"\x97\x02\n\nGetRequest\x12\x1a\n\x06prefix\x18\x01 \x01(\x0b\x32\n.gnmi.Path\x12\x18\n\x04path\x18\x02 \x03(\x0b\x32\n.gnmi.Path\x12\'\n\x04type\x18\x03 \x01(\x0e\x32\x19.gnmi.GetRequest.DataType\x12 \n\x08\x65ncoding\x18\x05 \x01(\x0e\x32\x0e.gnmi.Encoding\x12#\n\nuse_models\x18\x06 \x03(\x0b\x32\x0f.gnmi.ModelData\x12&\n\textension\x18\x07 \x03(\x0b\x32\x13.gnmi_ext.Extension\";\n\x08\x44\x61taType\x12\x07\n\x03\x41LL\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\t\n\x05STATE\x10\x02\x12\x0f\n\x0bOPERATIONAL\x10\x03\"\x7f\n\x0bGetResponse\x12(\n\x0cnotification\x18\x01 \x03(\x0b\x32\x12.gnmi.Notification\x12\x1e\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x0b.gnmi.ErrorB\x02\x18\x01\x12&\n\textension\x18\x03 \x03(\x0b\x32\x13.gnmi_ext.Extension\";\n\x11\x43\x61pabilityRequest\x12&\n\textension\x18\x01 \x03(\x0b\x32\x13.gnmi_ext.Extension\"\xaa\x01\n\x12\x43\x61pabilityResponse\x12)\n\x10supported_models\x18\x01 \x03(\x0b\x32\x0f.gnmi.ModelData\x12+\n\x13supported_encodings\x18\x02 \x03(\x0e\x32\x0e.gnmi.Encoding\x12\x14\n\x0cgNMI_version\x18\x03 \x01(\t\x12&\n\textension\x18\x04 \x03(\x0b\x32\x13.gnmi_ext.Extension\"@\n\tModelData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0corganization\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t*D\n\x08\x45ncoding\x12\x08\n\x04JSON\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\t\n\x05PROTO\x10\x02\x12\t\n\x05\x41SCII\x10\x03\x12\r\n\tJSON_IETF\x10\x04*A\n\x10SubscriptionMode\x12\x12\n\x0eTARGET_DEFINED\x10\x00\x12\r\n\tON_CHANGE\x10\x01\x12\n\n\x06SAMPLE\x10\x02\x32\xe3\x01\n\x04gNMI\x12\x41\n\x0c\x43\x61pabilities\x12\x17.gnmi.CapabilityRequest\x1a\x18.gnmi.CapabilityResponse\x12*\n\x03Get\x12\x10.gnmi.GetRequest\x1a\x11.gnmi.GetResponse\x12*\n\x03Set\x12\x10.gnmi.SetRequest\x1a\x11.gnmi.SetResponse\x12@\n\tSubscribe\x12\x16.gnmi.SubscribeRequest\x1a\x17.gnmi.SubscribeResponse(\x01\x30\x01:3\n\x0cgnmi_service\x12\x1c.google.protobuf.FileOptions\x18\xe9\x07 \x01(\tBS\n\x15\x63om.github.gnmi.protoB\tGnmiProtoP\x01Z%github.com/openconfig/gnmi/proto/gnmi\xca>\x05\x30.7.0b\x06proto3' + , + dependencies=[google_dot_protobuf_dot_any__pb2.DESCRIPTOR,google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2.DESCRIPTOR,]) + +_ENCODING = _descriptor.EnumDescriptor( + name='Encoding', + full_name='gnmi.Encoding', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='JSON', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BYTES', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PROTO', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ASCII', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JSON_IETF', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3470, + serialized_end=3538, +) +_sym_db.RegisterEnumDescriptor(_ENCODING) + +Encoding = enum_type_wrapper.EnumTypeWrapper(_ENCODING) +_SUBSCRIPTIONMODE = _descriptor.EnumDescriptor( + name='SubscriptionMode', + full_name='gnmi.SubscriptionMode', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='TARGET_DEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ON_CHANGE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SAMPLE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3540, + serialized_end=3605, +) +_sym_db.RegisterEnumDescriptor(_SUBSCRIPTIONMODE) + +SubscriptionMode = enum_type_wrapper.EnumTypeWrapper(_SUBSCRIPTIONMODE) +JSON = 0 +BYTES = 1 +PROTO = 2 +ASCII = 3 +JSON_IETF = 4 +TARGET_DEFINED = 0 +ON_CHANGE = 1 +SAMPLE = 2 + +GNMI_SERVICE_FIELD_NUMBER = 1001 +gnmi_service = _descriptor.FieldDescriptor( + name='gnmi_service', full_name='gnmi.gnmi_service', index=0, + number=1001, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key) + +_SUBSCRIPTIONLIST_MODE = _descriptor.EnumDescriptor( + name='Mode', + full_name='gnmi.SubscriptionList.Mode', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='STREAM', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ONCE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='POLL', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=1883, + serialized_end=1921, +) +_sym_db.RegisterEnumDescriptor(_SUBSCRIPTIONLIST_MODE) + +_UPDATERESULT_OPERATION = _descriptor.EnumDescriptor( + name='Operation', + full_name='gnmi.UpdateResult.Operation', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='INVALID', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DELETE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='REPLACE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='UPDATE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2696, + serialized_end=2757, +) +_sym_db.RegisterEnumDescriptor(_UPDATERESULT_OPERATION) + +_GETREQUEST_DATATYPE = _descriptor.EnumDescriptor( + name='DataType', + full_name='gnmi.GetRequest.DataType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='ALL', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIG', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STATE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OPERATIONAL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2980, + serialized_end=3039, +) +_sym_db.RegisterEnumDescriptor(_GETREQUEST_DATATYPE) + + +_NOTIFICATION = _descriptor.Descriptor( + name='Notification', + full_name='gnmi.Notification', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', full_name='gnmi.Notification.timestamp', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prefix', full_name='gnmi.Notification.prefix', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='alias', full_name='gnmi.Notification.alias', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='update', full_name='gnmi.Notification.update', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delete', full_name='gnmi.Notification.delete', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='atomic', full_name='gnmi.Notification.atomic', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=151, + serialized_end=301, +) + + +_UPDATE = _descriptor.Descriptor( + name='Update', + full_name='gnmi.Update', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='gnmi.Update.path', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.Update.value', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='val', full_name='gnmi.Update.val', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='duplicates', full_name='gnmi.Update.duplicates', index=3, + number=4, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=303, + serialized_end=420, +) + + +_TYPEDVALUE = _descriptor.Descriptor( + name='TypedValue', + full_name='gnmi.TypedValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='string_val', full_name='gnmi.TypedValue.string_val', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='int_val', full_name='gnmi.TypedValue.int_val', index=1, + number=2, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uint_val', full_name='gnmi.TypedValue.uint_val', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='bool_val', full_name='gnmi.TypedValue.bool_val', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='bytes_val', full_name='gnmi.TypedValue.bytes_val', index=4, + number=5, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='float_val', full_name='gnmi.TypedValue.float_val', index=5, + number=6, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='decimal_val', full_name='gnmi.TypedValue.decimal_val', index=6, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leaflist_val', full_name='gnmi.TypedValue.leaflist_val', index=7, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='any_val', full_name='gnmi.TypedValue.any_val', index=8, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='json_val', full_name='gnmi.TypedValue.json_val', index=9, + number=10, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='json_ietf_val', full_name='gnmi.TypedValue.json_ietf_val', index=10, + number=11, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ascii_val', full_name='gnmi.TypedValue.ascii_val', index=11, + number=12, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proto_bytes', full_name='gnmi.TypedValue.proto_bytes', index=12, + number=13, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='value', full_name='gnmi.TypedValue.value', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=423, + serialized_end=780, +) + + +_PATH = _descriptor.Descriptor( + name='Path', + full_name='gnmi.Path', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='element', full_name='gnmi.Path.element', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='origin', full_name='gnmi.Path.origin', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='elem', full_name='gnmi.Path.elem', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='target', full_name='gnmi.Path.target', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=782, + serialized_end=871, +) + + +_PATHELEM_KEYENTRY = _descriptor.Descriptor( + name='KeyEntry', + full_name='gnmi.PathElem.KeyEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='gnmi.PathElem.KeyEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.PathElem.KeyEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=937, + serialized_end=979, +) + +_PATHELEM = _descriptor.Descriptor( + name='PathElem', + full_name='gnmi.PathElem', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='gnmi.PathElem.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='key', full_name='gnmi.PathElem.key', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_PATHELEM_KEYENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=873, + serialized_end=979, +) + + +_VALUE = _descriptor.Descriptor( + name='Value', + full_name='gnmi.Value', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.Value.value', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='gnmi.Value.type', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=b'\030\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=981, + serialized_end=1037, +) + + +_ERROR = _descriptor.Descriptor( + name='Error', + full_name='gnmi.Error', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='code', full_name='gnmi.Error.code', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message', full_name='gnmi.Error.message', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='gnmi.Error.data', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=b'\030\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1039, + serialized_end=1117, +) + + +_DECIMAL64 = _descriptor.Descriptor( + name='Decimal64', + full_name='gnmi.Decimal64', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='digits', full_name='gnmi.Decimal64.digits', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='precision', full_name='gnmi.Decimal64.precision', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1119, + serialized_end=1165, +) + + +_SCALARARRAY = _descriptor.Descriptor( + name='ScalarArray', + full_name='gnmi.ScalarArray', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='element', full_name='gnmi.ScalarArray.element', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1167, + serialized_end=1215, +) + + +_SUBSCRIBEREQUEST = _descriptor.Descriptor( + name='SubscribeRequest', + full_name='gnmi.SubscribeRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='subscribe', full_name='gnmi.SubscribeRequest.subscribe', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='poll', full_name='gnmi.SubscribeRequest.poll', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='aliases', full_name='gnmi.SubscribeRequest.aliases', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.SubscribeRequest.extension', index=3, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='request', full_name='gnmi.SubscribeRequest.request', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1218, + serialized_end=1396, +) + + +_POLL = _descriptor.Descriptor( + name='Poll', + full_name='gnmi.Poll', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1398, + serialized_end=1404, +) + + +_SUBSCRIBERESPONSE = _descriptor.Descriptor( + name='SubscribeResponse', + full_name='gnmi.SubscribeResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='update', full_name='gnmi.SubscribeResponse.update', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sync_response', full_name='gnmi.SubscribeResponse.sync_response', index=1, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error', full_name='gnmi.SubscribeResponse.error', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.SubscribeResponse.extension', index=3, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='response', full_name='gnmi.SubscribeResponse.response', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1407, + serialized_end=1575, +) + + +_SUBSCRIPTIONLIST = _descriptor.Descriptor( + name='SubscriptionList', + full_name='gnmi.SubscriptionList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='prefix', full_name='gnmi.SubscriptionList.prefix', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='subscription', full_name='gnmi.SubscriptionList.subscription', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='use_aliases', full_name='gnmi.SubscriptionList.use_aliases', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='qos', full_name='gnmi.SubscriptionList.qos', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='mode', full_name='gnmi.SubscriptionList.mode', index=4, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='allow_aggregation', full_name='gnmi.SubscriptionList.allow_aggregation', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='use_models', full_name='gnmi.SubscriptionList.use_models', index=6, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='encoding', full_name='gnmi.SubscriptionList.encoding', index=7, + number=8, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='updates_only', full_name='gnmi.SubscriptionList.updates_only', index=8, + number=9, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _SUBSCRIPTIONLIST_MODE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1578, + serialized_end=1921, +) + + +_SUBSCRIPTION = _descriptor.Descriptor( + name='Subscription', + full_name='gnmi.Subscription', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='gnmi.Subscription.path', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='mode', full_name='gnmi.Subscription.mode', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sample_interval', full_name='gnmi.Subscription.sample_interval', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='suppress_redundant', full_name='gnmi.Subscription.suppress_redundant', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='heartbeat_interval', full_name='gnmi.Subscription.heartbeat_interval', index=4, + number=5, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1924, + serialized_end=2083, +) + + +_QOSMARKING = _descriptor.Descriptor( + name='QOSMarking', + full_name='gnmi.QOSMarking', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='marking', full_name='gnmi.QOSMarking.marking', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2085, + serialized_end=2114, +) + + +_ALIAS = _descriptor.Descriptor( + name='Alias', + full_name='gnmi.Alias', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='gnmi.Alias.path', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='alias', full_name='gnmi.Alias.alias', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2116, + serialized_end=2164, +) + + +_ALIASLIST = _descriptor.Descriptor( + name='AliasList', + full_name='gnmi.AliasList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='alias', full_name='gnmi.AliasList.alias', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2166, + serialized_end=2205, +) + + +_SETREQUEST = _descriptor.Descriptor( + name='SetRequest', + full_name='gnmi.SetRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='prefix', full_name='gnmi.SetRequest.prefix', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delete', full_name='gnmi.SetRequest.delete', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='replace', full_name='gnmi.SetRequest.replace', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='update', full_name='gnmi.SetRequest.update', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.SetRequest.extension', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2208, + serialized_end=2377, +) + + +_SETRESPONSE = _descriptor.Descriptor( + name='SetResponse', + full_name='gnmi.SetResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='prefix', full_name='gnmi.SetResponse.prefix', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='response', full_name='gnmi.SetResponse.response', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message', full_name='gnmi.SetResponse.message', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='timestamp', full_name='gnmi.SetResponse.timestamp', index=3, + number=4, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.SetResponse.extension', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2380, + serialized_end=2552, +) + + +_UPDATERESULT = _descriptor.Descriptor( + name='UpdateResult', + full_name='gnmi.UpdateResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', full_name='gnmi.UpdateResult.timestamp', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='path', full_name='gnmi.UpdateResult.path', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message', full_name='gnmi.UpdateResult.message', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='op', full_name='gnmi.UpdateResult.op', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _UPDATERESULT_OPERATION, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2555, + serialized_end=2757, +) + + +_GETREQUEST = _descriptor.Descriptor( + name='GetRequest', + full_name='gnmi.GetRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='prefix', full_name='gnmi.GetRequest.prefix', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='path', full_name='gnmi.GetRequest.path', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='gnmi.GetRequest.type', index=2, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='encoding', full_name='gnmi.GetRequest.encoding', index=3, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='use_models', full_name='gnmi.GetRequest.use_models', index=4, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.GetRequest.extension', index=5, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _GETREQUEST_DATATYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2760, + serialized_end=3039, +) + + +_GETRESPONSE = _descriptor.Descriptor( + name='GetResponse', + full_name='gnmi.GetResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='notification', full_name='gnmi.GetResponse.notification', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error', full_name='gnmi.GetResponse.error', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.GetResponse.extension', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3041, + serialized_end=3168, +) + + +_CAPABILITYREQUEST = _descriptor.Descriptor( + name='CapabilityRequest', + full_name='gnmi.CapabilityRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.CapabilityRequest.extension', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3170, + serialized_end=3229, +) + + +_CAPABILITYRESPONSE = _descriptor.Descriptor( + name='CapabilityResponse', + full_name='gnmi.CapabilityResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='supported_models', full_name='gnmi.CapabilityResponse.supported_models', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supported_encodings', full_name='gnmi.CapabilityResponse.supported_encodings', index=1, + number=2, type=14, cpp_type=8, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='gNMI_version', full_name='gnmi.CapabilityResponse.gNMI_version', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='gnmi.CapabilityResponse.extension', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3232, + serialized_end=3402, +) + + +_MODELDATA = _descriptor.Descriptor( + name='ModelData', + full_name='gnmi.ModelData', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='gnmi.ModelData.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='organization', full_name='gnmi.ModelData.organization', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='version', full_name='gnmi.ModelData.version', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3404, + serialized_end=3468, +) + +_NOTIFICATION.fields_by_name['prefix'].message_type = _PATH +_NOTIFICATION.fields_by_name['update'].message_type = _UPDATE +_NOTIFICATION.fields_by_name['delete'].message_type = _PATH +_UPDATE.fields_by_name['path'].message_type = _PATH +_UPDATE.fields_by_name['value'].message_type = _VALUE +_UPDATE.fields_by_name['val'].message_type = _TYPEDVALUE +_TYPEDVALUE.fields_by_name['decimal_val'].message_type = _DECIMAL64 +_TYPEDVALUE.fields_by_name['leaflist_val'].message_type = _SCALARARRAY +_TYPEDVALUE.fields_by_name['any_val'].message_type = google_dot_protobuf_dot_any__pb2._ANY +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['string_val']) +_TYPEDVALUE.fields_by_name['string_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['int_val']) +_TYPEDVALUE.fields_by_name['int_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['uint_val']) +_TYPEDVALUE.fields_by_name['uint_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['bool_val']) +_TYPEDVALUE.fields_by_name['bool_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['bytes_val']) +_TYPEDVALUE.fields_by_name['bytes_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['float_val']) +_TYPEDVALUE.fields_by_name['float_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['decimal_val']) +_TYPEDVALUE.fields_by_name['decimal_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['leaflist_val']) +_TYPEDVALUE.fields_by_name['leaflist_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['any_val']) +_TYPEDVALUE.fields_by_name['any_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['json_val']) +_TYPEDVALUE.fields_by_name['json_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['json_ietf_val']) +_TYPEDVALUE.fields_by_name['json_ietf_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['ascii_val']) +_TYPEDVALUE.fields_by_name['ascii_val'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_TYPEDVALUE.oneofs_by_name['value'].fields.append( + _TYPEDVALUE.fields_by_name['proto_bytes']) +_TYPEDVALUE.fields_by_name['proto_bytes'].containing_oneof = _TYPEDVALUE.oneofs_by_name['value'] +_PATH.fields_by_name['elem'].message_type = _PATHELEM +_PATHELEM_KEYENTRY.containing_type = _PATHELEM +_PATHELEM.fields_by_name['key'].message_type = _PATHELEM_KEYENTRY +_VALUE.fields_by_name['type'].enum_type = _ENCODING +_ERROR.fields_by_name['data'].message_type = google_dot_protobuf_dot_any__pb2._ANY +_SCALARARRAY.fields_by_name['element'].message_type = _TYPEDVALUE +_SUBSCRIBEREQUEST.fields_by_name['subscribe'].message_type = _SUBSCRIPTIONLIST +_SUBSCRIBEREQUEST.fields_by_name['poll'].message_type = _POLL +_SUBSCRIBEREQUEST.fields_by_name['aliases'].message_type = _ALIASLIST +_SUBSCRIBEREQUEST.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_SUBSCRIBEREQUEST.oneofs_by_name['request'].fields.append( + _SUBSCRIBEREQUEST.fields_by_name['subscribe']) +_SUBSCRIBEREQUEST.fields_by_name['subscribe'].containing_oneof = _SUBSCRIBEREQUEST.oneofs_by_name['request'] +_SUBSCRIBEREQUEST.oneofs_by_name['request'].fields.append( + _SUBSCRIBEREQUEST.fields_by_name['poll']) +_SUBSCRIBEREQUEST.fields_by_name['poll'].containing_oneof = _SUBSCRIBEREQUEST.oneofs_by_name['request'] +_SUBSCRIBEREQUEST.oneofs_by_name['request'].fields.append( + _SUBSCRIBEREQUEST.fields_by_name['aliases']) +_SUBSCRIBEREQUEST.fields_by_name['aliases'].containing_oneof = _SUBSCRIBEREQUEST.oneofs_by_name['request'] +_SUBSCRIBERESPONSE.fields_by_name['update'].message_type = _NOTIFICATION +_SUBSCRIBERESPONSE.fields_by_name['error'].message_type = _ERROR +_SUBSCRIBERESPONSE.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_SUBSCRIBERESPONSE.oneofs_by_name['response'].fields.append( + _SUBSCRIBERESPONSE.fields_by_name['update']) +_SUBSCRIBERESPONSE.fields_by_name['update'].containing_oneof = _SUBSCRIBERESPONSE.oneofs_by_name['response'] +_SUBSCRIBERESPONSE.oneofs_by_name['response'].fields.append( + _SUBSCRIBERESPONSE.fields_by_name['sync_response']) +_SUBSCRIBERESPONSE.fields_by_name['sync_response'].containing_oneof = _SUBSCRIBERESPONSE.oneofs_by_name['response'] +_SUBSCRIBERESPONSE.oneofs_by_name['response'].fields.append( + _SUBSCRIBERESPONSE.fields_by_name['error']) +_SUBSCRIBERESPONSE.fields_by_name['error'].containing_oneof = _SUBSCRIBERESPONSE.oneofs_by_name['response'] +_SUBSCRIPTIONLIST.fields_by_name['prefix'].message_type = _PATH +_SUBSCRIPTIONLIST.fields_by_name['subscription'].message_type = _SUBSCRIPTION +_SUBSCRIPTIONLIST.fields_by_name['qos'].message_type = _QOSMARKING +_SUBSCRIPTIONLIST.fields_by_name['mode'].enum_type = _SUBSCRIPTIONLIST_MODE +_SUBSCRIPTIONLIST.fields_by_name['use_models'].message_type = _MODELDATA +_SUBSCRIPTIONLIST.fields_by_name['encoding'].enum_type = _ENCODING +_SUBSCRIPTIONLIST_MODE.containing_type = _SUBSCRIPTIONLIST +_SUBSCRIPTION.fields_by_name['path'].message_type = _PATH +_SUBSCRIPTION.fields_by_name['mode'].enum_type = _SUBSCRIPTIONMODE +_ALIAS.fields_by_name['path'].message_type = _PATH +_ALIASLIST.fields_by_name['alias'].message_type = _ALIAS +_SETREQUEST.fields_by_name['prefix'].message_type = _PATH +_SETREQUEST.fields_by_name['delete'].message_type = _PATH +_SETREQUEST.fields_by_name['replace'].message_type = _UPDATE +_SETREQUEST.fields_by_name['update'].message_type = _UPDATE +_SETREQUEST.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_SETRESPONSE.fields_by_name['prefix'].message_type = _PATH +_SETRESPONSE.fields_by_name['response'].message_type = _UPDATERESULT +_SETRESPONSE.fields_by_name['message'].message_type = _ERROR +_SETRESPONSE.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_UPDATERESULT.fields_by_name['path'].message_type = _PATH +_UPDATERESULT.fields_by_name['message'].message_type = _ERROR +_UPDATERESULT.fields_by_name['op'].enum_type = _UPDATERESULT_OPERATION +_UPDATERESULT_OPERATION.containing_type = _UPDATERESULT +_GETREQUEST.fields_by_name['prefix'].message_type = _PATH +_GETREQUEST.fields_by_name['path'].message_type = _PATH +_GETREQUEST.fields_by_name['type'].enum_type = _GETREQUEST_DATATYPE +_GETREQUEST.fields_by_name['encoding'].enum_type = _ENCODING +_GETREQUEST.fields_by_name['use_models'].message_type = _MODELDATA +_GETREQUEST.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_GETREQUEST_DATATYPE.containing_type = _GETREQUEST +_GETRESPONSE.fields_by_name['notification'].message_type = _NOTIFICATION +_GETRESPONSE.fields_by_name['error'].message_type = _ERROR +_GETRESPONSE.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_CAPABILITYREQUEST.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +_CAPABILITYRESPONSE.fields_by_name['supported_models'].message_type = _MODELDATA +_CAPABILITYRESPONSE.fields_by_name['supported_encodings'].enum_type = _ENCODING +_CAPABILITYRESPONSE.fields_by_name['extension'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi__ext_dot_gnmi__ext__pb2._EXTENSION +DESCRIPTOR.message_types_by_name['Notification'] = _NOTIFICATION +DESCRIPTOR.message_types_by_name['Update'] = _UPDATE +DESCRIPTOR.message_types_by_name['TypedValue'] = _TYPEDVALUE +DESCRIPTOR.message_types_by_name['Path'] = _PATH +DESCRIPTOR.message_types_by_name['PathElem'] = _PATHELEM +DESCRIPTOR.message_types_by_name['Value'] = _VALUE +DESCRIPTOR.message_types_by_name['Error'] = _ERROR +DESCRIPTOR.message_types_by_name['Decimal64'] = _DECIMAL64 +DESCRIPTOR.message_types_by_name['ScalarArray'] = _SCALARARRAY +DESCRIPTOR.message_types_by_name['SubscribeRequest'] = _SUBSCRIBEREQUEST +DESCRIPTOR.message_types_by_name['Poll'] = _POLL +DESCRIPTOR.message_types_by_name['SubscribeResponse'] = _SUBSCRIBERESPONSE +DESCRIPTOR.message_types_by_name['SubscriptionList'] = _SUBSCRIPTIONLIST +DESCRIPTOR.message_types_by_name['Subscription'] = _SUBSCRIPTION +DESCRIPTOR.message_types_by_name['QOSMarking'] = _QOSMARKING +DESCRIPTOR.message_types_by_name['Alias'] = _ALIAS +DESCRIPTOR.message_types_by_name['AliasList'] = _ALIASLIST +DESCRIPTOR.message_types_by_name['SetRequest'] = _SETREQUEST +DESCRIPTOR.message_types_by_name['SetResponse'] = _SETRESPONSE +DESCRIPTOR.message_types_by_name['UpdateResult'] = _UPDATERESULT +DESCRIPTOR.message_types_by_name['GetRequest'] = _GETREQUEST +DESCRIPTOR.message_types_by_name['GetResponse'] = _GETRESPONSE +DESCRIPTOR.message_types_by_name['CapabilityRequest'] = _CAPABILITYREQUEST +DESCRIPTOR.message_types_by_name['CapabilityResponse'] = _CAPABILITYRESPONSE +DESCRIPTOR.message_types_by_name['ModelData'] = _MODELDATA +DESCRIPTOR.enum_types_by_name['Encoding'] = _ENCODING +DESCRIPTOR.enum_types_by_name['SubscriptionMode'] = _SUBSCRIPTIONMODE +DESCRIPTOR.extensions_by_name['gnmi_service'] = gnmi_service +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Notification = _reflection.GeneratedProtocolMessageType('Notification', (_message.Message,), { + 'DESCRIPTOR' : _NOTIFICATION, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Notification) + }) +_sym_db.RegisterMessage(Notification) + +Update = _reflection.GeneratedProtocolMessageType('Update', (_message.Message,), { + 'DESCRIPTOR' : _UPDATE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Update) + }) +_sym_db.RegisterMessage(Update) + +TypedValue = _reflection.GeneratedProtocolMessageType('TypedValue', (_message.Message,), { + 'DESCRIPTOR' : _TYPEDVALUE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.TypedValue) + }) +_sym_db.RegisterMessage(TypedValue) + +Path = _reflection.GeneratedProtocolMessageType('Path', (_message.Message,), { + 'DESCRIPTOR' : _PATH, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Path) + }) +_sym_db.RegisterMessage(Path) + +PathElem = _reflection.GeneratedProtocolMessageType('PathElem', (_message.Message,), { + + 'KeyEntry' : _reflection.GeneratedProtocolMessageType('KeyEntry', (_message.Message,), { + 'DESCRIPTOR' : _PATHELEM_KEYENTRY, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.PathElem.KeyEntry) + }) + , + 'DESCRIPTOR' : _PATHELEM, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.PathElem) + }) +_sym_db.RegisterMessage(PathElem) +_sym_db.RegisterMessage(PathElem.KeyEntry) + +Value = _reflection.GeneratedProtocolMessageType('Value', (_message.Message,), { + 'DESCRIPTOR' : _VALUE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Value) + }) +_sym_db.RegisterMessage(Value) + +Error = _reflection.GeneratedProtocolMessageType('Error', (_message.Message,), { + 'DESCRIPTOR' : _ERROR, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Error) + }) +_sym_db.RegisterMessage(Error) + +Decimal64 = _reflection.GeneratedProtocolMessageType('Decimal64', (_message.Message,), { + 'DESCRIPTOR' : _DECIMAL64, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Decimal64) + }) +_sym_db.RegisterMessage(Decimal64) + +ScalarArray = _reflection.GeneratedProtocolMessageType('ScalarArray', (_message.Message,), { + 'DESCRIPTOR' : _SCALARARRAY, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.ScalarArray) + }) +_sym_db.RegisterMessage(ScalarArray) + +SubscribeRequest = _reflection.GeneratedProtocolMessageType('SubscribeRequest', (_message.Message,), { + 'DESCRIPTOR' : _SUBSCRIBEREQUEST, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.SubscribeRequest) + }) +_sym_db.RegisterMessage(SubscribeRequest) + +Poll = _reflection.GeneratedProtocolMessageType('Poll', (_message.Message,), { + 'DESCRIPTOR' : _POLL, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Poll) + }) +_sym_db.RegisterMessage(Poll) + +SubscribeResponse = _reflection.GeneratedProtocolMessageType('SubscribeResponse', (_message.Message,), { + 'DESCRIPTOR' : _SUBSCRIBERESPONSE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.SubscribeResponse) + }) +_sym_db.RegisterMessage(SubscribeResponse) + +SubscriptionList = _reflection.GeneratedProtocolMessageType('SubscriptionList', (_message.Message,), { + 'DESCRIPTOR' : _SUBSCRIPTIONLIST, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.SubscriptionList) + }) +_sym_db.RegisterMessage(SubscriptionList) + +Subscription = _reflection.GeneratedProtocolMessageType('Subscription', (_message.Message,), { + 'DESCRIPTOR' : _SUBSCRIPTION, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Subscription) + }) +_sym_db.RegisterMessage(Subscription) + +QOSMarking = _reflection.GeneratedProtocolMessageType('QOSMarking', (_message.Message,), { + 'DESCRIPTOR' : _QOSMARKING, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.QOSMarking) + }) +_sym_db.RegisterMessage(QOSMarking) + +Alias = _reflection.GeneratedProtocolMessageType('Alias', (_message.Message,), { + 'DESCRIPTOR' : _ALIAS, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.Alias) + }) +_sym_db.RegisterMessage(Alias) + +AliasList = _reflection.GeneratedProtocolMessageType('AliasList', (_message.Message,), { + 'DESCRIPTOR' : _ALIASLIST, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.AliasList) + }) +_sym_db.RegisterMessage(AliasList) + +SetRequest = _reflection.GeneratedProtocolMessageType('SetRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETREQUEST, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.SetRequest) + }) +_sym_db.RegisterMessage(SetRequest) + +SetResponse = _reflection.GeneratedProtocolMessageType('SetResponse', (_message.Message,), { + 'DESCRIPTOR' : _SETRESPONSE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.SetResponse) + }) +_sym_db.RegisterMessage(SetResponse) + +UpdateResult = _reflection.GeneratedProtocolMessageType('UpdateResult', (_message.Message,), { + 'DESCRIPTOR' : _UPDATERESULT, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.UpdateResult) + }) +_sym_db.RegisterMessage(UpdateResult) + +GetRequest = _reflection.GeneratedProtocolMessageType('GetRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETREQUEST, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.GetRequest) + }) +_sym_db.RegisterMessage(GetRequest) + +GetResponse = _reflection.GeneratedProtocolMessageType('GetResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETRESPONSE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.GetResponse) + }) +_sym_db.RegisterMessage(GetResponse) + +CapabilityRequest = _reflection.GeneratedProtocolMessageType('CapabilityRequest', (_message.Message,), { + 'DESCRIPTOR' : _CAPABILITYREQUEST, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.CapabilityRequest) + }) +_sym_db.RegisterMessage(CapabilityRequest) + +CapabilityResponse = _reflection.GeneratedProtocolMessageType('CapabilityResponse', (_message.Message,), { + 'DESCRIPTOR' : _CAPABILITYRESPONSE, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.CapabilityResponse) + }) +_sym_db.RegisterMessage(CapabilityResponse) + +ModelData = _reflection.GeneratedProtocolMessageType('ModelData', (_message.Message,), { + 'DESCRIPTOR' : _MODELDATA, + '__module__' : 'proto.gnmi.gnmi_pb2' + # @@protoc_insertion_point(class_scope:gnmi.ModelData) + }) +_sym_db.RegisterMessage(ModelData) + +google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(gnmi_service) + +DESCRIPTOR._options = None +_UPDATE.fields_by_name['value']._options = None +_PATH.fields_by_name['element']._options = None +_PATHELEM_KEYENTRY._options = None +_VALUE._options = None +_ERROR._options = None +_SUBSCRIBERESPONSE.fields_by_name['error']._options = None +_SETRESPONSE.fields_by_name['message']._options = None +_UPDATERESULT.fields_by_name['timestamp']._options = None +_UPDATERESULT.fields_by_name['message']._options = None +_GETRESPONSE.fields_by_name['error']._options = None + +_GNMI = _descriptor.ServiceDescriptor( + name='gNMI', + full_name='gnmi.gNMI', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=3608, + serialized_end=3835, + methods=[ + _descriptor.MethodDescriptor( + name='Capabilities', + full_name='gnmi.gNMI.Capabilities', + index=0, + containing_service=None, + input_type=_CAPABILITYREQUEST, + output_type=_CAPABILITYRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='Get', + full_name='gnmi.gNMI.Get', + index=1, + containing_service=None, + input_type=_GETREQUEST, + output_type=_GETRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='Set', + full_name='gnmi.gNMI.Set', + index=2, + containing_service=None, + input_type=_SETREQUEST, + output_type=_SETRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='Subscribe', + full_name='gnmi.gNMI.Subscribe', + index=3, + containing_service=None, + input_type=_SUBSCRIBEREQUEST, + output_type=_SUBSCRIBERESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_GNMI) + +DESCRIPTOR.services_by_name['gNMI'] = _GNMI + +# @@protoc_insertion_point(module_scope) diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_pb2_grpc.py b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_pb2_grpc.py new file mode 100644 index 0000000000000000000000000000000000000000..f9841a797f96cc4fe0f66c93dfc3d757f9a78708 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi/gnmi_pb2_grpc.py @@ -0,0 +1,185 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from proto.gnmi import gnmi_pb2 as proto_dot_gnmi_dot_gnmi__pb2 + + +class gNMIStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Capabilities = channel.unary_unary( + '/gnmi.gNMI/Capabilities', + request_serializer=proto_dot_gnmi_dot_gnmi__pb2.CapabilityRequest.SerializeToString, + response_deserializer=proto_dot_gnmi_dot_gnmi__pb2.CapabilityResponse.FromString, + ) + self.Get = channel.unary_unary( + '/gnmi.gNMI/Get', + request_serializer=proto_dot_gnmi_dot_gnmi__pb2.GetRequest.SerializeToString, + response_deserializer=proto_dot_gnmi_dot_gnmi__pb2.GetResponse.FromString, + ) + self.Set = channel.unary_unary( + '/gnmi.gNMI/Set', + request_serializer=proto_dot_gnmi_dot_gnmi__pb2.SetRequest.SerializeToString, + response_deserializer=proto_dot_gnmi_dot_gnmi__pb2.SetResponse.FromString, + ) + self.Subscribe = channel.stream_stream( + '/gnmi.gNMI/Subscribe', + request_serializer=proto_dot_gnmi_dot_gnmi__pb2.SubscribeRequest.SerializeToString, + response_deserializer=proto_dot_gnmi_dot_gnmi__pb2.SubscribeResponse.FromString, + ) + + +class gNMIServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Capabilities(self, request, context): + """Capabilities allows the client to retrieve the set of capabilities that + is supported by the target. This allows the target to validate the + service version that is implemented and retrieve the set of models that + the target supports. The models can then be specified in subsequent RPCs + to restrict the set of data that is utilized. + Reference: gNMI Specification Section 3.2 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Get(self, request, context): + """Retrieve a snapshot of data from the target. A Get RPC requests that the + target snapshots a subset of the data tree as specified by the paths + included in the message and serializes this to be returned to the + client using the specified encoding. + Reference: gNMI Specification Section 3.3 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Set(self, request, context): + """Set allows the client to modify the state of data on the target. The + paths to modified along with the new values that the client wishes + to set the value to. + Reference: gNMI Specification Section 3.4 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Subscribe(self, request_iterator, context): + """Subscribe allows a client to request the target to send it values + of particular paths within the data tree. These values may be streamed + at a particular cadence (STREAM), sent one off on a long-lived channel + (POLL), or sent as a one-off retrieval (ONCE). + Reference: gNMI Specification Section 3.5 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_gNMIServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Capabilities': grpc.unary_unary_rpc_method_handler( + servicer.Capabilities, + request_deserializer=proto_dot_gnmi_dot_gnmi__pb2.CapabilityRequest.FromString, + response_serializer=proto_dot_gnmi_dot_gnmi__pb2.CapabilityResponse.SerializeToString, + ), + 'Get': grpc.unary_unary_rpc_method_handler( + servicer.Get, + request_deserializer=proto_dot_gnmi_dot_gnmi__pb2.GetRequest.FromString, + response_serializer=proto_dot_gnmi_dot_gnmi__pb2.GetResponse.SerializeToString, + ), + 'Set': grpc.unary_unary_rpc_method_handler( + servicer.Set, + request_deserializer=proto_dot_gnmi_dot_gnmi__pb2.SetRequest.FromString, + response_serializer=proto_dot_gnmi_dot_gnmi__pb2.SetResponse.SerializeToString, + ), + 'Subscribe': grpc.stream_stream_rpc_method_handler( + servicer.Subscribe, + request_deserializer=proto_dot_gnmi_dot_gnmi__pb2.SubscribeRequest.FromString, + response_serializer=proto_dot_gnmi_dot_gnmi__pb2.SubscribeResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'gnmi.gNMI', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class gNMI(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Capabilities(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.gNMI/Capabilities', + proto_dot_gnmi_dot_gnmi__pb2.CapabilityRequest.SerializeToString, + proto_dot_gnmi_dot_gnmi__pb2.CapabilityResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Get(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.gNMI/Get', + proto_dot_gnmi_dot_gnmi__pb2.GetRequest.SerializeToString, + proto_dot_gnmi_dot_gnmi__pb2.GetResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Set(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.gNMI/Set', + proto_dot_gnmi_dot_gnmi__pb2.SetRequest.SerializeToString, + proto_dot_gnmi_dot_gnmi__pb2.SetResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Subscribe(request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.stream_stream(request_iterator, target, '/gnmi.gNMI/Subscribe', + proto_dot_gnmi_dot_gnmi__pb2.SubscribeRequest.SerializeToString, + proto_dot_gnmi_dot_gnmi__pb2.SubscribeResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi_ext/BUILD.bazel b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..2e0e9b4c40c658385c989165a6cee360e318fbaa --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/BUILD.bazel @@ -0,0 +1,31 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Supporting infrastructure for implementing and testing PINS. +# +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +proto_library( + name = "gnmi_ext_proto", + srcs = ["gnmi_ext.proto"], + import_prefix = "github.com/openconfig/gnmi", +) + +cc_proto_library( + name = "gnmi_ext_cc_proto", + deps = [":gnmi_ext_proto"], +) diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.pb.go b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..d07f54a07d7a9129b30ef50c39a657a5bcb089df --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.pb.go @@ -0,0 +1,763 @@ +// +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.12.4 +// source: github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto + +// Package gnmi_ext defines a set of extensions messages which can be optionally +// included with the request and response messages of gNMI RPCs. A set of +// well-known extensions are defined within this file, along with a registry for +// extensions defined outside of this package. + +package gnmi_ext + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// RegisteredExtension is an enumeration acting as a registry for extensions +// defined by external sources. +type ExtensionID int32 + +const ( + ExtensionID_EID_UNSET ExtensionID = 0 + // An experimental extension that may be used during prototyping of a new + // extension. + ExtensionID_EID_EXPERIMENTAL ExtensionID = 999 +) + +// Enum value maps for ExtensionID. +var ( + ExtensionID_name = map[int32]string{ + 0: "EID_UNSET", + 999: "EID_EXPERIMENTAL", + } + ExtensionID_value = map[string]int32{ + "EID_UNSET": 0, + "EID_EXPERIMENTAL": 999, + } +) + +func (x ExtensionID) Enum() *ExtensionID { + p := new(ExtensionID) + *p = x + return p +} + +func (x ExtensionID) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ExtensionID) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_enumTypes[0].Descriptor() +} + +func (ExtensionID) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_enumTypes[0] +} + +func (x ExtensionID) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ExtensionID.Descriptor instead. +func (ExtensionID) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{0} +} + +// The Extension message contains a single gNMI extension. +type Extension struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Ext: + // *Extension_RegisteredExt + // *Extension_MasterArbitration + // *Extension_History + Ext isExtension_Ext `protobuf_oneof:"ext"` +} + +func (x *Extension) Reset() { + *x = Extension{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Extension) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Extension) ProtoMessage() {} + +func (x *Extension) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Extension.ProtoReflect.Descriptor instead. +func (*Extension) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{0} +} + +func (m *Extension) GetExt() isExtension_Ext { + if m != nil { + return m.Ext + } + return nil +} + +func (x *Extension) GetRegisteredExt() *RegisteredExtension { + if x, ok := x.GetExt().(*Extension_RegisteredExt); ok { + return x.RegisteredExt + } + return nil +} + +func (x *Extension) GetMasterArbitration() *MasterArbitration { + if x, ok := x.GetExt().(*Extension_MasterArbitration); ok { + return x.MasterArbitration + } + return nil +} + +func (x *Extension) GetHistory() *History { + if x, ok := x.GetExt().(*Extension_History); ok { + return x.History + } + return nil +} + +type isExtension_Ext interface { + isExtension_Ext() +} + +type Extension_RegisteredExt struct { + RegisteredExt *RegisteredExtension `protobuf:"bytes,1,opt,name=registered_ext,json=registeredExt,proto3,oneof"` // A registered extension. +} + +type Extension_MasterArbitration struct { + // Well known extensions. + MasterArbitration *MasterArbitration `protobuf:"bytes,2,opt,name=master_arbitration,json=masterArbitration,proto3,oneof"` // Master arbitration extension. +} + +type Extension_History struct { + History *History `protobuf:"bytes,3,opt,name=history,proto3,oneof"` // History extension. +} + +func (*Extension_RegisteredExt) isExtension_Ext() {} + +func (*Extension_MasterArbitration) isExtension_Ext() {} + +func (*Extension_History) isExtension_Ext() {} + +// The RegisteredExtension message defines an extension which is defined outside +// of this file. +type RegisteredExtension struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id ExtensionID `protobuf:"varint,1,opt,name=id,proto3,enum=gnmi_ext.ExtensionID" json:"id,omitempty"` // The unique ID assigned to this extension. + Msg []byte `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // The binary-marshalled protobuf extension payload. +} + +func (x *RegisteredExtension) Reset() { + *x = RegisteredExtension{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisteredExtension) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisteredExtension) ProtoMessage() {} + +func (x *RegisteredExtension) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisteredExtension.ProtoReflect.Descriptor instead. +func (*RegisteredExtension) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisteredExtension) GetId() ExtensionID { + if x != nil { + return x.Id + } + return ExtensionID_EID_UNSET +} + +func (x *RegisteredExtension) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +// MasterArbitration is used to select the master among multiple gNMI clients +// with the same Roles. The client with the largest election_id is honored as +// the master. +// The document about gNMI master arbitration can be found at +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md +type MasterArbitration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Role *Role `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"` + ElectionId *Uint128 `protobuf:"bytes,2,opt,name=election_id,json=electionId,proto3" json:"election_id,omitempty"` +} + +func (x *MasterArbitration) Reset() { + *x = MasterArbitration{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MasterArbitration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MasterArbitration) ProtoMessage() {} + +func (x *MasterArbitration) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MasterArbitration.ProtoReflect.Descriptor instead. +func (*MasterArbitration) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{2} +} + +func (x *MasterArbitration) GetRole() *Role { + if x != nil { + return x.Role + } + return nil +} + +func (x *MasterArbitration) GetElectionId() *Uint128 { + if x != nil { + return x.ElectionId + } + return nil +} + +// Representation of unsigned 128-bit integer. +type Uint128 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + High uint64 `protobuf:"varint,1,opt,name=high,proto3" json:"high,omitempty"` + Low uint64 `protobuf:"varint,2,opt,name=low,proto3" json:"low,omitempty"` +} + +func (x *Uint128) Reset() { + *x = Uint128{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Uint128) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Uint128) ProtoMessage() {} + +func (x *Uint128) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Uint128.ProtoReflect.Descriptor instead. +func (*Uint128) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{3} +} + +func (x *Uint128) GetHigh() uint64 { + if x != nil { + return x.High + } + return 0 +} + +func (x *Uint128) GetLow() uint64 { + if x != nil { + return x.Low + } + return 0 +} + +// There can be one master for each role. The role is identified by its id. +type Role struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *Role) Reset() { + *x = Role{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Role) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Role) ProtoMessage() {} + +func (x *Role) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Role.ProtoReflect.Descriptor instead. +func (*Role) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{4} +} + +func (x *Role) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// The History extension allows clients to request historical data. Its +// spec can be found at +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-history.md +type History struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Request: + // *History_SnapshotTime + // *History_Range + Request isHistory_Request `protobuf_oneof:"request"` +} + +func (x *History) Reset() { + *x = History{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *History) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*History) ProtoMessage() {} + +func (x *History) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use History.ProtoReflect.Descriptor instead. +func (*History) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{5} +} + +func (m *History) GetRequest() isHistory_Request { + if m != nil { + return m.Request + } + return nil +} + +func (x *History) GetSnapshotTime() int64 { + if x, ok := x.GetRequest().(*History_SnapshotTime); ok { + return x.SnapshotTime + } + return 0 +} + +func (x *History) GetRange() *TimeRange { + if x, ok := x.GetRequest().(*History_Range); ok { + return x.Range + } + return nil +} + +type isHistory_Request interface { + isHistory_Request() +} + +type History_SnapshotTime struct { + SnapshotTime int64 `protobuf:"varint,1,opt,name=snapshot_time,json=snapshotTime,proto3,oneof"` // Nanoseconds since the epoch +} + +type History_Range struct { + Range *TimeRange `protobuf:"bytes,2,opt,name=range,proto3,oneof"` +} + +func (*History_SnapshotTime) isHistory_Request() {} + +func (*History_Range) isHistory_Request() {} + +type TimeRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` // Nanoseconds since the epoch + End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` // Nanoseconds since the epoch +} + +func (x *TimeRange) Reset() { + *x = TimeRange{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TimeRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeRange) ProtoMessage() {} + +func (x *TimeRange) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeRange.ProtoReflect.Descriptor instead. +func (*TimeRange) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP(), []int{6} +} + +func (x *TimeRange) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *TimeRange) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +var File_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto protoreflect.FileDescriptor + +var file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDesc = []byte{ + 0x0a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2f, 0x67, 0x6e, 0x6d, 0x69, + 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x6e, 0x6d, 0x69, + 0x5f, 0x65, 0x78, 0x74, 0x22, 0xd7, 0x01, 0x0a, 0x09, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x5f, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x45, 0x78, 0x74, 0x12, 0x4c, 0x0a, 0x12, 0x6d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, + 0x74, 0x2e, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x11, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, 0x62, + 0x69, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x48, 0x00, 0x52, 0x07, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x05, 0x0a, 0x03, 0x65, 0x78, 0x74, 0x22, 0x4e, + 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x15, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x6b, + 0x0a, 0x11, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x52, 0x6f, 0x6c, + 0x65, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x32, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, + 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x32, 0x38, 0x52, + 0x0a, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x2f, 0x0a, 0x07, 0x55, + 0x69, 0x6e, 0x74, 0x31, 0x32, 0x38, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, + 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6c, 0x6f, 0x77, 0x22, 0x16, 0x0a, 0x04, + 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x68, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, + 0x25, 0x0a, 0x0d, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x5f, 0x65, 0x78, 0x74, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x33, + 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, + 0x65, 0x6e, 0x64, 0x2a, 0x33, 0x0a, 0x0b, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x49, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x45, 0x54, 0x10, + 0x00, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x52, 0x49, 0x4d, + 0x45, 0x4e, 0x54, 0x41, 0x4c, 0x10, 0xe7, 0x07, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6e, 0x6d, + 0x69, 0x5f, 0x65, 0x78, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescOnce sync.Once + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescData = file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDesc +) + +func file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescGZIP() []byte { + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescOnce.Do(func() { + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescData) + }) + return file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDescData +} + +var file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_goTypes = []interface{}{ + (ExtensionID)(0), // 0: gnmi_ext.ExtensionID + (*Extension)(nil), // 1: gnmi_ext.Extension + (*RegisteredExtension)(nil), // 2: gnmi_ext.RegisteredExtension + (*MasterArbitration)(nil), // 3: gnmi_ext.MasterArbitration + (*Uint128)(nil), // 4: gnmi_ext.Uint128 + (*Role)(nil), // 5: gnmi_ext.Role + (*History)(nil), // 6: gnmi_ext.History + (*TimeRange)(nil), // 7: gnmi_ext.TimeRange +} +var file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_depIdxs = []int32{ + 2, // 0: gnmi_ext.Extension.registered_ext:type_name -> gnmi_ext.RegisteredExtension + 3, // 1: gnmi_ext.Extension.master_arbitration:type_name -> gnmi_ext.MasterArbitration + 6, // 2: gnmi_ext.Extension.history:type_name -> gnmi_ext.History + 0, // 3: gnmi_ext.RegisteredExtension.id:type_name -> gnmi_ext.ExtensionID + 5, // 4: gnmi_ext.MasterArbitration.role:type_name -> gnmi_ext.Role + 4, // 5: gnmi_ext.MasterArbitration.election_id:type_name -> gnmi_ext.Uint128 + 7, // 6: gnmi_ext.History.range:type_name -> gnmi_ext.TimeRange + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_init() } +func file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_init() { + if File_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Extension); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisteredExtension); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MasterArbitration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Uint128); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Role); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*History); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TimeRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Extension_RegisteredExt)(nil), + (*Extension_MasterArbitration)(nil), + (*Extension_History)(nil), + } + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes[5].OneofWrappers = []interface{}{ + (*History_SnapshotTime)(nil), + (*History_Range)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDesc, + NumEnums: 1, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_goTypes, + DependencyIndexes: file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_depIdxs, + EnumInfos: file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_enumTypes, + MessageInfos: file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_msgTypes, + }.Build() + File_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto = out.File + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_rawDesc = nil + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_goTypes = nil + file_github_com_openconfig_gnmi_proto_gnmi_ext_gnmi_ext_proto_depIdxs = nil +} diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto new file mode 100644 index 0000000000000000000000000000000000000000..142fe1d22136a245cda842f6d4c4cc78aed58a6d --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto @@ -0,0 +1,91 @@ +// +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +syntax = "proto3"; + +// Package gnmi_ext defines a set of extensions messages which can be optionally +// included with the request and response messages of gNMI RPCs. A set of +// well-known extensions are defined within this file, along with a registry for +// extensions defined outside of this package. +package gnmi_ext; + +option go_package = "github.com/openconfig/gnmi/proto/gnmi_ext"; + +// The Extension message contains a single gNMI extension. +message Extension { + oneof ext { + RegisteredExtension registered_ext = 1; // A registered extension. + // Well known extensions. + MasterArbitration master_arbitration = 2; // Master arbitration extension. + History history = 3; // History extension. + } +} + +// The RegisteredExtension message defines an extension which is defined outside +// of this file. +message RegisteredExtension { + ExtensionID id = 1; // The unique ID assigned to this extension. + bytes msg = 2; // The binary-marshalled protobuf extension payload. +} + +// RegisteredExtension is an enumeration acting as a registry for extensions +// defined by external sources. +enum ExtensionID { + EID_UNSET = 0; + // New extensions are to be defined within this enumeration - their definition + // MUST link to a reference describing their implementation. + + // An experimental extension that may be used during prototyping of a new + // extension. + EID_EXPERIMENTAL = 999; +} + +// MasterArbitration is used to select the master among multiple gNMI clients +// with the same Roles. The client with the largest election_id is honored as +// the master. +// The document about gNMI master arbitration can be found at +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md +message MasterArbitration { + Role role = 1; + Uint128 election_id = 2; +} + +// Representation of unsigned 128-bit integer. +message Uint128 { + uint64 high = 1; + uint64 low = 2; +} + +// There can be one master for each role. The role is identified by its id. +message Role { + string id = 1; + // More fields can be added if needed, for example, to specify what paths the + // role can read/write. +} + +// The History extension allows clients to request historical data. Its +// spec can be found at +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-history.md +message History { + oneof request { + int64 snapshot_time = 1; // Nanoseconds since the epoch + TimeRange range = 2; + } +} + +message TimeRange { + int64 start = 1; // Nanoseconds since the epoch + int64 end = 2; // Nanoseconds since the epoch +} diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext_pb2.py b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..69f4406b7325c4a38febd6caa861b75877262c49 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext_pb2.py @@ -0,0 +1,422 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/gnmi_ext/gnmi_ext.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='proto/gnmi_ext/gnmi_ext.proto', + package='gnmi_ext', + syntax='proto3', + serialized_options=b'Z)github.com/openconfig/gnmi/proto/gnmi_ext', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x1dproto/gnmi_ext/gnmi_ext.proto\x12\x08gnmi_ext\"\xac\x01\n\tExtension\x12\x37\n\x0eregistered_ext\x18\x01 \x01(\x0b\x32\x1d.gnmi_ext.RegisteredExtensionH\x00\x12\x39\n\x12master_arbitration\x18\x02 \x01(\x0b\x32\x1b.gnmi_ext.MasterArbitrationH\x00\x12$\n\x07history\x18\x03 \x01(\x0b\x32\x11.gnmi_ext.HistoryH\x00\x42\x05\n\x03\x65xt\"E\n\x13RegisteredExtension\x12!\n\x02id\x18\x01 \x01(\x0e\x32\x15.gnmi_ext.ExtensionID\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"Y\n\x11MasterArbitration\x12\x1c\n\x04role\x18\x01 \x01(\x0b\x32\x0e.gnmi_ext.Role\x12&\n\x0b\x65lection_id\x18\x02 \x01(\x0b\x32\x11.gnmi_ext.Uint128\"$\n\x07Uint128\x12\x0c\n\x04high\x18\x01 \x01(\x04\x12\x0b\n\x03low\x18\x02 \x01(\x04\"\x12\n\x04Role\x12\n\n\x02id\x18\x01 \x01(\t\"S\n\x07History\x12\x17\n\rsnapshot_time\x18\x01 \x01(\x03H\x00\x12$\n\x05range\x18\x02 \x01(\x0b\x32\x13.gnmi_ext.TimeRangeH\x00\x42\t\n\x07request\"\'\n\tTimeRange\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03*3\n\x0b\x45xtensionID\x12\r\n\tEID_UNSET\x10\x00\x12\x15\n\x10\x45ID_EXPERIMENTAL\x10\xe7\x07\x42+Z)github.com/openconfig/gnmi/proto/gnmi_extb\x06proto3' +) + +_EXTENSIONID = _descriptor.EnumDescriptor( + name='ExtensionID', + full_name='gnmi_ext.ExtensionID', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='EID_UNSET', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EID_EXPERIMENTAL', index=1, number=999, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=564, + serialized_end=615, +) +_sym_db.RegisterEnumDescriptor(_EXTENSIONID) + +ExtensionID = enum_type_wrapper.EnumTypeWrapper(_EXTENSIONID) +EID_UNSET = 0 +EID_EXPERIMENTAL = 999 + + + +_EXTENSION = _descriptor.Descriptor( + name='Extension', + full_name='gnmi_ext.Extension', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='registered_ext', full_name='gnmi_ext.Extension.registered_ext', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='master_arbitration', full_name='gnmi_ext.Extension.master_arbitration', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='history', full_name='gnmi_ext.Extension.history', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='ext', full_name='gnmi_ext.Extension.ext', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=44, + serialized_end=216, +) + + +_REGISTEREDEXTENSION = _descriptor.Descriptor( + name='RegisteredExtension', + full_name='gnmi_ext.RegisteredExtension', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='gnmi_ext.RegisteredExtension.id', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='msg', full_name='gnmi_ext.RegisteredExtension.msg', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=218, + serialized_end=287, +) + + +_MASTERARBITRATION = _descriptor.Descriptor( + name='MasterArbitration', + full_name='gnmi_ext.MasterArbitration', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='role', full_name='gnmi_ext.MasterArbitration.role', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='election_id', full_name='gnmi_ext.MasterArbitration.election_id', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=289, + serialized_end=378, +) + + +_UINT128 = _descriptor.Descriptor( + name='Uint128', + full_name='gnmi_ext.Uint128', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='high', full_name='gnmi_ext.Uint128.high', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='low', full_name='gnmi_ext.Uint128.low', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=380, + serialized_end=416, +) + + +_ROLE = _descriptor.Descriptor( + name='Role', + full_name='gnmi_ext.Role', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='gnmi_ext.Role.id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=418, + serialized_end=436, +) + + +_HISTORY = _descriptor.Descriptor( + name='History', + full_name='gnmi_ext.History', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='snapshot_time', full_name='gnmi_ext.History.snapshot_time', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='range', full_name='gnmi_ext.History.range', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='request', full_name='gnmi_ext.History.request', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=438, + serialized_end=521, +) + + +_TIMERANGE = _descriptor.Descriptor( + name='TimeRange', + full_name='gnmi_ext.TimeRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='gnmi_ext.TimeRange.start', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='gnmi_ext.TimeRange.end', index=1, + number=2, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=523, + serialized_end=562, +) + +_EXTENSION.fields_by_name['registered_ext'].message_type = _REGISTEREDEXTENSION +_EXTENSION.fields_by_name['master_arbitration'].message_type = _MASTERARBITRATION +_EXTENSION.fields_by_name['history'].message_type = _HISTORY +_EXTENSION.oneofs_by_name['ext'].fields.append( + _EXTENSION.fields_by_name['registered_ext']) +_EXTENSION.fields_by_name['registered_ext'].containing_oneof = _EXTENSION.oneofs_by_name['ext'] +_EXTENSION.oneofs_by_name['ext'].fields.append( + _EXTENSION.fields_by_name['master_arbitration']) +_EXTENSION.fields_by_name['master_arbitration'].containing_oneof = _EXTENSION.oneofs_by_name['ext'] +_EXTENSION.oneofs_by_name['ext'].fields.append( + _EXTENSION.fields_by_name['history']) +_EXTENSION.fields_by_name['history'].containing_oneof = _EXTENSION.oneofs_by_name['ext'] +_REGISTEREDEXTENSION.fields_by_name['id'].enum_type = _EXTENSIONID +_MASTERARBITRATION.fields_by_name['role'].message_type = _ROLE +_MASTERARBITRATION.fields_by_name['election_id'].message_type = _UINT128 +_HISTORY.fields_by_name['range'].message_type = _TIMERANGE +_HISTORY.oneofs_by_name['request'].fields.append( + _HISTORY.fields_by_name['snapshot_time']) +_HISTORY.fields_by_name['snapshot_time'].containing_oneof = _HISTORY.oneofs_by_name['request'] +_HISTORY.oneofs_by_name['request'].fields.append( + _HISTORY.fields_by_name['range']) +_HISTORY.fields_by_name['range'].containing_oneof = _HISTORY.oneofs_by_name['request'] +DESCRIPTOR.message_types_by_name['Extension'] = _EXTENSION +DESCRIPTOR.message_types_by_name['RegisteredExtension'] = _REGISTEREDEXTENSION +DESCRIPTOR.message_types_by_name['MasterArbitration'] = _MASTERARBITRATION +DESCRIPTOR.message_types_by_name['Uint128'] = _UINT128 +DESCRIPTOR.message_types_by_name['Role'] = _ROLE +DESCRIPTOR.message_types_by_name['History'] = _HISTORY +DESCRIPTOR.message_types_by_name['TimeRange'] = _TIMERANGE +DESCRIPTOR.enum_types_by_name['ExtensionID'] = _EXTENSIONID +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Extension = _reflection.GeneratedProtocolMessageType('Extension', (_message.Message,), { + 'DESCRIPTOR' : _EXTENSION, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.Extension) + }) +_sym_db.RegisterMessage(Extension) + +RegisteredExtension = _reflection.GeneratedProtocolMessageType('RegisteredExtension', (_message.Message,), { + 'DESCRIPTOR' : _REGISTEREDEXTENSION, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.RegisteredExtension) + }) +_sym_db.RegisterMessage(RegisteredExtension) + +MasterArbitration = _reflection.GeneratedProtocolMessageType('MasterArbitration', (_message.Message,), { + 'DESCRIPTOR' : _MASTERARBITRATION, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.MasterArbitration) + }) +_sym_db.RegisterMessage(MasterArbitration) + +Uint128 = _reflection.GeneratedProtocolMessageType('Uint128', (_message.Message,), { + 'DESCRIPTOR' : _UINT128, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.Uint128) + }) +_sym_db.RegisterMessage(Uint128) + +Role = _reflection.GeneratedProtocolMessageType('Role', (_message.Message,), { + 'DESCRIPTOR' : _ROLE, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.Role) + }) +_sym_db.RegisterMessage(Role) + +History = _reflection.GeneratedProtocolMessageType('History', (_message.Message,), { + 'DESCRIPTOR' : _HISTORY, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.History) + }) +_sym_db.RegisterMessage(History) + +TimeRange = _reflection.GeneratedProtocolMessageType('TimeRange', (_message.Message,), { + 'DESCRIPTOR' : _TIMERANGE, + '__module__' : 'proto.gnmi_ext.gnmi_ext_pb2' + # @@protoc_insertion_point(class_scope:gnmi_ext.TimeRange) + }) +_sym_db.RegisterMessage(TimeRange) + + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext_pb2_grpc.py b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext_pb2_grpc.py new file mode 100644 index 0000000000000000000000000000000000000000..2daafffebfc817aefe8fcb96eaec25e65b3903e8 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/deps/github.com/openconfig/gnmi/proto/target/BUILD.bazel b/deps/github.com/openconfig/gnmi/proto/target/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..1a0619f54888f432e31aef04e937d4f212c707d0 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/target/BUILD.bazel @@ -0,0 +1,36 @@ +#Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Supporting infrastructure for implementing and testing PINS. +# + +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +proto_library( + name = "target_proto", + srcs = ["target.proto"], + deps = [ + "//proto/gnmi:gnmi_proto", + ], +) + +cc_proto_library( + name = "target_cc_proto", + deps = [":target_proto"], +) diff --git a/deps/github.com/openconfig/gnmi/proto/target/target.pb.go b/deps/github.com/openconfig/gnmi/proto/target/target.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..d511ee51362127c087801002ec78bef4408d2599 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/target/target.pb.go @@ -0,0 +1,431 @@ +// +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.12.4 +// source: github.com/openconfig/gnmi/proto/target/target.proto + +// Package target contains messages for defining a configuration of a caching +// collector to connect to multiple gNMI targets. + +package target + +import ( + gnmi "github.com/openconfig/gnmi/proto/gnmi" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Configuration holds all information necessary for a caching gNMI collector +// to establish subscriptions to a list of gNMI targets. +type Configuration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Request is a keyed list of all SubscriptionRequests that can be sent to + // to targets in the Configuration. + // The request must have at minimum a SubscriptionList with a prefix + // containing origin and one or more Subscriptions. Only the STREAM mode is + // supported. + Request map[string]*gnmi.SubscribeRequest `protobuf:"bytes,1,rep,name=request,proto3" json:"request,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Target is the full list of targets connected to by a caching gNMI + // collector. The key of the map is a unique name to identify a target and + // is set in the prefix.target of a SubscriptionRequest message when connecting + // to each respective target. + Target map[string]*Target `protobuf:"bytes,2,rep,name=target,proto3" json:"target,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Identifier for the caching collector. + InstanceId string `protobuf:"bytes,3,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + // Revision for this Configuration. Systems that non-atomically write + // configuration should populate and require revision, leveraging canonical + // protobuf serialization of fields in order. Presence of this field makes no + // guarantee. Consumers should account for atomicity constraints of their + // environment and any custom encoding. + Revision int64 `protobuf:"varint,536870911,opt,name=revision,proto3" json:"revision,omitempty"` +} + +func (x *Configuration) Reset() { + *x = Configuration{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configuration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configuration) ProtoMessage() {} + +func (x *Configuration) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configuration.ProtoReflect.Descriptor instead. +func (*Configuration) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescGZIP(), []int{0} +} + +func (x *Configuration) GetRequest() map[string]*gnmi.SubscribeRequest { + if x != nil { + return x.Request + } + return nil +} + +func (x *Configuration) GetTarget() map[string]*Target { + if x != nil { + return x.Target + } + return nil +} + +func (x *Configuration) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +func (x *Configuration) GetRevision() int64 { + if x != nil { + return x.Revision + } + return 0 +} + +// Target is the information necessary to establish a single gNMI Subscribe RPC +// to be collected and cached. +type Target struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A list of address and port or name that resolves to an address and port. + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + // Credentials to use in metadata for authorization of the RPC + Credentials *Credentials `protobuf:"bytes,2,opt,name=credentials,proto3" json:"credentials,omitempty"` + // The request to be sent to the target. The string supplied is looked up in + // the request map of the Configuration message. + Request string `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + // Additional target metadata. + Meta map[string]string `protobuf:"bytes,4,rep,name=meta,proto3" json:"meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Target) Reset() { + *x = Target{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Target) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Target) ProtoMessage() {} + +func (x *Target) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Target.ProtoReflect.Descriptor instead. +func (*Target) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescGZIP(), []int{1} +} + +func (x *Target) GetAddresses() []string { + if x != nil { + return x.Addresses + } + return nil +} + +func (x *Target) GetCredentials() *Credentials { + if x != nil { + return x.Credentials + } + return nil +} + +func (x *Target) GetRequest() string { + if x != nil { + return x.Request + } + return "" +} + +func (x *Target) GetMeta() map[string]string { + if x != nil { + return x.Meta + } + return nil +} + +// Credentials contains the fields necessary for authentication of the client to +// the target. +type Credentials struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + // Password lookup ID. + PasswordId string `protobuf:"bytes,3,opt,name=password_id,json=passwordId,proto3" json:"password_id,omitempty"` +} + +func (x *Credentials) Reset() { + *x = Credentials{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Credentials) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Credentials) ProtoMessage() {} + +func (x *Credentials) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Credentials.ProtoReflect.Descriptor instead. +func (*Credentials) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescGZIP(), []int{2} +} + +func (x *Credentials) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Credentials) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Credentials) GetPasswordId() string { + if x != nil { + return x.PasswordId + } + return "" +} + +var File_github_com_openconfig_gnmi_proto_target_target_proto protoreflect.FileDescriptor + +var file_github_com_openconfig_gnmi_proto_target_target_proto_rawDesc = []byte{ + 0x0a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x1a, 0x30, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0xe8, 0x02, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x39, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x08, + 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xff, 0xff, 0xff, 0xff, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x52, 0x0a, 0x0c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x49, 0x0a, 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xde, 0x01, 0x0a, 0x06, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x0b, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x6d, + 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x66, 0x0a, 0x0b, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x49, 0x64, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, + 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescOnce sync.Once + file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescData = file_github_com_openconfig_gnmi_proto_target_target_proto_rawDesc +) + +func file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescGZIP() []byte { + file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescOnce.Do(func() { + file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescData) + }) + return file_github_com_openconfig_gnmi_proto_target_target_proto_rawDescData +} + +var file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_github_com_openconfig_gnmi_proto_target_target_proto_goTypes = []interface{}{ + (*Configuration)(nil), // 0: target.Configuration + (*Target)(nil), // 1: target.Target + (*Credentials)(nil), // 2: target.Credentials + nil, // 3: target.Configuration.RequestEntry + nil, // 4: target.Configuration.TargetEntry + nil, // 5: target.Target.MetaEntry + (*gnmi.SubscribeRequest)(nil), // 6: gnmi.SubscribeRequest +} +var file_github_com_openconfig_gnmi_proto_target_target_proto_depIdxs = []int32{ + 3, // 0: target.Configuration.request:type_name -> target.Configuration.RequestEntry + 4, // 1: target.Configuration.target:type_name -> target.Configuration.TargetEntry + 2, // 2: target.Target.credentials:type_name -> target.Credentials + 5, // 3: target.Target.meta:type_name -> target.Target.MetaEntry + 6, // 4: target.Configuration.RequestEntry.value:type_name -> gnmi.SubscribeRequest + 1, // 5: target.Configuration.TargetEntry.value:type_name -> target.Target + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_github_com_openconfig_gnmi_proto_target_target_proto_init() } +func file_github_com_openconfig_gnmi_proto_target_target_proto_init() { + if File_github_com_openconfig_gnmi_proto_target_target_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Target); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Credentials); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_openconfig_gnmi_proto_target_target_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_openconfig_gnmi_proto_target_target_proto_goTypes, + DependencyIndexes: file_github_com_openconfig_gnmi_proto_target_target_proto_depIdxs, + MessageInfos: file_github_com_openconfig_gnmi_proto_target_target_proto_msgTypes, + }.Build() + File_github_com_openconfig_gnmi_proto_target_target_proto = out.File + file_github_com_openconfig_gnmi_proto_target_target_proto_rawDesc = nil + file_github_com_openconfig_gnmi_proto_target_target_proto_goTypes = nil + file_github_com_openconfig_gnmi_proto_target_target_proto_depIdxs = nil +} diff --git a/deps/github.com/openconfig/gnmi/proto/target/target.proto b/deps/github.com/openconfig/gnmi/proto/target/target.proto new file mode 100644 index 0000000000000000000000000000000000000000..5992bc562aa5ac39f824cc2a2dd2b34fa4501acb --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/target/target.proto @@ -0,0 +1,72 @@ +// +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +syntax = "proto3"; + +// Package target contains messages for defining a configuration of a caching +// collector to connect to multiple gNMI targets. +package target; + +import "github.com/openconfig/gnmi/proto/gnmi/gnmi.proto"; + +option go_package = "github.com/openconfig/gnmi/proto/target"; + +// Configuration holds all information necessary for a caching gNMI collector +// to establish subscriptions to a list of gNMI targets. +message Configuration { + // Request is a keyed list of all SubscriptionRequests that can be sent to + // to targets in the Configuration. + // The request must have at minimum a SubscriptionList with a prefix + // containing origin and one or more Subscriptions. Only the STREAM mode is + // supported. + map<string, gnmi.SubscribeRequest> request = 1; + // Target is the full list of targets connected to by a caching gNMI + // collector. The key of the map is a unique name to identify a target and + // is set in the prefix.target of a SubscriptionRequest message when connecting + // to each respective target. + map<string, Target> target = 2; + // Identifier for the caching collector. + string instance_id = 3; + // Revision for this Configuration. Systems that non-atomically write + // configuration should populate and require revision, leveraging canonical + // protobuf serialization of fields in order. Presence of this field makes no + // guarantee. Consumers should account for atomicity constraints of their + // environment and any custom encoding. + int64 revision = 536870911; + +} + +// Target is the information necessary to establish a single gNMI Subscribe RPC +// to be collected and cached. +message Target { + // A list of address and port or name that resolves to an address and port. + repeated string addresses = 1; + // Credentials to use in metadata for authorization of the RPC + Credentials credentials = 2; + // The request to be sent to the target. The string supplied is looked up in + // the request map of the Configuration message. + string request = 3; + // Additional target metadata. + map<string, string> meta = 4; +} + +// Credentials contains the fields necessary for authentication of the client to +// the target. +message Credentials { + string username = 1; + string password = 2; + // Password lookup ID. + string password_id = 3; +} diff --git a/deps/github.com/openconfig/gnmi/proto/target/target_pb2.py b/deps/github.com/openconfig/gnmi/proto/target/target_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..f1e801bee1d5689bd092d69d8ded5323b276b764 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/target/target_pb2.py @@ -0,0 +1,359 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/target/target.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from github.com.openconfig.gnmi.proto.gnmi import gnmi_pb2 as github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi_dot_gnmi__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='proto/target/target.proto', + package='target', + syntax='proto3', + serialized_options=b'Z\'github.com/openconfig/gnmi/proto/target', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x19proto/target/target.proto\x12\x06target\x1a\x30github.com/openconfig/gnmi/proto/gnmi/gnmi.proto\"\xa9\x02\n\rConfiguration\x12\x33\n\x07request\x18\x01 \x03(\x0b\x32\".target.Configuration.RequestEntry\x12\x31\n\x06target\x18\x02 \x03(\x0b\x32!.target.Configuration.TargetEntry\x12\x13\n\x0binstance_id\x18\x03 \x01(\t\x12\x14\n\x08revision\x18\xff\xff\xff\xff\x01 \x01(\x03\x1a\x46\n\x0cRequestEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x05value\x18\x02 \x01(\x0b\x32\x16.gnmi.SubscribeRequest:\x02\x38\x01\x1a=\n\x0bTargetEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.target.Target:\x02\x38\x01\"\xab\x01\n\x06Target\x12\x11\n\taddresses\x18\x01 \x03(\t\x12(\n\x0b\x63redentials\x18\x02 \x01(\x0b\x32\x13.target.Credentials\x12\x0f\n\x07request\x18\x03 \x01(\t\x12&\n\x04meta\x18\x04 \x03(\x0b\x32\x18.target.Target.MetaEntry\x1a+\n\tMetaEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"F\n\x0b\x43redentials\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\x12\x13\n\x0bpassword_id\x18\x03 \x01(\tB)Z\'github.com/openconfig/gnmi/proto/targetb\x06proto3' + , + dependencies=[github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi_dot_gnmi__pb2.DESCRIPTOR,]) + + + + +_CONFIGURATION_REQUESTENTRY = _descriptor.Descriptor( + name='RequestEntry', + full_name='target.Configuration.RequestEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='target.Configuration.RequestEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='target.Configuration.RequestEntry.value', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=252, + serialized_end=322, +) + +_CONFIGURATION_TARGETENTRY = _descriptor.Descriptor( + name='TargetEntry', + full_name='target.Configuration.TargetEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='target.Configuration.TargetEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='target.Configuration.TargetEntry.value', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=324, + serialized_end=385, +) + +_CONFIGURATION = _descriptor.Descriptor( + name='Configuration', + full_name='target.Configuration', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='request', full_name='target.Configuration.request', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='target', full_name='target.Configuration.target', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='instance_id', full_name='target.Configuration.instance_id', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='revision', full_name='target.Configuration.revision', index=3, + number=536870911, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_CONFIGURATION_REQUESTENTRY, _CONFIGURATION_TARGETENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=88, + serialized_end=385, +) + + +_TARGET_METAENTRY = _descriptor.Descriptor( + name='MetaEntry', + full_name='target.Target.MetaEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='target.Target.MetaEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='target.Target.MetaEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=516, + serialized_end=559, +) + +_TARGET = _descriptor.Descriptor( + name='Target', + full_name='target.Target', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='addresses', full_name='target.Target.addresses', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='credentials', full_name='target.Target.credentials', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='request', full_name='target.Target.request', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='meta', full_name='target.Target.meta', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_TARGET_METAENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=388, + serialized_end=559, +) + + +_CREDENTIALS = _descriptor.Descriptor( + name='Credentials', + full_name='target.Credentials', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='username', full_name='target.Credentials.username', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='password', full_name='target.Credentials.password', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='password_id', full_name='target.Credentials.password_id', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=561, + serialized_end=631, +) + +_CONFIGURATION_REQUESTENTRY.fields_by_name['value'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi_dot_gnmi__pb2._SUBSCRIBEREQUEST +_CONFIGURATION_REQUESTENTRY.containing_type = _CONFIGURATION +_CONFIGURATION_TARGETENTRY.fields_by_name['value'].message_type = _TARGET +_CONFIGURATION_TARGETENTRY.containing_type = _CONFIGURATION +_CONFIGURATION.fields_by_name['request'].message_type = _CONFIGURATION_REQUESTENTRY +_CONFIGURATION.fields_by_name['target'].message_type = _CONFIGURATION_TARGETENTRY +_TARGET_METAENTRY.containing_type = _TARGET +_TARGET.fields_by_name['credentials'].message_type = _CREDENTIALS +_TARGET.fields_by_name['meta'].message_type = _TARGET_METAENTRY +DESCRIPTOR.message_types_by_name['Configuration'] = _CONFIGURATION +DESCRIPTOR.message_types_by_name['Target'] = _TARGET +DESCRIPTOR.message_types_by_name['Credentials'] = _CREDENTIALS +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Configuration = _reflection.GeneratedProtocolMessageType('Configuration', (_message.Message,), { + + 'RequestEntry' : _reflection.GeneratedProtocolMessageType('RequestEntry', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGURATION_REQUESTENTRY, + '__module__' : 'proto.target.target_pb2' + # @@protoc_insertion_point(class_scope:target.Configuration.RequestEntry) + }) + , + + 'TargetEntry' : _reflection.GeneratedProtocolMessageType('TargetEntry', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGURATION_TARGETENTRY, + '__module__' : 'proto.target.target_pb2' + # @@protoc_insertion_point(class_scope:target.Configuration.TargetEntry) + }) + , + 'DESCRIPTOR' : _CONFIGURATION, + '__module__' : 'proto.target.target_pb2' + # @@protoc_insertion_point(class_scope:target.Configuration) + }) +_sym_db.RegisterMessage(Configuration) +_sym_db.RegisterMessage(Configuration.RequestEntry) +_sym_db.RegisterMessage(Configuration.TargetEntry) + +Target = _reflection.GeneratedProtocolMessageType('Target', (_message.Message,), { + + 'MetaEntry' : _reflection.GeneratedProtocolMessageType('MetaEntry', (_message.Message,), { + 'DESCRIPTOR' : _TARGET_METAENTRY, + '__module__' : 'proto.target.target_pb2' + # @@protoc_insertion_point(class_scope:target.Target.MetaEntry) + }) + , + 'DESCRIPTOR' : _TARGET, + '__module__' : 'proto.target.target_pb2' + # @@protoc_insertion_point(class_scope:target.Target) + }) +_sym_db.RegisterMessage(Target) +_sym_db.RegisterMessage(Target.MetaEntry) + +Credentials = _reflection.GeneratedProtocolMessageType('Credentials', (_message.Message,), { + 'DESCRIPTOR' : _CREDENTIALS, + '__module__' : 'proto.target.target_pb2' + # @@protoc_insertion_point(class_scope:target.Credentials) + }) +_sym_db.RegisterMessage(Credentials) + + +DESCRIPTOR._options = None +_CONFIGURATION_REQUESTENTRY._options = None +_CONFIGURATION_TARGETENTRY._options = None +_TARGET_METAENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/deps/github.com/openconfig/gnmi/proto/target/target_pb2_grpc.py b/deps/github.com/openconfig/gnmi/proto/target/target_pb2_grpc.py new file mode 100644 index 0000000000000000000000000000000000000000..2daafffebfc817aefe8fcb96eaec25e65b3903e8 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/proto/target/target_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/deps/github.com/openconfig/gnmi/subscribe/subscribe.go b/deps/github.com/openconfig/gnmi/subscribe/subscribe.go new file mode 100644 index 0000000000000000000000000000000000000000..d85cff19e48f23facdc598166a7c54824362c505 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/subscribe/subscribe.go @@ -0,0 +1,493 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package subscribe implements the gnmi.proto Subscribe service API. +package subscribe + +import ( + "context" + "errors" + "io" + "time" + + log "github.com/golang/glog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/cache" + "github.com/openconfig/gnmi/coalesce" + "github.com/openconfig/gnmi/ctree" + "github.com/openconfig/gnmi/match" + "github.com/openconfig/gnmi/path" + + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +var ( + // Value overridden in tests to simulate flow control. + flowControlTest = func() {} + // Timeout specifies how long a send can be pending before the RPC is closed. + Timeout = time.Minute + // SubscriptionLimit specifies how many queries can be processing simultaneously. + // This number includes Once queries, Polling subscriptions, and Streaming + // subscriptions that have not yet synced. Once a streaming subscription has + // synced, it no longer counts against the limit. A polling subscription + // counts against the limit during each polling cycle while it is processed. + SubscriptionLimit = 0 + // Value overridden in tests to evaluate SubscriptionLimit enforcement. + subscriptionLimitTest = func() {} +) + +type aclStub struct{} + +func (a *aclStub) Check(string) bool { + return true +} + +// RPCACL is the per RPC ACL interface +type RPCACL interface { + Check(string) bool +} + +// ACL is server ACL interface +type ACL interface { + NewRPCACL(context.Context) (RPCACL, error) + Check(string, string) bool +} + +// Server is the implementation of the gNMI Subcribe API. +type Server struct { + pb.UnimplementedGNMIServer // Stub out all RPCs except Subscribe. + + c *cache.Cache // The cache queries are performed against. + m *match.Match // Structure to match updates against active subscriptions. + a ACL // server ACL. + // subscribeSlots is a channel of size SubscriptionLimit to restrict how many + // queries are in flight. + subscribeSlots chan struct{} + timeout time.Duration +} + +// NewServer instantiates server to handle client queries. The cache should be +// already instantiated. +func NewServer(c *cache.Cache) (*Server, error) { + s := &Server{c: c, m: match.New(), timeout: Timeout} + if SubscriptionLimit > 0 { + s.subscribeSlots = make(chan struct{}, SubscriptionLimit) + } + return s, nil +} + +// SetACL sets server ACL. This method is called before server starts to run. +func (s *Server) SetACL(a ACL) { + s.a = a +} + +// UpdateNotification uses paths in a pb.Notification n to match registered +// clients in m and pass value v to those clients. +// prefix is the prefix of n that should be used to match clients in m. +// Depending on the caller, the target may or may not be in prefix. +// v should be n itself or the container of n (e.g. a ctree.Leaf) depending +// on the caller. +func UpdateNotification(m *match.Match, v interface{}, n *pb.Notification, prefix []string) { + var updated map[match.Client]struct{} + if len(n.Update) + len(n.Delete) > 1 { + updated = make(map[match.Client]struct{}) + } + for _, u := range n.Update { + m.UpdateOnce(v, append(prefix, path.ToStrings(u.Path, false)...), updated) + } + for _, d := range n.Delete { + m.UpdateOnce(v, append(prefix, path.ToStrings(d, false)...), updated) + } +} + +// Update passes a streaming update to registered clients. +func (s *Server) Update(n *ctree.Leaf) { + switch v := n.Value().(type) { + case *pb.Notification: + UpdateNotification(s.m, n, v, path.ToStrings(v.Prefix, true)) + default: + log.Errorf("update is not a known type; type is %T", v) + } +} + +// addSubscription registers all subscriptions for this client for update matching. +func addSubscription(m *match.Match, s *pb.SubscriptionList, c *matchClient) (remove func()) { + var removes []func() + prefix := path.ToStrings(s.Prefix, true) + for _, p := range s.Subscription { + if p.Path == nil { + continue + } + // TODO(yusufsn) : Origin field in the Path may need to be included + path := append(prefix, path.ToStrings(p.Path, false)...) + removes = append(removes, m.AddQuery(path, c)) + } + return func() { + for _, remove := range removes { + remove() + } + } +} + +// Subscribe is the entry point for the external RPC request of the same name +// defined in gnmi.proto. +func (s *Server) Subscribe(stream pb.GNMI_SubscribeServer) error { + c := streamClient{stream: stream, acl: &aclStub{}} + var err error + if s.a != nil { + a, err := s.a.NewRPCACL(stream.Context()) + if err != nil { + log.Errorf("NewRPCACL fails due to %v", err) + return status.Error(codes.Unauthenticated, "no authentication/authorization for requested operation") + } + c.acl = a + } + c.sr, err = stream.Recv() + + switch { + case err == io.EOF: + return nil + case err != nil: + return err + case c.sr.GetSubscribe() == nil: + return status.Errorf(codes.InvalidArgument, "request must contain a subscription %#v", c.sr) + case c.sr.GetSubscribe().GetPrefix() == nil: + return status.Errorf(codes.InvalidArgument, "request must contain a prefix %#v", c.sr) + case c.sr.GetSubscribe().GetPrefix().GetTarget() == "": + return status.Error(codes.InvalidArgument, "missing target") + } + + c.target = c.sr.GetSubscribe().GetPrefix().GetTarget() + if !s.c.HasTarget(c.target) { + return status.Errorf(codes.NotFound, "no such target: %q", c.target) + } + peer, _ := peer.FromContext(stream.Context()) + mode := c.sr.GetSubscribe().Mode + + log.Infof("peer: %v target: %q subscription: %s", peer.Addr, c.target, c.sr) + defer log.Infof("peer: %v target %q subscription: end: %q", peer.Addr, c.target, c.sr) + + c.queue = coalesce.NewQueue() + defer c.queue.Close() + + // reject single device subscription if not allowed by ACL + if c.target != "*" && !c.acl.Check(c.target) { + return status.Errorf(codes.PermissionDenied, "not authorized for target %q", c.target) + } + // This error channel is buffered to accept errors from all goroutines spawned + // for this RPC. Only the first is ever read and returned causing the RPC to + // terminate. + errC := make(chan error, 3) + c.errC = errC + + switch mode { + case pb.SubscriptionList_ONCE: + go func() { + s.processSubscription(&c) + c.queue.Close() + }() + case pb.SubscriptionList_POLL: + go s.processPollingSubscription(&c) + case pb.SubscriptionList_STREAM: + if c.sr.GetSubscribe().GetUpdatesOnly() { + c.queue.Insert(syncMarker{}) + } + remove := addSubscription(s.m, c.sr.GetSubscribe(), + &matchClient{acl: c.acl, q: c.queue}) + defer remove() + if !c.sr.GetSubscribe().GetUpdatesOnly() { + go s.processSubscription(&c) + } + default: + return status.Errorf(codes.InvalidArgument, "Subscription mode %v not recognized", mode) + } + + go s.sendStreamingResults(&c) + + return <-errC +} + +type resp struct { + stream pb.GNMI_SubscribeServer + n *ctree.Leaf + dup uint32 + t *time.Timer // Timer used to timout the subscription. +} + +// sendSubscribeResponse populates and sends a single response returned on +// the Subscription RPC output stream. Streaming queries send responses for the +// initial walk of the results as well as streamed updates and use a queue to +// ensure order. +func (s *Server) sendSubscribeResponse(r *resp, c *streamClient) error { + notification, err := MakeSubscribeResponse(r.n.Value(), r.dup) + if err != nil { + return status.Errorf(codes.Unknown, err.Error()) + } + + if pre := notification.GetUpdate().GetPrefix(); pre != nil { + if !c.acl.Check(pre.GetTarget()) { + // reaching here means notification is denied for sending. + // return with no error. function caller can continue for next one. + return nil + } + } + + // Start the timeout before attempting to send. + r.t.Reset(s.timeout) + // Clear the timeout upon sending. + defer r.t.Stop() + // An empty function in production, replaced in test to simulate flow control + // by blocking before send. + flowControlTest() + return r.stream.Send(notification) +} + +// subscribeSync is a response indicating that a Subscribe RPC has successfully +// returned all matching nodes once for ONCE and POLLING queries and at least +// once for STREAMING queries. +var subscribeSync = &pb.SubscribeResponse{Response: &pb.SubscribeResponse_SyncResponse{true}} + +type syncMarker struct{} + +// cacheClient implements match.Client interface. +type matchClient struct { + acl RPCACL + q *coalesce.Queue + err error +} + +// Update implements the match.Client Update interface for coalesce.Queue. +func (c matchClient) Update(n interface{}) { + // Stop processing updates on error. + if c.err != nil { + return + } + _, c.err = c.q.Insert(n) +} + +type streamClient struct { + acl RPCACL + target string + sr *pb.SubscribeRequest + queue *coalesce.Queue + stream pb.GNMI_SubscribeServer + errC chan<- error +} + +// processSubscription walks the cache tree and inserts all of the matching +// nodes into the coalesce queue followed by a subscriptionSync response. +func (s *Server) processSubscription(c *streamClient) { + var err error + log.V(2).Infof("start processSubscription for %p", c) + // Close the cache client queue on error. + defer func() { + if err != nil { + log.Error(err) + c.queue.Close() + c.errC <- err + } + log.V(2).Infof("end processSubscription for %p", c) + }() + if s.subscribeSlots != nil { + select { + // Register a subscription in the channel, which will block if SubscriptionLimit queries + // are already in flight. + case s.subscribeSlots <- struct{}{}: + default: + log.V(2).Infof("subscription %s delayed waiting for 1 of %d subscription slots.", c.sr, len(s.subscribeSlots)) + s.subscribeSlots <- struct{}{} + log.V(2).Infof("subscription %s resumed", c.sr) + } + // Remove subscription from the channel upon completion. + defer func() { + // Artificially hold subscription processing in tests to synchronously test limit. + subscriptionLimitTest() + <-s.subscribeSlots + }() + } + if !c.sr.GetSubscribe().GetUpdatesOnly() { + for _, subscription := range c.sr.GetSubscribe().Subscription { + var fullPath []string + fullPath, err = path.CompletePath(c.sr.GetSubscribe().GetPrefix(), subscription.GetPath()) + if err != nil { + return + } + // Note that fullPath doesn't contain target name as the first element. + s.c.Query(c.target, fullPath, func(_ []string, l *ctree.Leaf, _ interface{}) error { + // Stop processing query results on error. + if err != nil { + return err + } + _, err = c.queue.Insert(l) + return nil + }) + if err != nil { + return + } + } + } + + _, err = c.queue.Insert(syncMarker{}) +} + +// processPollingSubscription handles the POLL mode Subscription RPC. +func (s *Server) processPollingSubscription(c *streamClient) { + s.processSubscription(c) + log.Infof("polling subscription: first complete response: %q", c.sr) + for { + if c.queue.IsClosed() { + log.Info("Terminating polling subscription due to closed client queue.") + c.errC <- nil + return + } + // Subsequent receives are only triggers to poll again. The contents of the + // request are completely ignored. + _, err := c.stream.Recv() + if err == io.EOF { + log.Info("Terminating polling subscription due to EOF.") + c.errC <- nil + return + } + if err != nil { + log.Error(err) + c.errC <- err + return + } + log.Infof("polling subscription: repoll: %q", c.sr) + s.processSubscription(c) + log.Infof("polling subscription: repoll complete: %q", c.sr) + } +} + +// sendStreamingResults forwards all streaming updates to a given streaming +// Subscription RPC client. +func (s *Server) sendStreamingResults(c *streamClient) { + ctx := c.stream.Context() + peer, _ := peer.FromContext(ctx) + t := time.NewTimer(s.timeout) + // Make sure the timer doesn't expire waiting for a value to send, only + // waiting to send. + t.Stop() + done := make(chan struct{}) + defer close(done) + // If a send doesn't occur within the timeout, close the stream. + go func() { + select { + case <-t.C: + err := errors.New("subscription timed out while sending") + c.errC <- err + log.Errorf("%v : %v", peer, err) + case <-done: + } + }() + for { + item, dup, err := c.queue.Next(ctx) + if coalesce.IsClosedQueue(err) { + c.errC <- nil + return + } + if err != nil { + c.errC <- err + return + } + + // s.processSubscription will send a sync marker, handle it separately. + if _, ok := item.(syncMarker); ok { + if err = c.stream.Send(subscribeSync); err != nil { + c.errC <- err + return + } + continue + } + + n, ok := item.(*ctree.Leaf) + if !ok || n == nil { + c.errC <- status.Errorf(codes.Internal, "invalid cache node: %#v", item) + return + } + + if err = s.sendSubscribeResponse(&resp{ + stream: c.stream, + n: n, + dup: dup, + t: t, + }, c); err != nil { + c.errC <- err + return + } + // If the only target being subscribed was deleted, stop streaming. + if isTargetDelete(n) && c.target != "*" { + log.Infof("Target %q was deleted. Closing stream.", c.target) + c.errC <- nil + return + } + } +} + +// MakeSubscribeResponse produces a gnmi_proto.SubscribeResponse from a +// gnmi_proto.Notification. +// +// This function modifies the message to set the duplicate count if it is +// greater than 0. The function clones the gnmi notification if the duplicate count needs to be set. +// You have to be working on a cloned message if you need to modify the message in any way. +func MakeSubscribeResponse(n interface{}, dup uint32) (*pb.SubscribeResponse, error) { + var notification *pb.Notification + var ok bool + notification, ok = n.(*pb.Notification) + if !ok { + return nil, status.Errorf(codes.Internal, "invalid notification type: %#v", n) + } + + // There may be multiple updates in a notification. Since duplicate count is just + // an indicator that coalescion is happening, not a critical data, just the first + // update is set with duplicate count to be on the side of efficiency. + // Only attempt to set the duplicate count if it is greater than 0. The default + // value in the message is already 0. + if dup > 0 && len(notification.Update) > 0 { + // We need a copy of the cached notification before writing a client specific + // duplicate count as the notification is shared across all clients. + notification = proto.Clone(notification).(*pb.Notification) + notification.Update[0].Duplicates = dup + } + response := &pb.SubscribeResponse{ + Response: &pb.SubscribeResponse_Update{ + Update: notification, + }, + } + + return response, nil +} + +func isTargetDelete(l *ctree.Leaf) bool { + switch v := l.Value().(type) { + case *pb.Notification: + if len(v.Delete) == 1 { + var orig string + if v.Prefix != nil { + orig = v.Prefix.Origin + } + // Prefix path is indexed without target and origin + p := path.ToStrings(v.Prefix, false) + p = append(p, path.ToStrings(v.Delete[0], false)...) + // When origin isn't set, intention must be to delete entire target. + return orig == "" && len(p) == 1 && p[0] == "*" + } + } + return false +} diff --git a/deps/github.com/openconfig/gnmi/subscribe/subscribe_test.go b/deps/github.com/openconfig/gnmi/subscribe/subscribe_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3fbb79b07d3995442fc9930e2c647d47830a5ba5 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/subscribe/subscribe_test.go @@ -0,0 +1,1365 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package subscribe + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "strings" + "sync" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/cache" + "github.com/openconfig/gnmi/client" + gnmiclient "github.com/openconfig/gnmi/client/gnmi" + "github.com/openconfig/gnmi/ctree" + "github.com/openconfig/gnmi/path" + "github.com/openconfig/gnmi/testing/fake/testing/grpc/config" + "github.com/openconfig/gnmi/value" + + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +func startServer(targets []string) (string, *cache.Cache, func(), error) { + c := cache.New(targets) + p, err := NewServer(c) + if err != nil { + return "", nil, nil, fmt.Errorf("NewServer: %v", err) + } + c.SetClient(p.Update) + lis, err := net.Listen("tcp", "") + if err != nil { + return "", nil, nil, fmt.Errorf("net.Listen: %v", err) + } + opt, err := config.WithSelfTLSCert() + if err != nil { + return "", nil, nil, fmt.Errorf("config.WithSelfCert: %v", err) + } + srv := grpc.NewServer(opt) + pb.RegisterGNMIServer(srv, p) + go srv.Serve(lis) + return lis.Addr().String(), p.c, func() { + lis.Close() + }, nil +} + +func TestOriginInSubscribeRequest(t *testing.T) { + addr, cache, teardown, err := startServer(client.Path{"dev1"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b", "c", "d"}, + {"dev1", "a", "b", "d", "e"}, + {"dev1", "a", "c", "d", "e"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths, ×tamp) + + tests := []struct { + desc string + inPrefix *pb.Path + inPath *pb.Path + wantCount int + wantErr bool + }{ + { + desc: "no origin set", + inPrefix: &pb.Path{Target: "dev1", Elem: []*pb.PathElem{{Name: "a"}}}, + inPath: &pb.Path{Elem: []*pb.PathElem{}}, + wantCount: 3, + }, + { + desc: "origin set in prefix", + inPrefix: &pb.Path{Target: "dev1", Origin: "a", Elem: []*pb.PathElem{{Name: "b"}}}, + inPath: &pb.Path{Elem: []*pb.PathElem{}}, + wantCount: 2, + }, + { + desc: "origin set in path with path elements in prefix", + inPrefix: &pb.Path{Target: "dev1", Elem: []*pb.PathElem{{Name: "a"}}}, + inPath: &pb.Path{Origin: "b"}, + wantErr: true, + }, + { + desc: "origin set in path", + inPrefix: &pb.Path{Target: "dev1"}, + inPath: &pb.Path{Origin: "a", Elem: []*pb.PathElem{{Name: "b"}}}, + wantCount: 2, + }, + { + desc: "origin set in path and prefix", + inPrefix: &pb.Path{Target: "dev1", Origin: "a", Elem: []*pb.PathElem{{Name: "b"}}}, + inPath: &pb.Path{Origin: "c"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("%s: prefix %v and path %v", tt.desc, tt.inPrefix, tt.inPath), func(t *testing.T) { + sync := 0 + count := 0 + q, err := client.NewQuery(&pb.SubscribeRequest{ + Request: &pb.SubscribeRequest_Subscribe{ + Subscribe: &pb.SubscriptionList{ + Prefix: tt.inPrefix, + Subscription: []*pb.Subscription{{Path: tt.inPath}}, + Mode: pb.SubscriptionList_ONCE, + }, + }, + }) + if err != nil { + t.Fatalf("failed to initialize a client.Query: %v", err) + } + q.ProtoHandler = func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch v := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + case *pb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", v) + case *pb.SubscribeResponse_SyncResponse: + sync++ + default: + return fmt.Errorf("unknown response %T: %s", v, v) + } + + return nil + } + q.TLS = &tls.Config{InsecureSkipVerify: true} + q.Addrs = []string{addr} + + c := client.BaseClient{} + err = c.Subscribe(context.Background(), q, gnmiclient.Type) + defer c.Close() + if tt.wantErr { + if err == nil { + t.Fatal("got nil, want err") + } + return + } + if err != nil { + t.Fatalf("got %v, want no error", err) + } + if sync != 1 { + t.Errorf("got %d sync messages, want 1", sync) + } + if count != tt.wantCount { + t.Errorf("got %d updates, want %d", count, tt.wantCount) + } + }) + } +} + +func TestGNMIOnce(t *testing.T) { + addr, cache, teardown, err := startServer(client.Path{"dev1"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + {"dev1", "a", "c"}, + {"dev1", "e", "f"}, + {"dev1", "f", "b"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths, ×tamp) + + testCases := []struct { + dev string + query client.Path + count int + err bool + }{ + // These cases will be found. + {"dev1", client.Path{"a", "b"}, 1, false}, + {"dev1", client.Path{"a"}, 2, false}, + {"dev1", client.Path{"e"}, 1, false}, + {"dev1", client.Path{"*", "b"}, 2, false}, + // This case is not found. + {"dev1", client.Path{"b"}, 0, false}, + // This target doesn't even exist, and will return an error. + {"dev2", client.Path{"a"}, 0, true}, + } + for _, tt := range testCases { + t.Run(fmt.Sprintf("target: %q query: %q", tt.dev, tt.query), func(t *testing.T) { + sync := 0 + count := 0 + q := client.Query{ + Addrs: []string{addr}, + Target: tt.dev, + Queries: []client.Path{tt.query}, + Type: client.Once, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch v := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + case *pb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", v) + case *pb.SubscribeResponse_SyncResponse: + sync++ + default: + return fmt.Errorf("unknown response %T: %s", v, v) + } + + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + c := client.BaseClient{} + err := c.Subscribe(context.Background(), q, gnmiclient.Type) + defer c.Close() + if err != nil && !tt.err { + t.Fatalf("unexpected error: %v", err) + } + if err == nil && tt.err { + t.Fatal("didn't get expected error") + } + if tt.err { + return + } + if sync != 1 { + t.Errorf("got %d sync messages, want 1", sync) + } + if count != tt.count { + t.Errorf("got %d updates, want %d", count, tt.count) + } + }) + } +} + +// sendUpdates generates an update for each supplied path incrementing the +// timestamp and value for each. +func sendUpdates(t *testing.T, c *cache.Cache, paths []client.Path, timestamp *time.Time) { + t.Helper() + for _, path := range paths { + *timestamp = timestamp.Add(time.Nanosecond) + sv, err := value.FromScalar(timestamp.UnixNano()) + if err != nil { + t.Errorf("Scalar value err %v", err) + continue + } + noti := &pb.Notification{ + Prefix: &pb.Path{Target: path[0]}, + Timestamp: timestamp.UnixNano(), + Update: []*pb.Update{ + { + Path: &pb.Path{Element: path[1:]}, + Val: sv, + }, + }, + } + if err := c.GnmiUpdate(noti); err != nil { + t.Errorf("streamUpdate: %v", err) + } + } +} + +func TestGNMIPoll(t *testing.T) { + addr, cache, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + {"dev1", "a", "c"}, + {"dev1", "e", "f"}, + {"dev2", "a", "b"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths, ×tamp) + + // The streaming Updates change only the timestamp, so the value is used as + // a key. + m := map[string]time.Time{} + sync := 0 + count := 0 + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Poll, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + ts := time.Unix(0, r.Update.GetTimestamp()) + v := strings.Join(path.ToStrings(r.Update.Update[0].Path, false), "/") + if ts.Before(m[v]) { + t.Fatalf("#%d: got timestamp %s, want >= %s for value %q", count, ts, m[v], v) + } + m[v] = ts + case *pb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", r) + case *pb.SubscribeResponse_SyncResponse: + if count != 2 { + t.Fatalf("did not receive initial updates before sync, got %d, want 2", count) + } + count = 0 + sync++ + if sync == 3 { + c.Close() + } else { + sendUpdates(t, cache, paths, ×tamp) + c.Poll() + } + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + err = c.Subscribe(context.Background(), q, gnmiclient.Type) + if err != nil { + t.Error(err) + } +} + +func TestGNMIStream(t *testing.T) { + addr, cache, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + {"dev1", "a", "c"}, + {"dev1", "e", "f"}, + {"dev2", "a", "b"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths, ×tamp) + + m := map[string]time.Time{} + sync := false + count := 0 + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + // The total updates received should be 4, 2 before sync, 2 after. + if count == 4 { + c.Close() + } + v := strings.Join(path.ToStrings(r.Update.Update[0].Path, false), "/") + ts := time.Unix(0, r.Update.GetTimestamp()) + if ts.Before(m[v]) { + t.Fatalf("#%d: got timestamp %s, want >= %s for value %q", count, ts, m[v], v) + } + m[v] = ts + case *pb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", r) + case *pb.SubscribeResponse_SyncResponse: + if sync { + t.Fatal("received more than one sync message") + } + if count < 2 { + t.Fatalf("did not receive initial updates before sync, got %d, want > 2", count) + } + sync = true + // Send some updates after the sync occurred. + sendUpdates(t, cache, paths, ×tamp) + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + err = c.Subscribe(context.Background(), q, gnmiclient.Type) + if err != nil { + t.Error(err) + } + if !sync { + t.Error("streaming query did not send sync message") + } +} + +func atomicNotification(prefix client.Path, paths []client.Path, values []int64, timestamp time.Time) *pb.Notification { + n := len(paths) + if n != len(values) { + return nil + } + us := make([]*pb.Update, 0, n) + for i, path := range paths { + sv, err := value.FromScalar(values[i]) + if err != nil { + return nil + } + us = append(us, &pb.Update{ + Path: &pb.Path{Element: path}, + Val: sv, + }) + } + return &pb.Notification{ + Prefix: &pb.Path{Target: prefix[0], Element: prefix[1:]}, + Timestamp: timestamp.UnixNano(), + Update: us, + Atomic: true, + } +} + +// sendNotification sends a notification. +func sendNotification(t *testing.T, c *cache.Cache, n *pb.Notification) { + t.Helper() + if err := c.GnmiUpdate(n); err != nil { + t.Errorf("sendNotification: %v", err) + } +} + +func TestGNMIStreamAtomic(t *testing.T) { + addr, cache, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + prefix := []string{"dev1", "a"} + paths := []client.Path{{"b"}, {"c"}, {"d"}} + n1 := atomicNotification(prefix, paths, []int64{1, 2, 3}, time.Unix(12345678900, 0)) + n2 := atomicNotification(prefix, paths, []int64{4, 5, 6}, time.Unix(12345678930, 0)) + n3 := atomicNotification(prefix, paths, []int64{7, 8, 9}, time.Unix(12345678960, 0)) + sendNotification(t, cache, n1) + + ns := []*pb.Notification{} + sync := false + count := 0 + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + ns = append(ns, resp.GetUpdate()) + // The total updates received should be 3, 1 before sync, 2 after. + if count == 3 { + c.Close() + } + case *pb.SubscribeResponse_Error: + return fmt.Errorf("error in response: %s", r) + case *pb.SubscribeResponse_SyncResponse: + if sync { + t.Fatal("received more than one sync message") + } + if count != 1 { + t.Fatalf("initial updates before sync, got %d, want 1", count) + } + sync = true + // Send some updates after the sync occurred. + sendNotification(t, cache, n2) + time.AfterFunc(time.Second, func() { + sendNotification(t, cache, n3) + }) + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + err = c.Subscribe(context.Background(), q, gnmiclient.Type) + if err != nil { + t.Fatal(err) + } + if !sync { + t.Fatal("streaming query did not send sync message") + } + if len(ns) != 3 { + t.Fatalf("number of notifications received: got %d, want 3", len(ns)) + } + for i, n := range []*pb.Notification{n1, n2, n3} { + if diff := cmp.Diff(n, ns[i], cmp.Comparer(proto.Equal)); diff != "" { + t.Errorf("diff in received notification %d:\n%s", i+1, diff) + } + } +} + +func TestGNMIStreamNewUpdates(t *testing.T) { + addr, cache, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "e", "f"}, + {"dev2", "a", "b"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths, ×tamp) + + newpaths := []client.Path{ + {"dev1", "b", "d"}, + {"dev2", "a", "x"}, + {"dev1", "a", "x"}, // The update we want to see. + } + + sync := false + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + v, want := strings.Join(path.ToStrings(r.Update.Update[0].Path, false), "/"), "a/x" + if v != want { + t.Fatalf("got update %q, want only %q", v, want) + } + c.Close() + case *pb.SubscribeResponse_SyncResponse: + if sync { + t.Fatal("received more than one sync message") + } + sync = true + // Stream new updates only after sync which should have had 0 + // updates. + sendUpdates(t, cache, newpaths, ×tamp) + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + err = c.Subscribe(context.Background(), q, gnmiclient.Type) + if err != nil { + t.Error(err) + } + if !sync { + t.Error("streaming query did not send sync message") + } +} + +func TestGNMIUpdatesOnly(t *testing.T) { + addr, cache, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths, ×tamp) + + sync := false + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + if !sync { + t.Errorf("got update %v before sync", r) + } + c.Close() + case *pb.SubscribeResponse_SyncResponse: + sync = true + sendUpdates(t, cache, paths, ×tamp) + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + UpdatesOnly: true, + } + err = c.Subscribe(context.Background(), q, gnmiclient.Type) + if err != nil { + t.Error(err) + } +} + +// If a client doesn't read any of the responses, it should not affect other +// clients querying the same target. +func TestGNMISubscribeUnresponsiveClient(t *testing.T) { + addr, cache, teardown, err := startServer([]string{"dev1"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + {"dev1", "a", "c"}, + {"dev1", "e", "f"}, + } + sendUpdates(t, cache, paths, &time.Time{}) + + // Start the first client and do *not* read any responses. + started := make(chan struct{}) + stall := make(chan struct{}) + defer close(stall) + client1 := client.BaseClient{} + defer client1.Close() + q1 := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + select { + case <-started: + default: + close(started) + } + <-stall + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + ctx := context.Background() + go client1.Subscribe(ctx, q1, gnmiclient.Type) + // Wait for client1 to start. + <-started + + // Start the second client for the same target and actually accept + // responses. + count := 0 + client2 := client.BaseClient{} + q2 := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + case *pb.SubscribeResponse_SyncResponse: + client2.Close() + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + if err := client2.Subscribe(ctx, q2, gnmiclient.Type); err != nil { + t.Errorf("client2.Subscribe: %v", err) + } + if count != 2 { + t.Errorf("client2.Subscribe got %d updates, want 2", count) + } +} + +func TestGNMIDeletedTargetMessage(t *testing.T) { + addr, ch, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + {"dev1", "a", "c"}, + {"dev1", "e", "f"}, + {"dev2", "a", "b"}, + } + sendUpdates(t, ch, paths, &time.Time{}) + + deleted := false + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + if len(r.Update.Delete) > 0 { + // Want to see a target delete message. No need to call c.Close() + // because the server should close the connection if the target is + // removed. + if r.Update.Prefix.GetTarget() == "dev1" { + deleted = true + } + } + case *pb.SubscribeResponse_SyncResponse: + ch.Remove("dev1") + default: + return fmt.Errorf("unknown response %T: %s", r, r) + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + if err := c.Subscribe(context.Background(), q, gnmiclient.Type); err != nil { + t.Errorf("c.Subscribe: %v", err) + } + if !deleted { + t.Error("Target delete not sent.") + } +} + +func TestGNMICoalescedDupCount(t *testing.T) { + // Inject a simulated flow control to block sends and induce coalescing. + flowControlTest = func() { time.Sleep(10 * time.Millisecond) } + addr, cache, teardown, err := startServer([]string{"dev1"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + stall := make(chan struct{}) + done := make(chan struct{}) + coalesced := uint32(0) + count := 0 + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + count++ + if r.Update.Update[0].GetDuplicates() > 0 { + coalesced = r.Update.Update[0].GetDuplicates() + } + switch count { + case 1: + close(stall) + case 2: + close(done) + } + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go c.Subscribe(ctx, q, gnmiclient.Type) + + paths := []client.Path{ + {"dev1", "a"}, + {"dev1", "a"}, + {"dev1", "a"}, + {"dev1", "a"}, + {"dev1", "a"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths[0:1], ×tamp) + <-stall + sendUpdates(t, cache, paths, ×tamp) + <-done + + if want := uint32(len(paths) - 1); coalesced != want { + t.Errorf("got coalesced count %d, want %d", coalesced, want) + } +} + +func TestGNMISubscribeTimeout(t *testing.T) { + // Set a low timeout that is below the induced flowControl delay. + Timeout = 100 * time.Millisecond + // Cause query to hang indefinitely to induce timeout. + flowControlTest = func() { select {} } + // Reset the global variables so as not to interfere with other tests. + defer func() { + Timeout = time.Minute + flowControlTest = func() {} + }() + + addr, cache, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + paths := []client.Path{ + {"dev1", "a", "b"}, + {"dev1", "a", "c"}, + {"dev1", "e", "f"}, + {"dev2", "a", "b"}, + } + sendUpdates(t, cache, paths, &time.Time{}) + + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + if err := c.Subscribe(context.Background(), q, gnmiclient.Type); err == nil { + t.Error("c.Subscribe got nil, wanted a timeout err") + } +} + +func TestSubscriptionLimit(t *testing.T) { + totalQueries := 20 + SubscriptionLimit = 7 + causeLimit := make(chan struct{}) + subscriptionLimitTest = func() { + <-causeLimit + } + // Clear the global variables so as not to interfere with other tests. + defer func() { + SubscriptionLimit = 0 + subscriptionLimitTest = func() {} + }() + + addr, _, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + fc := make(chan struct{}) + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + NotificationHandler: func(n client.Notification) error { + switch n.(type) { + case client.Sync: + fc <- struct{}{} + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + + // Launch parallel queries. + for i := 0; i < totalQueries; i++ { + c := client.BaseClient{} + go c.Subscribe(context.Background(), q, gnmiclient.Type) + } + + timeout := time.After(500 * time.Millisecond) + finished := 0 +firstQueries: + for { + select { + case <-fc: + finished++ + case <-timeout: + break firstQueries + } + } + if finished != SubscriptionLimit { + t.Fatalf("got %d finished queries, want %d", finished, SubscriptionLimit) + } + + close(causeLimit) + timeout = time.After(time.Second) +remainingQueries: + for { + select { + case <-fc: + if finished++; finished == totalQueries { + break remainingQueries + } + case <-timeout: + t.Errorf("Remaining queries did not proceed after limit removed. got %d, want %d", finished, totalQueries) + } + } +} + +func TestGNMISubscriptionLimit(t *testing.T) { + totalQueries := 20 + SubscriptionLimit = 7 + causeLimit := make(chan struct{}) + subscriptionLimitTest = func() { + <-causeLimit + } + // Clear the global variables so as not to interfere with other tests. + defer func() { + SubscriptionLimit = 0 + subscriptionLimitTest = func() {} + }() + + addr, _, teardown, err := startServer([]string{"dev1", "dev2"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + + fc := make(chan struct{}) + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Once, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch resp.Response.(type) { + case *pb.SubscribeResponse_SyncResponse: + fc <- struct{}{} + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + + // Launch parallel queries. + for i := 0; i < totalQueries; i++ { + c := client.BaseClient{} + go c.Subscribe(context.Background(), q, gnmiclient.Type) + } + + timeout := time.After(500 * time.Millisecond) + finished := 0 +firstQueries: + for { + select { + case <-fc: + finished++ + case <-timeout: + break firstQueries + } + } + if finished != SubscriptionLimit { + t.Fatalf("got %d finished queries, want %d", finished, SubscriptionLimit) + } + + close(causeLimit) + timeout = time.After(time.Second) +remainingQueries: + for { + select { + case <-fc: + if finished++; finished == totalQueries { + break remainingQueries + } + case <-timeout: + t.Errorf("Remaining queries did not proceed after limit removed. got %d, want %d", finished, totalQueries) + } + } +} + +func TestGNMIMultipleSubscriberCoalescion(t *testing.T) { + // Inject a simulated flow control to block sends and induce coalescing. + flowControlTest = func() { time.Sleep(time.Second) } + addr, cache, teardown, err := startServer([]string{"dev1"}) + if err != nil { + t.Fatal(err) + } + defer teardown() + var wg sync.WaitGroup + sc := 5 + wg.Add(sc) + cr := make([]uint32, 0, sc) + var mux sync.Mutex + for i := 0; i < sc; i++ { + c := client.BaseClient{} + q := client.Query{ + Addrs: []string{addr}, + Target: "dev1", + Queries: []client.Path{{"a"}}, + Type: client.Stream, + ProtoHandler: func(msg proto.Message) error { + resp, ok := msg.(*pb.SubscribeResponse) + if !ok { + return fmt.Errorf("failed to type assert message %#v", msg) + } + switch r := resp.Response.(type) { + case *pb.SubscribeResponse_Update: + mux.Lock() + if r.Update.Update[0].GetDuplicates() > 0 { + cr = append(cr, r.Update.Update[0].GetDuplicates()) + } + mux.Unlock() + wg.Done() + } + return nil + }, + TLS: &tls.Config{InsecureSkipVerify: true}, + } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go c.Subscribe(ctx, q, gnmiclient.Type) + } + + paths := []client.Path{ + {"dev1", "a"}, + {"dev1", "a"}, + } + var timestamp time.Time + sendUpdates(t, cache, paths[0:1], ×tamp) + wg.Wait() + wg.Add(sc) + sendUpdates(t, cache, paths, ×tamp) + wg.Wait() + for i, d := range cr { + if d != uint32(len(paths)-1) { + t.Errorf("#%d got %d, expect %d duplicate count", i, d, uint32(len(paths)-1)) + } + } +} + +type fakeRPCACL struct { + user string + acl ACL +} + +func (r *fakeRPCACL) Check(dev string) bool { + return r.acl.Check(r.user, dev) +} + +type fakeACL struct { + allow int + deny int + check int +} +type fakeNet struct{} + +func (n *fakeNet) Network() string { + return "fake network" +} +func (n *fakeNet) String() string { + return "127.0.0.1" +} + +type userKey int + +var uk userKey + +func (a *fakeACL) NewRPCACL(ctx context.Context) (RPCACL, error) { + u, ok := ctx.Value(uk).(string) + if !ok { + return nil, errors.New("no user field in ctx") + } + r := &fakeRPCACL{user: u, acl: a} + return r, nil +} + +func (a *fakeACL) Check(user string, dev string) bool { + a.check++ + m := map[string]map[string]bool{ + "dev-pii": {"user1": true, "user2": false}, + "dev-no-pii": {"user1": true, "user2": true}, + } + if v, ok := m[dev]; ok { + if v2, ok := v[user]; ok { + if v2 { + a.allow++ + } else { + a.deny++ + } + return v2 + } + } + a.deny++ + return false +} + +type fakeSubServer struct { + user string + ctx context.Context + req chan *pb.SubscribeRequest + rsp chan *pb.SubscribeResponse + grpc.ServerStream +} + +func (s *fakeSubServer) Send(rsp *pb.SubscribeResponse) error { + select { + case <-s.ctx.Done(): + return io.ErrClosedPipe + default: + } + s.rsp <- rsp + return nil +} + +func (s *fakeSubServer) Recv() (*pb.SubscribeRequest, error) { + select { + case <-s.ctx.Done(): + return <-s.req, io.EOF + default: + } + return <-s.req, nil +} + +func (s *fakeSubServer) Context() context.Context { + return s.ctx +} + +func TestGNMIACL(t *testing.T) { + targets := []string{"dev-pii", "dev-no-pii"} + c := cache.New(targets) + p, err := NewServer(c) + if err != nil { + t.Errorf("NewServer: %v", err) + return + } + paths := []client.Path{ + {"dev-pii", "a", "b"}, + {"dev-pii", "a", "c"}, + {"dev-no-pii", "e", "f"}, + {"dev-no-pii", "a", "b"}, + } + var timestamp time.Time + sendUpdates(t, c, paths, ×tamp) + tests := []struct { + name string + user string + dev string + mode pb.SubscriptionList_Mode + wantErr string + wantCnt *fakeACL + }{ + { + name: "user1 once with pii allow", + user: "user1", + dev: "dev-pii", + mode: pb.SubscriptionList_ONCE, + wantErr: "<nil>", + wantCnt: &fakeACL{allow: 3, deny: 0, check: 3}, + }, + { + name: "user2 once with pii deny", + user: "user2", + dev: "dev-pii", + mode: pb.SubscriptionList_ONCE, + wantErr: "rpc error: code = PermissionDenied desc = not authorized for target \"dev-pii\"", + wantCnt: &fakeACL{allow: 0, deny: 1, check: 1}, + }, + { + name: "user1 once all devices with pii allow", + user: "user1", + dev: "*", + mode: pb.SubscriptionList_ONCE, + wantErr: "<nil>", + wantCnt: &fakeACL{allow: 3, deny: 0, check: 3}, + }, + { + name: "user2 once all devices with pii deny", + user: "user2", + dev: "*", + mode: pb.SubscriptionList_ONCE, + wantErr: "<nil>", + wantCnt: &fakeACL{allow: 1, deny: 2, check: 3}, + }, + { + name: "user2 poll with pii deny", + user: "user2", + dev: "dev-pii", + mode: pb.SubscriptionList_POLL, + wantErr: "rpc error: code = PermissionDenied desc = not authorized for target \"dev-pii\"", + wantCnt: &fakeACL{allow: 0, deny: 1, check: 1}, + }, + { + name: "user2 stream with pii deny", + user: "user2", + dev: "dev-pii", + mode: pb.SubscriptionList_STREAM, + wantErr: "rpc error: code = PermissionDenied desc = not authorized for target \"dev-pii\"", + wantCnt: &fakeACL{allow: 0, deny: 1, check: 1}, + }, + } + + for _, test := range tests { + facl := &fakeACL{} + p.SetACL(facl) + var cancel context.CancelFunc + subSvr := &fakeSubServer{user: test.user, + req: make(chan *pb.SubscribeRequest, 2), + rsp: make(chan *pb.SubscribeResponse, len(paths)+1)} + ctx := peer.NewContext(context.Background(), &peer.Peer{Addr: &fakeNet{}}) + subSvr.ctx = context.WithValue(ctx, uk, test.user) + subSvr.ctx, cancel = context.WithCancel(subSvr.ctx) + subSvr.req <- &pb.SubscribeRequest{ + Request: &pb.SubscribeRequest_Subscribe{ + Subscribe: &pb.SubscriptionList{ + Prefix: &pb.Path{ + Target: test.dev, + }, + Subscription: []*pb.Subscription{ + &pb.Subscription{ + Path: &pb.Path{ + Element: []string{"a"}, + Elem: []*pb.PathElem{ + &pb.PathElem{ + Name: "a", + }, + }, + }, + }, + }, + Mode: test.mode, + }, + }, + } + + errCh := make(chan error) + go func() { + errCh <- p.Subscribe(subSvr) + }() + + select { + case err := <-errCh: + got := fmt.Sprint(err) + if diff := cmp.Diff(got, test.wantErr, cmpopts.EquateEmpty()); diff != "" { + t.Errorf("%v returned unexpected result:\n got %v\n want %v\n diff %v", test.name, got, test.wantErr, diff) + } + case <-time.After(5 * time.Second): + } + cancel() + + if diff := cmp.Diff(facl, test.wantCnt, cmpopts.EquateEmpty(), cmp.AllowUnexported(fakeACL{})); diff != "" { + t.Errorf("%v returned unexpected result:\n got %v\n want %v\n diff %v", test.name, facl, test.wantCnt, diff) + } + } +} + +func TestIsTargetDelete(t *testing.T) { + testCases := []struct { + name string + noti *pb.Notification + want bool + }{ + { + name: "GNMI Update", + noti: &pb.Notification{ + Prefix: &pb.Path{Target: "d", Origin: "o"}, + Update: []*pb.Update{ + { + Path: &pb.Path{ + Elem: []*pb.PathElem{{Name: "p"}}, + }, + }, + }, + }, + want: false, + }, + { + name: "GNMI Leaf Delete", + noti: &pb.Notification{ + Prefix: &pb.Path{Target: "d", Origin: "o"}, + Delete: []*pb.Path{{ + Elem: []*pb.PathElem{{Name: "p"}}, + }}, + }, + want: false, + }, + { + name: "GNMI Root Delete", + noti: &pb.Notification{ + Prefix: &pb.Path{Target: "d", Origin: "o"}, + Delete: []*pb.Path{{ + Elem: []*pb.PathElem{{Name: "*"}}, + }}, + }, + want: false, + }, + { + name: "GNMI Target Delete", + noti: &pb.Notification{ + Prefix: &pb.Path{Target: "d"}, + Delete: []*pb.Path{{ + Elem: []*pb.PathElem{{Name: "*"}}, + }}, + }, + want: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + l := ctree.DetachedLeaf(tc.noti) + if got := isTargetDelete(l); got != tc.want { + t.Errorf("got %t, want %t", got, tc.want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/target/target.go b/deps/github.com/openconfig/gnmi/target/target.go new file mode 100644 index 0000000000000000000000000000000000000000..be852cba7d307306f1992cedd2b5b42649b10d1c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/target/target.go @@ -0,0 +1,191 @@ +/* +Copyright 2018 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package target has utility functions for working with target configuration +// proto messages in target.proto. +package target + +import ( + "errors" + "fmt" + "sync" + + "github.com/golang/protobuf/proto" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + pb "github.com/openconfig/gnmi/proto/target" +) + +// Update describes a single target configuration. +type Update struct { + Name string + Request *gpb.SubscribeRequest + Target *pb.Target +} + +// Handler defines callbacks to be synchronously invoked in response to +// configuration changes. +type Handler struct { + // Add handles addition of a new target. + Add func(Update) + // Update handles target modification, including subscription request changes. + Update func(Update) + // Delete handles a target being removed. + Delete func(name string) +} + +// Config handles configuration file changes and contains configuration state. +type Config struct { + h Handler + mu sync.Mutex + configuration *pb.Configuration +} + +// NewConfig creates a new Config that can process configuration changes. +func NewConfig(h Handler) *Config { + return &Config{ + h: h, + } +} + +// NewConfigWithBase creates a new Config that can process configuration +// changes. An optional configuration is used as the initial state. +func NewConfigWithBase(h Handler, config *pb.Configuration) (*Config, error) { + if config != nil { + if err := Validate(config); err != nil { + return nil, fmt.Errorf("invalid configuration: %v", err) + } + } + return &Config{ + configuration: config, + h: h, + }, nil +} + +// Current returns a copy of the current configuration. +func (c *Config) Current() *pb.Configuration { + c.mu.Lock() + defer c.mu.Unlock() + return proto.Clone(c.configuration).(*pb.Configuration) +} + +// Load updates the current configuration and invokes Handler callbacks for +// detected changes. An error is returned when loading fails, or the new revision +// is not strictly increasing. +func (c *Config) Load(config *pb.Configuration) error { + if config == nil { + return fmt.Errorf("attempted to load nil configuration") + } + if err := Validate(config); err != nil { + return fmt.Errorf("invalid configuration: %v", err) + } + + c.mu.Lock() + defer c.mu.Unlock() + if err := c.checkRevision(config); err != nil { + return err + } + // Diff before setting new state. + c.handleDiffs(config) + c.configuration = config + + return nil +} + +func (c *Config) checkRevision(cf *pb.Configuration) error { + switch { + case c.configuration == nil: + return nil + case cf.Revision <= c.configuration.GetRevision(): + return fmt.Errorf("revision %v is not strictly greater than %v", cf.Revision, c.configuration.GetRevision()) + } + return nil +} + +// handleDiffs should be called while locking c. It performs a read-only diff on +// state in c against the new configuration. +func (c *Config) handleDiffs(config *pb.Configuration) { + requestChanged := map[string]bool{} + for k, new := range config.Request { + if old, ok := c.configuration.GetRequest()[k]; ok { + if !proto.Equal(old, new) { + requestChanged[k] = true + } + } + } + + // Make a copy of new targets so we can safely modify the map. + newTargets := make(map[string]*pb.Target) + for k, t := range config.GetTarget() { + newTargets[k] = t + } + + for k, t := range c.configuration.GetTarget() { + nt := newTargets[k] + switch { + case nt == nil: + if c.h.Delete != nil { + c.h.Delete(k) + } + case !requestChanged[t.GetRequest()] && proto.Equal(t, nt): + delete(newTargets, k) + default: + if c.h.Update != nil { + r := config.GetRequest()[nt.GetRequest()] + c.h.Update(Update{ + Name: k, + Request: r, + Target: nt, + }) + } + delete(newTargets, k) + } + } + + // Anything left in newTargets must be a new target. + for k, t := range newTargets { + r := config.GetRequest()[t.GetRequest()] + if c.h.Add != nil { + c.h.Add(Update{ + Name: k, + Request: r, + Target: t, + }) + } + } +} + +// Validate confirms that the configuration is valid. +func Validate(config *pb.Configuration) error { + for name, target := range config.Target { + if name == "" { + return errors.New("target with empty name") + } + if target == nil { + return fmt.Errorf("missing target configuration for %q", name) + } + if len(target.Addresses) == 0 { + return fmt.Errorf("target %q missing address", name) + } + if target.Request == "" { + return fmt.Errorf("target %q missing request", name) + } + if _, ok := config.Request[target.Request]; !ok { + return fmt.Errorf("missing request %q for target %q", target.Request, name) + } + } + return nil +} diff --git a/deps/github.com/openconfig/gnmi/target/target_test.go b/deps/github.com/openconfig/gnmi/target/target_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9a9eae03889ef5847901f4b75a9882127810aaf3 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/target/target_test.go @@ -0,0 +1,497 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package target + +import ( + "reflect" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/testing/protocmp" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + pb "github.com/openconfig/gnmi/proto/target" +) + +type record struct { + adds []Update + updates []Update + deletes []string +} + +func (r *record) assertLast(t *testing.T, desc string, wantAdd, wantUpdate []Update, wantDelete []string) { + sort.Slice(r.adds, func(i int, j int) bool { return r.adds[i].Name < r.adds[j].Name }) + sort.Slice(r.updates, func(i int, j int) bool { return r.updates[i].Name < r.updates[j].Name }) + sort.Strings(r.deletes) + switch { + case !reflect.DeepEqual(r.adds, wantAdd): + t.Errorf("%v: Mismatched adds: got %v, want %+v", desc, r.adds, wantAdd) + case !reflect.DeepEqual(r.updates, wantUpdate): + t.Errorf("%v: Mismatched updates: got %v, want %+v", desc, r.updates, wantUpdate) + case !reflect.DeepEqual(r.deletes, wantDelete): + t.Errorf("%v: Mismatched deletes: got %v, want %v", desc, r.deletes, wantDelete) + } + r.adds = nil + r.updates = nil + r.deletes = nil +} + +func TestLoad(t *testing.T) { + for _, tt := range []struct { + desc string + initial *pb.Configuration + toLoad *pb.Configuration + wantAdd []string + wantUpdate []string + wantDelete []string + err bool + }{ + { + desc: "nil Config", + toLoad: nil, + err: true, + }, { + desc: "bad config", + toLoad: &pb.Configuration{ + Target: map[string]*pb.Target{ + "dev1": {}, + }, + Revision: 1, + }, + err: true, + }, { + desc: "old Config", + initial: &pb.Configuration{}, + toLoad: &pb.Configuration{ + Revision: -1, + }, + err: true, + }, { + desc: "add targets", + toLoad: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{}, + }, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev2": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + }, + Revision: 1, + }, + wantAdd: []string{"dev1", "dev2"}, + }, { + desc: "modify targets", + initial: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev3": { + Request: "sub1", + Addresses: []string{"33.333.333.33:33333"}, + }, + }, + Revision: 0, + }, + toLoad: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111", "12.111.111.11:11111"}, + }, + "dev2": { + Request: "sub1", + Addresses: []string{"22.222.222.22:22222"}, + }, + }, + Revision: 1, + }, + wantAdd: []string{"dev2"}, + wantUpdate: []string{"dev1"}, + wantDelete: []string{"dev3"}, + }, { + desc: "modify subscription", + initial: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{}, + "sub3": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev2": { + Request: "sub1", + Addresses: []string{"22.222.222.22:22222"}, + }, + "dev3": { + Request: "sub3", + Addresses: []string{"33.333.333.33:33333"}, + }, + }, + Revision: 0, + }, + toLoad: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path2"}}, + }, + }, + }, + }, + }, + }, + "sub3": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev2": { + Request: "sub1", + Addresses: []string{"22.222.222.22:22222"}, + }, + "dev3": { + Request: "sub3", + Addresses: []string{"33.333.333.33:33333"}, + }, + }, + Revision: 1, + }, + wantUpdate: []string{"dev1", "dev2"}, + }, { + desc: "modify subscription key", + initial: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path2"}}, + }, + }, + }, + }, + }, + }, + "sub3": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev2": { + Request: "sub1", + Addresses: []string{"22.222.222.22:22222"}, + }, + "dev3": { + Request: "sub3", + Addresses: []string{"33.333.333.33:33333"}, + }, + }, + Revision: 0, + }, + toLoad: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{ + &gpb.SubscriptionList{ + Subscription: []*gpb.Subscription{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{{Name: "path2"}}, + }, + }, + }, + }, + }, + }, + "sub3": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev2": { + Request: "sub3", + Addresses: []string{"22.222.222.22:22222"}, + }, + "dev3": { + Request: "sub3", + Addresses: []string{"33.333.333.33:33333"}, + }, + }, + Revision: 1, + }, + wantUpdate: []string{"dev2"}, + }, + } { + r := &record{} + h := Handler{ + Add: func(c Update) { + r.adds = append(r.adds, Update{c.Name, c.Request, c.Target}) + }, + Update: func(c Update) { + r.updates = append(r.updates, Update{c.Name, c.Request, c.Target}) + }, + Delete: func(s string) { + r.deletes = append(r.deletes, s) + }, + } + c := NewConfig(h) + c.configuration = tt.initial + err := c.Load(tt.toLoad) + switch { + case err == nil && !tt.err: + if diff := cmp.Diff(tt.toLoad, c.Current(), protocmp.Transform()); diff != "" { + t.Errorf("%v: wrong state: (-want +got)\n%s", tt.desc, diff) + } + updates := func(want []string, c *pb.Configuration) []Update { + if want == nil { + return nil + } + u := []Update{} + for _, n := range want { + ta := c.GetTarget()[n] + u = append(u, Update{ + Name: n, + Request: c.GetRequest()[ta.GetRequest()], + Target: ta, + }) + } + return u + } + r.assertLast(t, tt.desc, updates(tt.wantAdd, tt.toLoad), updates(tt.wantUpdate, tt.toLoad), tt.wantDelete) + case err == nil: + t.Errorf("%v: did not get expected error", tt.desc) + case !tt.err: + t.Errorf("%v: unexpected error %v", tt.desc, err) + } + } +} + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + valid bool + config *pb.Configuration + }{{ + desc: "valid configuration with multiple targets", + valid: true, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "target1": &pb.Target{ + Addresses: []string{"192.168.0.10:10162"}, + Request: "interfaces", + }, + "target2": &pb.Target{ + Addresses: []string{"192.168.0.11:10162"}, + Request: "interfaces", + }, + }, + }, + }, { + desc: "empty configuration", + valid: true, + config: &pb.Configuration{}, + }, { + desc: "request but no targets", + valid: true, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + }, + }, { + desc: "target with empty name", + valid: false, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "": &pb.Target{ + Addresses: []string{"192.168.0.10:10162"}, + Request: "interfaces", + }, + "target2": &pb.Target{ + Addresses: []string{"192.168.0.11:10162"}, + Request: "interfaces", + }, + }, + }, + }, { + desc: "missing target configuration", + valid: false, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "target1": nil, + "target2": &pb.Target{ + Addresses: []string{"192.168.0.11:10162"}, + Request: "interfaces", + }, + }, + }, + }, { + desc: "missing target address", + valid: false, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "target1": &pb.Target{ + Request: "interfaces", + }, + }, + }, + }, { + desc: "missing target request", + valid: false, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "target1": &pb.Target{ + Addresses: []string{"192.168.0.10:10162"}, + }, + }, + }, + }, { + desc: "invalid target request", + valid: false, + config: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "interfaces": &gpb.SubscribeRequest{}, + }, + Target: map[string]*pb.Target{ + "target1": &pb.Target{ + Addresses: []string{"192.168.0.10:10162"}, + Request: "qos", + }, + }, + }, + }} + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + err := Validate(test.config) + switch { + case test.valid && err != nil: + t.Errorf("got error %v, want nil for configuration: %s", err, test.config.String()) + case !test.valid && err == nil: + t.Errorf("got nit, want error for configuration: %s", test.config.String()) + } + }) + } +} + +func TestNewConfigWithBase(t *testing.T) { + tests := []struct { + desc string + initial *pb.Configuration + wantErr bool + }{ + { + desc: "valid", + initial: &pb.Configuration{ + Request: map[string]*gpb.SubscribeRequest{ + "sub1": &gpb.SubscribeRequest{ + Request: &gpb.SubscribeRequest_Subscribe{}, + }, + }, + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + "dev2": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + }, + Revision: 1, + }, + }, { + desc: "invalid", + initial: &pb.Configuration{ + // Missing Request mapping. + Target: map[string]*pb.Target{ + "dev1": { + Request: "sub1", + Addresses: []string{"11.111.111.11:11111"}, + }, + }, + Revision: 1, + }, + wantErr: true, + }, { + desc: "nil diffbase", + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + c, err := NewConfigWithBase(Handler{}, tt.initial) + switch { + case tt.wantErr && err != nil: + // Expected. + case !tt.wantErr && err != nil: + t.Fatalf("NewConfig got error %v, want no error", err) + case tt.wantErr && err == nil: + t.Fatalf("NewConfig got no error, want error") + default: + if diff := cmp.Diff(tt.initial, c.Current(), protocmp.Transform()); diff != "" { + t.Errorf("Current() got diff (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/agent.go b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/agent.go new file mode 100644 index 0000000000000000000000000000000000000000..21d2fd954d1a0cda465317d228346e14c05faa5e --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/agent.go @@ -0,0 +1,195 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package gnmi implements a gRPC gNMI agent for testing against a +// collector implementation. Each agent will generate a set of Value protocol +// buffer messages that create a queue of updates to be streamed from a +// synthetic device. +package gnmi + +import ( + "golang.org/x/net/context" + "errors" + "fmt" + "net" + "strings" + "sync" + + log "github.com/golang/glog" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + "github.com/openconfig/grpctunnel/tunnel" + + tunnelpb "github.com/openconfig/grpctunnel/proto/tunnel" + gnmipb "github.com/openconfig/gnmi/proto/gnmi" + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +// Agent manages a single gNMI agent implementation. Each client that connects +// via Subscribe or Get will receive a stream of updates based on the requested +// path and the provided initial configuration. +type Agent struct { + gnmipb.UnimplementedGNMIServer + mu sync.Mutex + s *grpc.Server + lis []net.Listener + target string + state fpb.State + config *fpb.Config + // cMu protects client. + cMu sync.Mutex + client *Client +} + +// New returns an initialized fake agent. +func New(config *fpb.Config, opts []grpc.ServerOption) (*Agent, error) { + if config == nil { + return nil, errors.New("config not provided") + } + s := grpc.NewServer(opts...) + reflection.Register(s) + + return NewFromServer(s, config) +} + +// NewFromServer returns a new initialized fake agent from provided server. +func NewFromServer(s *grpc.Server, config *fpb.Config) (*Agent, error) { + a := &Agent{ + s: s, + state: fpb.State_INIT, + config: config, + target: config.Target, + } + var err error + if a.config.Port < 0 { + a.config.Port = 0 + } + + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", a.config.Port)) + if err != nil { + return nil, fmt.Errorf("failed to open listener port %d: %v", a.config.Port, err) + } + a.lis = append(a.lis, lis) + + if config.TunnelAddr != "" { + targets := map[tunnel.Target]struct{}{tunnel.Target{ID: config.Target, Type: tunnelpb.TargetType_GNMI_GNOI.String()}: struct{}{}} + lis, err = tunnel.Listen(context.Background(), config.TunnelAddr, config.TunnelCrt, targets) + if err != nil { + return nil, fmt.Errorf("failed to open listener port %d: %v", a.config.Port, err) + } + a.lis = append(a.lis, lis) + } + + gnmipb.RegisterGNMIServer(a.s, a) + log.V(1).Infof("Created Agent: %s on %s", a.target, a.Address()) + go a.serve() + return a, nil +} + +// serve will start the agent serving and block until closed. +func (a *Agent) serve() error { + a.mu.Lock() + a.state = fpb.State_RUNNING + s := a.s + lis := a.lis + a.mu.Unlock() + if s == nil || lis == nil { + return fmt.Errorf("Serve() failed: not initialized") + } + + chErr := make(chan error, len(lis)) + for _, l := range lis { + go func(l net.Listener) { + log.Infof("listening: %s", l.Addr()) + chErr <- s.Serve(l) + }(l) + } + + for range lis { + if err := <-chErr; err != nil { + log.Infof("received error serving: %v", err) + return err + } + } + + return nil +} + +// Target returns the target name the agent is faking. +func (a *Agent) Target() string { + return a.target +} + +// Type returns the target type the agent is faking. +func (a *Agent) Type() string { + return a.config.ClientType.String() +} + +// Address returns the port the agent is listening to. +func (a *Agent) Address() string { + var addr string + for _, l := range a.lis { + // Skip tunnel listeners. + // We assume there is at most one non-tunnel listener. + if _, ok := l.(*tunnel.Listener); !ok { + addr = strings.Replace(l.Addr().String(), "[::]", "localhost", 1) + break + } + } + return addr +} + +// State returns the current state of the agent. +func (a *Agent) State() fpb.State { + a.mu.Lock() + defer a.mu.Unlock() + return a.state +} + +// Close shuts down the agent. +func (a *Agent) Close() { + a.mu.Lock() + defer a.mu.Unlock() + a.state = fpb.State_STOPPED + if a.s == nil { + return + } + a.s.Stop() + for _, l := range a.lis { + l.Close() + } + a.s = nil + a.lis = nil +} + +// Subscribe implements the gNMI Subscribe RPC. +func (a *Agent) Subscribe(stream gnmipb.GNMI_SubscribeServer) error { + c := NewClient(a.config) + defer c.Close() + + a.cMu.Lock() + a.client = c + a.cMu.Unlock() + + return c.Run(stream) +} + +// Requests returns the subscribe requests received by the most recently created client. +func (a *Agent) Requests() []*gnmipb.SubscribeRequest { + a.cMu.Lock() + defer a.cMu.Unlock() + return a.client.requests +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/client.go b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/client.go new file mode 100644 index 0000000000000000000000000000000000000000..99b304c199027ef06d2b70e66128992202edd64a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/client.go @@ -0,0 +1,314 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package gnmi + +import ( + "fmt" + "io" + "sync" + + log "github.com/golang/glog" + "github.com/kylelemons/godebug/pretty" + "google.golang.org/grpc/codes" + "google.golang.org/grpc" + "github.com/openconfig/gnmi/testing/fake/queue" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +// Client contains information about a client that has connected to the fake. +type Client struct { + errors int64 + config *fpb.Config + polled chan struct{} + mu sync.RWMutex + canceled bool + q queue.Queue + subscribe *gpb.SubscriptionList + requests []*gpb.SubscribeRequest +} + +// NewClient returns a new initialized client. +func NewClient(config *fpb.Config) *Client { + return &Client{ + config: config, + polled: make(chan struct{}), + } +} + +// String returns the target the client is querying. +func (c *Client) String() string { + return c.config.Target +} + +// Run starts the client. The first message received must be a +// SubscriptionList. Once the client is started, it will run until the stream +// is closed or the schedule completes. For Poll queries the Run will block +// internally after sync until a Poll request is made to the server. This is +// important as the test may look like a deadlock since it can cause a timeout. +// Also if you Reset the client the change will not take effect until after the +// previous queue has been drained of notifications. +func (c *Client) Run(stream gpb.GNMI_SubscribeServer) (err error) { + if c.config == nil { + return grpc.Errorf(codes.FailedPrecondition, "cannot start client: config is nil") + } + if stream == nil { + return grpc.Errorf(codes.FailedPrecondition, "cannot start client: stream is nil") + } + + defer func() { + if err != nil { + c.errors++ + } + }() + + query, err := stream.Recv() + if err != nil { + if err == io.EOF { + return grpc.Errorf(codes.Aborted, "stream EOF received before init") + } + return grpc.Errorf(grpc.Code(err), "received error from client") + } + c.requests = append(c.requests, query) + log.V(1).Infof("Client %s recieved initial query: %v", c, query) + + c.subscribe = query.GetSubscribe() + if c.subscribe == nil { + return grpc.Errorf(codes.InvalidArgument, "first message must be SubscriptionList: %q", query) + } + // Initialize the queue used between send and recv. + if err = c.reset(); err != nil { + return grpc.Errorf(codes.Aborted, "failed to initialize the queue: %v", err) + } + + log.V(1).Infof("Client %s running", c) + go c.recv(stream) + c.send(stream) + log.V(1).Infof("Client %s shutdown", c) + return nil +} + +// Close will cancel the client context and will cause the send and recv goroutines to exit. +func (c *Client) Close() { + c.mu.Lock() + defer c.mu.Unlock() + c.canceled = true +} + +// Config returns the current config of the client. +func (c *Client) Config() *fpb.Config { + return c.config +} + +var syncResp = &gpb.SubscribeResponse{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, +} + +func (c *Client) recv(stream gpb.GNMI_SubscribeServer) { + for { + event, err := stream.Recv() + switch err { + default: + log.V(1).Infof("Client %s received error: %v", c, err) + c.Close() + return + case io.EOF: + log.V(1).Infof("Client %s received io.EOF", c) + return + case nil: + c.requests = append(c.requests, event) + } + if c.subscribe.Mode == gpb.SubscriptionList_POLL { + log.V(1).Infof("Client %s received Poll event: %v", c, event) + if _, ok := event.Request.(*gpb.SubscribeRequest_Poll); !ok { + log.V(1).Infof("Client %s received invalid Poll event: %v", c, event) + c.Close() + return + } + if err = c.reset(); err != nil { + c.Close() + return + } + c.polled <- struct{}{} + continue + } + log.V(1).Infof("Client %s received invalid event: %s", c, event) + } +} + +// processQueue makes a copy of q then will process values in the queue until +// the queue is complete or an error. Each value is converted into a gNMI +// notification and sent on stream. +func (c *Client) processQueue(stream gpb.GNMI_SubscribeServer) error { + c.mu.RLock() + q := c.q + c.mu.RUnlock() + if q == nil { + return fmt.Errorf("nil client queue nothing to do") + } + for { + c.mu.RLock() + canceled := c.canceled + c.mu.RUnlock() + if canceled { + return fmt.Errorf("client canceled") + } + event, err := q.Next() + if err != nil { + c.errors++ + return fmt.Errorf("unexpected queue Next(): %v", err) + } + if event == nil { + switch { + case c.subscribe.Mode == gpb.SubscriptionList_POLL: + <-c.polled + log.V(1).Infof("Client %s received poll", c) + return nil + case c.config.DisableEof: + return fmt.Errorf("send exiting due to disabled EOF") + } + return fmt.Errorf("end of updates") + } + var resp *gpb.SubscribeResponse + switch v := event.(type) { + case *fpb.Value: + if resp, err = valToResp(v); err != nil { + c.errors++ + return err + } + case *gpb.SubscribeResponse: + resp = v + } + // If the subscription request specified a target explicitly... + if sp := c.subscribe.GetPrefix(); sp != nil { + if target := sp.Target; target != "" { + // and the message is an update... + if update := resp.GetUpdate(); update != nil { + // then set target in the prefix. + if update.Prefix == nil { + update.Prefix = &gpb.Path{} + } + update.Prefix.Target = target + } + } + } + log.V(1).Infof("Client %s sending:\n%v", c, resp) + err = stream.Send(resp) + if err != nil { + c.errors++ + return err + } + } +} + +// send runs until process Queue returns an error. Each loop is meant to allow +// for a reset of the sending queue based on query type. +func (c *Client) send(stream gpb.GNMI_SubscribeServer) { + for { + if err := c.processQueue(stream); err != nil { + log.Errorf("Client %s error: %v", c, err) + return + } + } +} + +// SetConfig will replace the current configuration of the Client. If the client +// is running then the change will not take effect until the queue is drained +// of notifications. +func (c *Client) SetConfig(config *fpb.Config) { + c.mu.Lock() + defer c.mu.Unlock() + c.config = config +} + +func (c *Client) reset() error { + c.mu.Lock() + defer c.mu.Unlock() + log.V(1).Infof("Client %s using config:\n%s", c, pretty.Sprint(c.config)) + switch { + default: + q := queue.New(c.config.GetEnableDelay(), c.config.Seed, c.config.Values) + // Inject sync message after latest provided update in the config. + if !c.config.DisableSync { + q.Add(&fpb.Value{ + Timestamp: &fpb.Timestamp{Timestamp: q.Latest()}, + Repeat: 1, + Value: &fpb.Value_Sync{uint64(1)}, + }) + } + c.q = q + case c.config.GetFixed() != nil: + q := queue.NewFixed(c.config.GetFixed().Responses, c.config.EnableDelay) + // Inject sync message after latest provided update in the config. + if !c.config.DisableSync { + q.Add(syncResp) + } + c.q = q + } + return nil +} + +// valToResp converts a fake_proto Value to its corresponding gNMI proto stream +// response type. +// fake_proto sync values are converted to gNMI subscribe responses containing +// SyncResponses. +// All other fake_proto values are assumed to be gNMI subscribe responses +// containing Updates. +func valToResp(val *fpb.Value) (*gpb.SubscribeResponse, error) { + switch val.GetValue().(type) { + case *fpb.Value_Delete: + return &gpb.SubscribeResponse{ + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: val.Timestamp.Timestamp, + Delete: []*gpb.Path{{Element: val.Path}}, + }, + }, + }, nil + case *fpb.Value_Sync: + var sync bool + if queue.ValueOf(val).(uint64) > 0 { + sync = true + } + return &gpb.SubscribeResponse{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: sync, + }, + }, nil + default: + tv := queue.TypedValueOf(val) + if tv == nil { + return nil, fmt.Errorf("failed to get TypedValue of %s", val) + } + return &gpb.SubscribeResponse{ + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: val.Timestamp.Timestamp, + Update: []*gpb.Update{ + { + Path: &gpb.Path{Element: val.Path}, + Val: tv, + }, + }, + }, + }, + }, nil + } +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/README.md b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/README.md new file mode 100644 index 0000000000000000000000000000000000000000..074fef73fb89d31420624b22cb6ba4f31a834dee --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/README.md @@ -0,0 +1,30 @@ +## Running the fake gNMI target + +First create a config file (text protobuf from `testing/fake/proto/fake.proto`). + +You can use a `gen_fake_config` tool in this repo (modify +`../gen_fake_config/gen_config.go` as needed and `go run +../gen_fake_config/gen_config.go`). + +Next, compile the server binary (`go build` or `go install`). + +Make sure you have TLS key pair for the server (a self-signed pair is fine). + +Finally, run: + +``` +$ fake_server --config config.pb.txt --text --port 8080 --server_crt <path to server TLS cert> --server_key <path to server TLS key> --allow_no_client_auth --logtostderr +``` + +(replace binary and config paths as needed). + +If you wish to verify client TLS authentication, don't use +`--allow_no_client_auth` and set `--ca_crt` to CA certificate path. + +To verify your server is running, try: + +``` +$ gnmi_cli -a localhost:8080 -q '*' -logtostderr -insecure -qt s +``` + +Check the `./server --help` output for full flag set. diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/config.pb.txt b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/config.pb.txt new file mode 100644 index 0000000000000000000000000000000000000000..e183242d8bda4483e9774b8cf13a2274c059ac49 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/config.pb.txt @@ -0,0 +1,11 @@ +target: "targetclient" +seed: 12345 +values: < + path: "interfaces" + repeat: 3 + int_value: < + value: 4 + > +> + +client_type: GRPC_GNMI diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/example-config.pb.txt b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/example-config.pb.txt new file mode 100755 index 0000000000000000000000000000000000000000..d925a336159ca05ce9d2c83c94cc44990b8e6eb8 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/example-config.pb.txt @@ -0,0 +1,2 @@ +target: "insert-target-name-here" +client_type: GRPC_GNMI diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/server.go b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/server.go new file mode 100644 index 0000000000000000000000000000000000000000..015ebd1c92e3b63b6fb5f791c4b88ccb562ebde0 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/fake_server/server.go @@ -0,0 +1,125 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// The fake_server is a simple gRPC gnmi agent implementation which will take a +// configuration and start a listening service for the configured target. +package main + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + + "flag" + + log "github.com/golang/glog" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/testing/fake/gnmi" + + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +var ( + configFile = flag.String("config", "", "configuration file to load") + text = flag.Bool("text", false, "use text configuration file") + port = flag.Int("port", -1, "port to listen on") + + caCert = flag.String("ca_crt", "", "CA certificate for client certificate validation. Optional.") + serverCert = flag.String("server_crt", "", "TLS server certificate") + serverKey = flag.String("server_key", "", "TLS server private key") + allowNoClientCert = flag.Bool("allow_no_client_auth", false, "When set, fake_server will request but not require a client certificate.") + + tunnelAddr = flag.String("tunnel_addr", "", "tunnel server address") + tunnelCrt = flag.String("tunnel_crt", "", "tunnel server cert file") +) + +func loadConfig(fileName string) (*fpb.Config, error) { + in, err := ioutil.ReadFile(fileName) + if err != nil { + return nil, err + } + cfg := &fpb.Config{} + if *text { + if err := proto.UnmarshalText(string(in), cfg); err != nil { + return nil, fmt.Errorf("failed to parse text file %s: %v", fileName, err) + } + } else { + if err := proto.Unmarshal(in, cfg); err != nil { + return nil, fmt.Errorf("failed to parse %s: %v", fileName, err) + } + } + return cfg, nil +} + +func main() { + flag.Parse() + switch { + case *configFile == "": + log.Errorf("config must be set.") + return + case *port < 0: + log.Errorf("port must be >= 0.") + return + } + cfg, err := loadConfig(*configFile) + if err != nil { + log.Errorf("Failed to load %s: %v", *configFile, err) + return + } + + certificate, err := tls.LoadX509KeyPair(*serverCert, *serverKey) + if err != nil { + log.Exitf("could not load server key pair: %s", err) + } + tlsCfg := &tls.Config{ + ClientAuth: tls.RequireAndVerifyClientCert, + Certificates: []tls.Certificate{certificate}, + } + if *allowNoClientCert { + // RequestClientCert will ask client for a certificate but won't + // require it to proceed. If certificate is provided, it will be + // verified. + tlsCfg.ClientAuth = tls.RequestClientCert + } + + if *caCert != "" { + ca, err := ioutil.ReadFile(*caCert) + if err != nil { + log.Exitf("could not read CA certificate: %s", err) + } + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(ca); !ok { + log.Exit("failed to append CA certificate") + } + tlsCfg.ClientCAs = certPool + } + + opts := []grpc.ServerOption{grpc.Creds(credentials.NewTLS(tlsCfg))} + cfg.Port = int32(*port) + cfg.TunnelAddr = *tunnelAddr + cfg.TunnelCrt = *tunnelCrt + a, err := gnmi.New(cfg, opts) + if err != nil { + log.Errorf("Failed to create gNMI server: %v", err) + return + } + + log.Infof("Starting RPC server on address: %s", a.Address()) + select {} // block forever +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/gen_fake_config/gen_config.go b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/gen_fake_config/gen_config.go new file mode 100644 index 0000000000000000000000000000000000000000..b4468489779ce8085297f4092e8d037d7c470451 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/cmd/gen_fake_config/gen_config.go @@ -0,0 +1,63 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// The gen_fake_config command converts a hardcoded fake.proto message into a +// textual protobuf. The source code is intended to be modified manually to +// generate a valid text proto, instead of writing it by hand. +package main + +import ( + "io/ioutil" + "os" + + log "github.com/golang/glog" + "github.com/golang/protobuf/proto" + + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +// Modify the config below to change generated output. +var ( + outputPath = "config.pb.txt" + + config = &fpb.Config{ + Target: "fake target name", + Seed: 12345, + Values: []*fpb.Value{ + { + Path: []string{"a", "b"}, + Repeat: 3, + Value: &fpb.Value_IntValue{&fpb.IntValue{Value: 4}}, + }, + { + Path: []string{"b", "c"}, + Repeat: 5, + Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "foo"}}, + }, + }, + DisableSync: false, + DisableEof: false, + EnableDelay: false, + ClientType: fpb.Config_GRPC_GNMI, + } +) + +func main() { + out := proto.MarshalTextString(config) + if err := ioutil.WriteFile(outputPath, []byte(out), os.ModePerm); err != nil { + log.Exit(err) + } +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/gnmi/gnmi_test.go b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/gnmi_test.go new file mode 100644 index 0000000000000000000000000000000000000000..802681c860f607de69844fa16b446299f7dc79f3 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/gnmi/gnmi_test.go @@ -0,0 +1,459 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package gnmi + +import ( + "golang.org/x/net/context" + "crypto/tls" + "errors" + "fmt" + "io" + "strings" + "sync" + "testing" + + "github.com/kylelemons/godebug/pretty" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc" + "github.com/openconfig/gnmi/testing/fake/testing/grpc/config" + + gnmipb "github.com/openconfig/gnmi/proto/gnmi" + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +type direction string + +const ( + sendDirection direction = "Send" + recvDirection direction = "Recv" + noneDirection direction = "None" +) + +type event interface { + Direction() direction + String() string +} + +type cancelEvent struct { + d direction +} + +func (c *cancelEvent) String() string { return fmt.Sprintf("%s: Cancel Event", c.d) } +func (c *cancelEvent) Direction() direction { return c.d } + +type errorEvent struct { + d direction + err error +} + +func (e *errorEvent) String() string { + if e.err == nil { + return fmt.Sprintf("%s: no error", e.d) + } + return fmt.Sprintf("%s: %s", e.d, e.err.Error()) +} +func (e *errorEvent) Direction() direction { return e.d } + +type receiveEvent struct { + d direction + e *gnmipb.SubscribeRequest +} + +func (r *receiveEvent) String() string { return fmt.Sprintf("%s: Event\n%s", r.d, r.e) } +func (r *receiveEvent) Direction() direction { return r.d } + +type fakeStream struct { + grpc.ServerStream + curr int + events []event + recv []*gnmipb.SubscribeResponse + ctx context.Context + Cancel func() + rEvent chan event + sEvent chan event + mu sync.Mutex + synced int +} + +func newFakeStream(events []event) *fakeStream { + ctx, cancel := context.WithCancel(context.Background()) + f := &fakeStream{ + events: events, + ctx: ctx, + recv: []*gnmipb.SubscribeResponse{}, + Cancel: cancel, + rEvent: make(chan event), + sEvent: make(chan event), + } + go func() { + for _, e := range events { + switch e.Direction() { + default: + switch e.(type) { + case *cancelEvent: + f.Cancel() + return + } + case sendDirection: + f.sEvent <- e + case recvDirection: + f.rEvent <- e + } + } + f.Cancel() + }() + go func() { + <-f.ctx.Done() + close(f.sEvent) + close(f.rEvent) + }() + return f +} + +func (f *fakeStream) Send(resp *gnmipb.SubscribeResponse) (err error) { + if resp.GetSyncResponse() { + f.mu.Lock() + f.synced = 1 + f.mu.Unlock() + } + f.recv = append(f.recv, resp) + _, ok := <-f.sEvent + if !ok { + return io.EOF + } + return nil +} + +func (f *fakeStream) Recv() (resp *gnmipb.SubscribeRequest, err error) { + e, ok := <-f.rEvent + if !ok { + return nil, io.EOF + } + switch v := e.(type) { + default: + return nil, io.EOF + case *receiveEvent: + return v.e, nil + case *errorEvent: + return nil, v.err + } +} + +func (f *fakeStream) Context() context.Context { + return f.ctx +} + +func TestClientCreate(t *testing.T) { + defaultConfig := &fpb.Config{ + Target: "arista", + Port: -1, + Values: []*fpb.Value{{ + Path: []string{"interfaces", "interface[name=Port-Channel1]", "state", "counters", "in-octets"}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 0, + Distribution: &fpb.IntValue_Range{ + &fpb.IntRange{ + Minimum: 0, + Maximum: 10000000, + DeltaMax: 100, + DeltaMin: 0, + }}}}, + }}, + } + tests := []struct { + config *fpb.Config + events []event + err codes.Code + }{{ + config: nil, + err: codes.FailedPrecondition, + }, { + config: defaultConfig, + err: codes.Aborted, + }, { + config: defaultConfig, + events: []event{ + &cancelEvent{d: "None"}, + }, + err: codes.Aborted, + }, { + config: defaultConfig, + events: []event{ + &receiveEvent{d: "Recv"}, + }, + err: codes.InvalidArgument, + }, { + config: defaultConfig, + events: []event{ + &receiveEvent{d: "Recv", e: &gnmipb.SubscribeRequest{ + Request: &gnmipb.SubscribeRequest_Subscribe{}, + }}, + &receiveEvent{d: "Recv"}, + }, + err: codes.InvalidArgument, + }, { + config: defaultConfig, + events: []event{ + &receiveEvent{d: "Recv", e: &gnmipb.SubscribeRequest{ + Request: &gnmipb.SubscribeRequest_Subscribe{ + Subscribe: &gnmipb.SubscriptionList{}, + }, + }}, + &receiveEvent{d: "Recv"}, + &errorEvent{d: "Recv", err: errors.New("cancelable error")}, + }, + err: codes.OK, + }, { + config: &fpb.Config{ + Target: "arista", + Port: -1, + Values: []*fpb.Value{{ + Path: []string{"interfaces", "interface[name=Port-Channel1]", "state", "counters", "in-octets"}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 0, + Distribution: &fpb.IntValue_Range{ + &fpb.IntRange{ + Minimum: 0, + Maximum: 10000000, + DeltaMax: 100, + DeltaMin: 0, + }}}}, + Repeat: 2, + }}, + }, + events: []event{ + &receiveEvent{d: "Recv", e: &gnmipb.SubscribeRequest{ + Request: &gnmipb.SubscribeRequest_Subscribe{ + Subscribe: &gnmipb.SubscriptionList{}, + }, + }}, + &receiveEvent{d: "Recv"}, + &receiveEvent{d: "Recv"}, + }, + err: codes.OK, + }} + pp := pretty.Config{ + IncludeUnexported: true, + PrintStringers: true, + } + for i, tt := range tests { + t.Run(fmt.Sprintf("Test case %d", i+1), func(t *testing.T) { + c := NewClient(tt.config) + s := newFakeStream(tt.events) + gotErr := c.Run(s) + if gotErr != nil { + if got, want := grpc.Code(gotErr), tt.err; got != want { + t.Errorf("Test:\n%s\nRun() unexpected error %s: got %s, want %s", pp.Sprint(tt), gotErr.Error(), got, want) + } + return + } + if tt.err != codes.OK { + t.Errorf("Test:\n%s\nRun() expected error %s: got nil", pp.Sprint(tt), tt.err) + } + }) + } +} + +func festClientSend(t *testing.T) { + defaultConfig := &fpb.Config{ + Target: "arista", + Port: -1, + Values: []*fpb.Value{}, + } + tests := []struct { + config *fpb.Config + wantErr string + events []event + }{{ + config: defaultConfig, + wantErr: "invalid configuration", + events: []event{ + &receiveEvent{d: "Send"}, + }, + }, { + config: &fpb.Config{ + Target: "arista", + Port: -1, + Values: []*fpb.Value{{ + Path: []string{"interfaces", "interface[name=Port-Channel1]", "state", "counters", "in-octets"}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 0, + Distribution: &fpb.IntValue_Range{ + &fpb.IntRange{ + Minimum: 0, + Maximum: 10000000, + DeltaMax: 100, + DeltaMin: 0, + }}}}, + Repeat: 2, + }}, + }, + events: []event{ + &receiveEvent{d: "Send"}, + &receiveEvent{d: "Send"}, + &receiveEvent{d: "Send"}, + }, + }} + pp := pretty.Config{ + IncludeUnexported: true, + PrintStringers: true, + } + for _, tt := range tests { + c := NewClient(tt.config) + s := newFakeStream(tt.events) + defer s.Cancel() + c.subscribe = &gnmipb.SubscriptionList{Mode: gnmipb.SubscriptionList_ONCE} + err := c.reset() + switch { + case err == nil && tt.wantErr != "": + t.Fatalf("reset() failed: got %v, want %v", err, tt.wantErr) + case err != nil && !strings.HasPrefix(err.Error(), tt.wantErr): + t.Fatalf("reset() failed: got %q, want %q", err, tt.wantErr) + } + c.send(s) + t.Logf("Received Events:\n%s\n", pp.Sprint(s.recv)) + if c.errors != 0 { + t.Fatalf("send(%s) errored", pp.Sprint(tt.events)) + } + s.mu.Lock() + synced := s.synced + s.mu.Unlock() + if synced == 0 { + t.Fatalf("send(%s) failed to sync stream", pp.Sprint(tt.events)) + } + } +} + +func TestNewAgent(t *testing.T) { + tests := []struct { + config *fpb.Config + err error + }{{ + config: nil, + err: fmt.Errorf("config not provided"), + }, { + config: &fpb.Config{}, + err: fmt.Errorf("config not provided"), + }, { + config: &fpb.Config{ + Target: "arista", + }, + err: nil, + }, { + config: &fpb.Config{ + Target: "arista", + Port: 0, + }, + err: nil, + }, { + config: &fpb.Config{ + Target: "arista", + Port: -1, + }, + err: nil, + }, { + config: &fpb.Config{ + Target: "arista", + Port: -1, + }, + err: nil, + }, { + config: &fpb.Config{ + Target: "arista", + Port: -1, + Values: []*fpb.Value{{ + Path: []string{"interfaces", "interface[name=Port-Channel1]", "state", "counters", "in-octets"}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 0, + Distribution: &fpb.IntValue_Range{ + &fpb.IntRange{ + Minimum: 0, + Maximum: 10000000, + DeltaMax: 100, + DeltaMin: 0, + }}}}, + }}, + }, + err: nil, + }, { + config: &fpb.Config{ + Target: "arista", + Port: -1, + DisableSync: true, + Values: []*fpb.Value{{ + Path: []string{"interfaces", "interface[name=Port-Channel1]", "state", "counters", "in-octets"}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 0, + Distribution: &fpb.IntValue_Range{ + &fpb.IntRange{ + Minimum: 0, + Maximum: 10000000, + DeltaMax: 100, + DeltaMin: 0, + }}}}, + }, { + Value: &fpb.Value_Sync{uint64(1)}, + }}, + }, + err: nil, + }} + certOpt, err := config.WithSelfTLSCert() + if err != nil { + t.Fatal(err) + } + for _, tc := range tests { + t.Run(fmt.Sprintf("Test case %+v", tc.config), func(t *testing.T) { + a, err := New(tc.config, []grpc.ServerOption{certOpt}) + if err != nil { + if tc.err == nil || tc.err.Error() != err.Error() { + t.Fatalf("New(%q) error return: got %v, want %v", tc.config, err, tc.err) + } + return + } + conn, err := grpc.Dial(a.Address(), grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + InsecureSkipVerify: true, + }))) + if err != nil { + t.Fatalf("New(%q) failed to dial server: %s", tc.config, err) + } + c := gnmipb.NewGNMIClient(conn) + s, err := c.Subscribe(context.Background()) + if err != nil { + t.Fatalf("New(%q).Subscribe() failed: %v", tc.config, err) + } + sub := &gnmipb.SubscribeRequest{ + Request: &gnmipb.SubscribeRequest_Subscribe{ + Subscribe: &gnmipb.SubscriptionList{}, + }, + } + if err := s.Send(sub); err != nil { + t.Fatalf("New(%q).Send(%q) failed: got %s", tc.config, sub, err) + } + if _, err = s.Recv(); err != nil { + t.Fatalf("New(%q).Recv() failed: got %s", tc.config, err) + } + if got, want := a.State(), fpb.State_RUNNING; got != want { + t.Errorf("New(%q).State() failed: got %q, want %q", tc.config, got, want) + } + a.Close() + if got, want := a.State(), fpb.State_STOPPED; got != want { + t.Errorf("New(%q).Close() failed: got %q, want %q", tc.config, got, want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/proto/fake.pb.go b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..d44d21d0a38c7d645c45f020285e638103ed488a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake.pb.go @@ -0,0 +1,2541 @@ +// fake.proto describes the message format for creating integration tests for +// streaming telemetry components by generating a reproducible stream of +// updates from fake targets. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.12.4 +// source: github.com/openconfig/gnmi/testing/fake/proto/fake.proto + +package gnmi_fake + +import ( + any "github.com/golang/protobuf/ptypes/any" + gnmi "github.com/openconfig/gnmi/proto/gnmi" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type State int32 + +const ( + State_STOPPED State = 0 + State_INIT State = 1 + State_RUNNING State = 2 +) + +// Enum value maps for State. +var ( + State_name = map[int32]string{ + 0: "STOPPED", + 1: "INIT", + 2: "RUNNING", + } + State_value = map[string]int32{ + "STOPPED": 0, + "INIT": 1, + "RUNNING": 2, + } +) + +func (x State) Enum() *State { + p := new(State) + *p = x + return p +} + +func (x State) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (State) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_enumTypes[0].Descriptor() +} + +func (State) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_enumTypes[0] +} + +func (x State) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use State.Descriptor instead. +func (State) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{0} +} + +type Config_ClientType int32 + +const ( + Config_GRPC Config_ClientType = 0 + Config_STUBBY Config_ClientType = 1 + Config_GRPC_GNMI Config_ClientType = 2 + Config_GRPC_GNMI_PROD Config_ClientType = 3 +) + +// Enum value maps for Config_ClientType. +var ( + Config_ClientType_name = map[int32]string{ + 0: "GRPC", + 1: "STUBBY", + 2: "GRPC_GNMI", + 3: "GRPC_GNMI_PROD", + } + Config_ClientType_value = map[string]int32{ + "GRPC": 0, + "STUBBY": 1, + "GRPC_GNMI": 2, + "GRPC_GNMI_PROD": 3, + } +) + +func (x Config_ClientType) Enum() *Config_ClientType { + p := new(Config_ClientType) + *p = x + return p +} + +func (x Config_ClientType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Config_ClientType) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_enumTypes[1].Descriptor() +} + +func (Config_ClientType) Type() protoreflect.EnumType { + return &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_enumTypes[1] +} + +func (x Config_ClientType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Config_ClientType.Descriptor instead. +func (Config_ClientType) EnumDescriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{2, 0} +} + +// Configuration is used to store all agent configuration for the fake agent +// server. Each config describes a single agent hosted on the server. +type Configuration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Repeated list of targets to emulate. + Config []*Config `protobuf:"bytes,1,rep,name=config,proto3" json:"config,omitempty"` +} + +func (x *Configuration) Reset() { + *x = Configuration{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configuration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configuration) ProtoMessage() {} + +func (x *Configuration) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configuration.ProtoReflect.Descriptor instead. +func (*Configuration) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{0} +} + +func (x *Configuration) GetConfig() []*Config { + if x != nil { + return x.Config + } + return nil +} + +type Credentials struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *Credentials) Reset() { + *x = Credentials{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Credentials) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Credentials) ProtoMessage() {} + +func (x *Credentials) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Credentials.ProtoReflect.Descriptor instead. +func (*Credentials) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{1} +} + +func (x *Credentials) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Credentials) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +// Config is a collection of values that together represent the update streams +// for one or more fake devices. +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The target for which the fake will publish values for. + Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` + // Port for the agent to listen on. If 0 or unset the agent will pick a port + // for this agent. + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + // A global random seed used in generating subsequent values. Set to have + // reproducible results. + // + // Deprecated: Do not use. + Seed int64 `protobuf:"varint,6,opt,name=seed,proto3" json:"seed,omitempty"` + // The list of values generated. Each value will contain its corresponding + // target as the first string in the event.GetValue().path meaning that it is + // possible to generate streams that will be rejected by the cache for testing + // purposes. + // + // Deprecated: Do not use. + Values []*Value `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty"` + // Setting disable sync will keep the configured client from autogenerating a + // sync message. This allows negative testing on sync handling. + DisableSync bool `protobuf:"varint,4,opt,name=disable_sync,json=disableSync,proto3" json:"disable_sync,omitempty"` + // Type of client to fake either Stubby or GRPC based fake. + ClientType Config_ClientType `protobuf:"varint,5,opt,name=client_type,json=clientType,proto3,enum=gnmi.fake.Config_ClientType" json:"client_type,omitempty"` + // Disable EOF will hold open the subscription and not automagically close + // the stream once the value queue is empty. + DisableEof bool `protobuf:"varint,7,opt,name=disable_eof,json=disableEof,proto3" json:"disable_eof,omitempty"` + // Per RPC credentials for the agent. If not set no per RPC auth will be used. + Credentials *Credentials `protobuf:"bytes,8,opt,name=credentials,proto3" json:"credentials,omitempty"` + // TLS cert for use on the agent. If not set the transport will not be TLS. + Cert []byte `protobuf:"bytes,9,opt,name=cert,proto3" json:"cert,omitempty"` + // Honor the delay between events in the generated value streams. Default will + // play events as fast as the can be streamed. + EnableDelay bool `protobuf:"varint,10,opt,name=enable_delay,json=enableDelay,proto3" json:"enable_delay,omitempty"` + // Generator for value series for the target. + // + // Types that are assignable to Generator: + // *Config_Custom + // *Config_Random + // *Config_Fixed + Generator isConfig_Generator `protobuf_oneof:"generator"` + // tunnel_addr is the address of the tunnel server. + TunnelAddr string `protobuf:"bytes,11,opt,name=tunnel_addr,json=tunnelAddr,proto3" json:"tunnel_addr,omitempty"` + // tunnel_crt is the certificate file for the tunnel conection. + TunnelCrt string `protobuf:"bytes,12,opt,name=tunnel_crt,json=tunnelCrt,proto3" json:"tunnel_crt,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{2} +} + +func (x *Config) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *Config) GetPort() int32 { + if x != nil { + return x.Port + } + return 0 +} + +// Deprecated: Do not use. +func (x *Config) GetSeed() int64 { + if x != nil { + return x.Seed + } + return 0 +} + +// Deprecated: Do not use. +func (x *Config) GetValues() []*Value { + if x != nil { + return x.Values + } + return nil +} + +func (x *Config) GetDisableSync() bool { + if x != nil { + return x.DisableSync + } + return false +} + +func (x *Config) GetClientType() Config_ClientType { + if x != nil { + return x.ClientType + } + return Config_GRPC +} + +func (x *Config) GetDisableEof() bool { + if x != nil { + return x.DisableEof + } + return false +} + +func (x *Config) GetCredentials() *Credentials { + if x != nil { + return x.Credentials + } + return nil +} + +func (x *Config) GetCert() []byte { + if x != nil { + return x.Cert + } + return nil +} + +func (x *Config) GetEnableDelay() bool { + if x != nil { + return x.EnableDelay + } + return false +} + +func (m *Config) GetGenerator() isConfig_Generator { + if m != nil { + return m.Generator + } + return nil +} + +func (x *Config) GetCustom() *any.Any { + if x, ok := x.GetGenerator().(*Config_Custom); ok { + return x.Custom + } + return nil +} + +func (x *Config) GetRandom() *RandomGenerator { + if x, ok := x.GetGenerator().(*Config_Random); ok { + return x.Random + } + return nil +} + +func (x *Config) GetFixed() *FixedGenerator { + if x, ok := x.GetGenerator().(*Config_Fixed); ok { + return x.Fixed + } + return nil +} + +func (x *Config) GetTunnelAddr() string { + if x != nil { + return x.TunnelAddr + } + return "" +} + +func (x *Config) GetTunnelCrt() string { + if x != nil { + return x.TunnelCrt + } + return "" +} + +type isConfig_Generator interface { + isConfig_Generator() +} + +type Config_Custom struct { + Custom *any.Any `protobuf:"bytes,100,opt,name=custom,proto3,oneof"` +} + +type Config_Random struct { + Random *RandomGenerator `protobuf:"bytes,101,opt,name=random,proto3,oneof"` +} + +type Config_Fixed struct { + Fixed *FixedGenerator `protobuf:"bytes,102,opt,name=fixed,proto3,oneof"` +} + +func (*Config_Custom) isConfig_Generator() {} + +func (*Config_Random) isConfig_Generator() {} + +func (*Config_Fixed) isConfig_Generator() {} + +type FixedGenerator struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Responses []*gnmi.SubscribeResponse `protobuf:"bytes,1,rep,name=responses,proto3" json:"responses,omitempty"` +} + +func (x *FixedGenerator) Reset() { + *x = FixedGenerator{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FixedGenerator) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FixedGenerator) ProtoMessage() {} + +func (x *FixedGenerator) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FixedGenerator.ProtoReflect.Descriptor instead. +func (*FixedGenerator) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{3} +} + +func (x *FixedGenerator) GetResponses() []*gnmi.SubscribeResponse { + if x != nil { + return x.Responses + } + return nil +} + +type RandomGenerator struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Seed int64 `protobuf:"varint,1,opt,name=seed,proto3" json:"seed,omitempty"` + Values []*Value `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *RandomGenerator) Reset() { + *x = RandomGenerator{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RandomGenerator) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RandomGenerator) ProtoMessage() {} + +func (x *RandomGenerator) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RandomGenerator.ProtoReflect.Descriptor instead. +func (*RandomGenerator) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{4} +} + +func (x *RandomGenerator) GetSeed() int64 { + if x != nil { + return x.Seed + } + return 0 +} + +func (x *RandomGenerator) GetValues() []*Value { + if x != nil { + return x.Values + } + return nil +} + +// Delete will cause the value to be deleted at the Value's path. +type DeleteValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteValue) Reset() { + *x = DeleteValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteValue) ProtoMessage() {} + +func (x *DeleteValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteValue.ProtoReflect.Descriptor instead. +func (*DeleteValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{5} +} + +// Value is the main message that will trigger a stream of updates for a given +// path. A file containing a list of values can be used to simulate a network +// device for integration tests. +type Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The device specific, or OpenConfig path corresponding to a value. + Path []string `protobuf:"bytes,1,rep,name=path,proto3" json:"path,omitempty"` + // The initial timestamp and configuration on how the timestamp will change + // for subsequent values. If timestamp is not set the default will assume to + // be the current system time. + Timestamp *Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // If set, repeat indicates that the value should be repeated this many times, + // otherwise it is repeated indefinitely. + Repeat int32 `protobuf:"varint,6,opt,name=repeat,proto3" json:"repeat,omitempty"` + // A local random seed used in generating subsequent values for this path. If + // not set, will share the global random source with seed defined in Config. + Seed int64 `protobuf:"varint,7,opt,name=seed,proto3" json:"seed,omitempty"` + // The initial value of the chosen type including configuration on how the + // value will mutate for subsequent updates. + // + // Types that are assignable to Value: + // *Value_IntValue + // *Value_DoubleValue + // *Value_StringValue + // *Value_Sync + // *Value_Delete + // *Value_BoolValue + // *Value_UintValue + // *Value_StringListValue + Value isValue_Value `protobuf_oneof:"value"` +} + +func (x *Value) Reset() { + *x = Value{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Value) ProtoMessage() {} + +func (x *Value) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Value.ProtoReflect.Descriptor instead. +func (*Value) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{6} +} + +func (x *Value) GetPath() []string { + if x != nil { + return x.Path + } + return nil +} + +func (x *Value) GetTimestamp() *Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *Value) GetRepeat() int32 { + if x != nil { + return x.Repeat + } + return 0 +} + +func (x *Value) GetSeed() int64 { + if x != nil { + return x.Seed + } + return 0 +} + +func (m *Value) GetValue() isValue_Value { + if m != nil { + return m.Value + } + return nil +} + +func (x *Value) GetIntValue() *IntValue { + if x, ok := x.GetValue().(*Value_IntValue); ok { + return x.IntValue + } + return nil +} + +func (x *Value) GetDoubleValue() *DoubleValue { + if x, ok := x.GetValue().(*Value_DoubleValue); ok { + return x.DoubleValue + } + return nil +} + +func (x *Value) GetStringValue() *StringValue { + if x, ok := x.GetValue().(*Value_StringValue); ok { + return x.StringValue + } + return nil +} + +func (x *Value) GetSync() uint64 { + if x, ok := x.GetValue().(*Value_Sync); ok { + return x.Sync + } + return 0 +} + +func (x *Value) GetDelete() *DeleteValue { + if x, ok := x.GetValue().(*Value_Delete); ok { + return x.Delete + } + return nil +} + +func (x *Value) GetBoolValue() *BoolValue { + if x, ok := x.GetValue().(*Value_BoolValue); ok { + return x.BoolValue + } + return nil +} + +func (x *Value) GetUintValue() *UintValue { + if x, ok := x.GetValue().(*Value_UintValue); ok { + return x.UintValue + } + return nil +} + +func (x *Value) GetStringListValue() *StringListValue { + if x, ok := x.GetValue().(*Value_StringListValue); ok { + return x.StringListValue + } + return nil +} + +type isValue_Value interface { + isValue_Value() +} + +type Value_IntValue struct { + IntValue *IntValue `protobuf:"bytes,100,opt,name=int_value,json=intValue,proto3,oneof"` +} + +type Value_DoubleValue struct { + DoubleValue *DoubleValue `protobuf:"bytes,101,opt,name=double_value,json=doubleValue,proto3,oneof"` +} + +type Value_StringValue struct { + StringValue *StringValue `protobuf:"bytes,102,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type Value_Sync struct { + Sync uint64 `protobuf:"varint,103,opt,name=sync,proto3,oneof"` +} + +type Value_Delete struct { + Delete *DeleteValue `protobuf:"bytes,104,opt,name=delete,proto3,oneof"` +} + +type Value_BoolValue struct { + BoolValue *BoolValue `protobuf:"bytes,105,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Value_UintValue struct { + UintValue *UintValue `protobuf:"bytes,106,opt,name=uint_value,json=uintValue,proto3,oneof"` +} + +type Value_StringListValue struct { + StringListValue *StringListValue `protobuf:"bytes,107,opt,name=string_list_value,json=stringListValue,proto3,oneof"` +} + +func (*Value_IntValue) isValue_Value() {} + +func (*Value_DoubleValue) isValue_Value() {} + +func (*Value_StringValue) isValue_Value() {} + +func (*Value_Sync) isValue_Value() {} + +func (*Value_Delete) isValue_Value() {} + +func (*Value_BoolValue) isValue_Value() {} + +func (*Value_UintValue) isValue_Value() {} + +func (*Value_StringListValue) isValue_Value() {} + +type Timestamp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Initial timestamp for the corresponding value, nanoseconds since epoch. + // This value need have no relation to absolute real-time as the stream of + // of updates is generated without regard to the real clock and can be run + // repeatably at any time if the seed is set in the corresponding Value. + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // These values will vary the change in the timestamp for subsequent outputs + // by a value between delta_min and delta_max. Set to the same value to force + // a set periodic interval. + DeltaMin int64 `protobuf:"varint,2,opt,name=delta_min,json=deltaMin,proto3" json:"delta_min,omitempty"` + DeltaMax int64 `protobuf:"varint,3,opt,name=delta_max,json=deltaMax,proto3" json:"delta_max,omitempty"` +} + +func (x *Timestamp) Reset() { + *x = Timestamp{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Timestamp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Timestamp) ProtoMessage() {} + +func (x *Timestamp) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Timestamp.ProtoReflect.Descriptor instead. +func (*Timestamp) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{7} +} + +func (x *Timestamp) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Timestamp) GetDeltaMin() int64 { + if x != nil { + return x.DeltaMin + } + return 0 +} + +func (x *Timestamp) GetDeltaMax() int64 { + if x != nil { + return x.DeltaMax + } + return 0 +} + +type IntValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If distribution is IntRange, value is used as the initial value + // inside [minimum, maximum] and hold the value as it mutates. + // If distribution is IntList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + // Types that are assignable to Distribution: + // *IntValue_Range + // *IntValue_List + Distribution isIntValue_Distribution `protobuf_oneof:"distribution"` +} + +func (x *IntValue) Reset() { + *x = IntValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IntValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IntValue) ProtoMessage() {} + +func (x *IntValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IntValue.ProtoReflect.Descriptor instead. +func (*IntValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{8} +} + +func (x *IntValue) GetValue() int64 { + if x != nil { + return x.Value + } + return 0 +} + +func (m *IntValue) GetDistribution() isIntValue_Distribution { + if m != nil { + return m.Distribution + } + return nil +} + +func (x *IntValue) GetRange() *IntRange { + if x, ok := x.GetDistribution().(*IntValue_Range); ok { + return x.Range + } + return nil +} + +func (x *IntValue) GetList() *IntList { + if x, ok := x.GetDistribution().(*IntValue_List); ok { + return x.List + } + return nil +} + +type isIntValue_Distribution interface { + isIntValue_Distribution() +} + +type IntValue_Range struct { + Range *IntRange `protobuf:"bytes,2,opt,name=range,proto3,oneof"` +} + +type IntValue_List struct { + List *IntList `protobuf:"bytes,3,opt,name=list,proto3,oneof"` +} + +func (*IntValue_Range) isIntValue_Distribution() {} + +func (*IntValue_List) isIntValue_Distribution() {} + +type IntRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The range of the value allowed. + Minimum int64 `protobuf:"varint,1,opt,name=minimum,proto3" json:"minimum,omitempty"` + Maximum int64 `protobuf:"varint,2,opt,name=maximum,proto3" json:"maximum,omitempty"` + // If set, the value is cumulative and the subsequent value is value + delta + // where delta is randomly chosen between delta_min and delta_max. The range + // minimum and maximum are still respected and values will saturate at the + // boundaries if they are exceeded. If not set subsequent value is a value + // randomly chosen between minimum and maximum. + DeltaMin int64 `protobuf:"varint,3,opt,name=delta_min,json=deltaMin,proto3" json:"delta_min,omitempty"` + DeltaMax int64 `protobuf:"varint,4,opt,name=delta_max,json=deltaMax,proto3" json:"delta_max,omitempty"` +} + +func (x *IntRange) Reset() { + *x = IntRange{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IntRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IntRange) ProtoMessage() {} + +func (x *IntRange) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IntRange.ProtoReflect.Descriptor instead. +func (*IntRange) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{9} +} + +func (x *IntRange) GetMinimum() int64 { + if x != nil { + return x.Minimum + } + return 0 +} + +func (x *IntRange) GetMaximum() int64 { + if x != nil { + return x.Maximum + } + return 0 +} + +func (x *IntRange) GetDeltaMin() int64 { + if x != nil { + return x.DeltaMin + } + return 0 +} + +func (x *IntRange) GetDeltaMax() int64 { + if x != nil { + return x.DeltaMax + } + return 0 +} + +type IntList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of values which can be used. + Options []int64 `protobuf:"varint,1,rep,packed,name=options,proto3" json:"options,omitempty"` + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + Random bool `protobuf:"varint,2,opt,name=random,proto3" json:"random,omitempty"` +} + +func (x *IntList) Reset() { + *x = IntList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IntList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IntList) ProtoMessage() {} + +func (x *IntList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IntList.ProtoReflect.Descriptor instead. +func (*IntList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{10} +} + +func (x *IntList) GetOptions() []int64 { + if x != nil { + return x.Options + } + return nil +} + +func (x *IntList) GetRandom() bool { + if x != nil { + return x.Random + } + return false +} + +type DoubleValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If distribution is DoubleRange, value is used as the initial value + // inside [minimum, maximum] and hold the value as it mutates. + // If distribution is DoubleList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + // Types that are assignable to Distribution: + // *DoubleValue_Range + // *DoubleValue_List + Distribution isDoubleValue_Distribution `protobuf_oneof:"distribution"` +} + +func (x *DoubleValue) Reset() { + *x = DoubleValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DoubleValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleValue) ProtoMessage() {} + +func (x *DoubleValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleValue.ProtoReflect.Descriptor instead. +func (*DoubleValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{11} +} + +func (x *DoubleValue) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +func (m *DoubleValue) GetDistribution() isDoubleValue_Distribution { + if m != nil { + return m.Distribution + } + return nil +} + +func (x *DoubleValue) GetRange() *DoubleRange { + if x, ok := x.GetDistribution().(*DoubleValue_Range); ok { + return x.Range + } + return nil +} + +func (x *DoubleValue) GetList() *DoubleList { + if x, ok := x.GetDistribution().(*DoubleValue_List); ok { + return x.List + } + return nil +} + +type isDoubleValue_Distribution interface { + isDoubleValue_Distribution() +} + +type DoubleValue_Range struct { + Range *DoubleRange `protobuf:"bytes,2,opt,name=range,proto3,oneof"` +} + +type DoubleValue_List struct { + List *DoubleList `protobuf:"bytes,3,opt,name=list,proto3,oneof"` +} + +func (*DoubleValue_Range) isDoubleValue_Distribution() {} + +func (*DoubleValue_List) isDoubleValue_Distribution() {} + +type DoubleRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The range of the value allowed. + Minimum float64 `protobuf:"fixed64,1,opt,name=minimum,proto3" json:"minimum,omitempty"` + Maximum float64 `protobuf:"fixed64,2,opt,name=maximum,proto3" json:"maximum,omitempty"` + // If set, the value is cumulative and the subsequent value is value + delta + // where delta is randomly chosen between delta_min and delta_max. The range + // minimum and maximum are still respected. If not set subsequent value is a + // value randomly chosen between minimum and maximum. + DeltaMin float64 `protobuf:"fixed64,3,opt,name=delta_min,json=deltaMin,proto3" json:"delta_min,omitempty"` + DeltaMax float64 `protobuf:"fixed64,4,opt,name=delta_max,json=deltaMax,proto3" json:"delta_max,omitempty"` +} + +func (x *DoubleRange) Reset() { + *x = DoubleRange{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DoubleRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleRange) ProtoMessage() {} + +func (x *DoubleRange) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleRange.ProtoReflect.Descriptor instead. +func (*DoubleRange) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{12} +} + +func (x *DoubleRange) GetMinimum() float64 { + if x != nil { + return x.Minimum + } + return 0 +} + +func (x *DoubleRange) GetMaximum() float64 { + if x != nil { + return x.Maximum + } + return 0 +} + +func (x *DoubleRange) GetDeltaMin() float64 { + if x != nil { + return x.DeltaMin + } + return 0 +} + +func (x *DoubleRange) GetDeltaMax() float64 { + if x != nil { + return x.DeltaMax + } + return 0 +} + +type DoubleList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of values which can be used. + Options []float64 `protobuf:"fixed64,1,rep,packed,name=options,proto3" json:"options,omitempty"` + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order. + Random bool `protobuf:"varint,2,opt,name=random,proto3" json:"random,omitempty"` +} + +func (x *DoubleList) Reset() { + *x = DoubleList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DoubleList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleList) ProtoMessage() {} + +func (x *DoubleList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleList.ProtoReflect.Descriptor instead. +func (*DoubleList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{13} +} + +func (x *DoubleList) GetOptions() []float64 { + if x != nil { + return x.Options + } + return nil +} + +func (x *DoubleList) GetRandom() bool { + if x != nil { + return x.Random + } + return false +} + +type StringValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If distribution is StringList, value is used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + // Types that are assignable to Distribution: + // *StringValue_List + Distribution isStringValue_Distribution `protobuf_oneof:"distribution"` +} + +func (x *StringValue) Reset() { + *x = StringValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringValue) ProtoMessage() {} + +func (x *StringValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringValue.ProtoReflect.Descriptor instead. +func (*StringValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{14} +} + +func (x *StringValue) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (m *StringValue) GetDistribution() isStringValue_Distribution { + if m != nil { + return m.Distribution + } + return nil +} + +func (x *StringValue) GetList() *StringList { + if x, ok := x.GetDistribution().(*StringValue_List); ok { + return x.List + } + return nil +} + +type isStringValue_Distribution interface { + isStringValue_Distribution() +} + +type StringValue_List struct { + List *StringList `protobuf:"bytes,2,opt,name=list,proto3,oneof"` +} + +func (*StringValue_List) isStringValue_Distribution() {} + +type StringList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of strings which can be used. + Options []string `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + Random bool `protobuf:"varint,2,opt,name=random,proto3" json:"random,omitempty"` +} + +func (x *StringList) Reset() { + *x = StringList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringList) ProtoMessage() {} + +func (x *StringList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringList.ProtoReflect.Descriptor instead. +func (*StringList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{15} +} + +func (x *StringList) GetOptions() []string { + if x != nil { + return x.Options + } + return nil +} + +func (x *StringList) GetRandom() bool { + if x != nil { + return x.Random + } + return false +} + +type StringListValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If distribution is StringList, value is used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` + // Types that are assignable to Distribution: + // *StringListValue_List + Distribution isStringListValue_Distribution `protobuf_oneof:"distribution"` +} + +func (x *StringListValue) Reset() { + *x = StringListValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringListValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringListValue) ProtoMessage() {} + +func (x *StringListValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringListValue.ProtoReflect.Descriptor instead. +func (*StringListValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{16} +} + +func (x *StringListValue) GetValue() []string { + if x != nil { + return x.Value + } + return nil +} + +func (m *StringListValue) GetDistribution() isStringListValue_Distribution { + if m != nil { + return m.Distribution + } + return nil +} + +func (x *StringListValue) GetList() *StringList { + if x, ok := x.GetDistribution().(*StringListValue_List); ok { + return x.List + } + return nil +} + +type isStringListValue_Distribution interface { + isStringListValue_Distribution() +} + +type StringListValue_List struct { + List *StringList `protobuf:"bytes,2,opt,name=list,proto3,oneof"` +} + +func (*StringListValue_List) isStringListValue_Distribution() {} + +type BoolValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If distribution is BoolList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + // Types that are assignable to Distribution: + // *BoolValue_List + Distribution isBoolValue_Distribution `protobuf_oneof:"distribution"` +} + +func (x *BoolValue) Reset() { + *x = BoolValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BoolValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BoolValue) ProtoMessage() {} + +func (x *BoolValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BoolValue.ProtoReflect.Descriptor instead. +func (*BoolValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{17} +} + +func (x *BoolValue) GetValue() bool { + if x != nil { + return x.Value + } + return false +} + +func (m *BoolValue) GetDistribution() isBoolValue_Distribution { + if m != nil { + return m.Distribution + } + return nil +} + +func (x *BoolValue) GetList() *BoolList { + if x, ok := x.GetDistribution().(*BoolValue_List); ok { + return x.List + } + return nil +} + +type isBoolValue_Distribution interface { + isBoolValue_Distribution() +} + +type BoolValue_List struct { + List *BoolList `protobuf:"bytes,2,opt,name=list,proto3,oneof"` +} + +func (*BoolValue_List) isBoolValue_Distribution() {} + +type BoolList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of values which can be used. + Options []bool `protobuf:"varint,1,rep,packed,name=options,proto3" json:"options,omitempty"` + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + Random bool `protobuf:"varint,2,opt,name=random,proto3" json:"random,omitempty"` +} + +func (x *BoolList) Reset() { + *x = BoolList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BoolList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BoolList) ProtoMessage() {} + +func (x *BoolList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BoolList.ProtoReflect.Descriptor instead. +func (*BoolList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{18} +} + +func (x *BoolList) GetOptions() []bool { + if x != nil { + return x.Options + } + return nil +} + +func (x *BoolList) GetRandom() bool { + if x != nil { + return x.Random + } + return false +} + +type UintValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If distribution is UintRange, value is used as the initial value + // inside [minimum, maximum] and hold the value as it mutates. + // If distribution is UintList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + // Types that are assignable to Distribution: + // *UintValue_Range + // *UintValue_List + Distribution isUintValue_Distribution `protobuf_oneof:"distribution"` +} + +func (x *UintValue) Reset() { + *x = UintValue{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UintValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UintValue) ProtoMessage() {} + +func (x *UintValue) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UintValue.ProtoReflect.Descriptor instead. +func (*UintValue) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{19} +} + +func (x *UintValue) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +func (m *UintValue) GetDistribution() isUintValue_Distribution { + if m != nil { + return m.Distribution + } + return nil +} + +func (x *UintValue) GetRange() *UintRange { + if x, ok := x.GetDistribution().(*UintValue_Range); ok { + return x.Range + } + return nil +} + +func (x *UintValue) GetList() *UintList { + if x, ok := x.GetDistribution().(*UintValue_List); ok { + return x.List + } + return nil +} + +type isUintValue_Distribution interface { + isUintValue_Distribution() +} + +type UintValue_Range struct { + Range *UintRange `protobuf:"bytes,2,opt,name=range,proto3,oneof"` +} + +type UintValue_List struct { + List *UintList `protobuf:"bytes,3,opt,name=list,proto3,oneof"` +} + +func (*UintValue_Range) isUintValue_Distribution() {} + +func (*UintValue_List) isUintValue_Distribution() {} + +type UintRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The range of the value allowed. + Minimum uint64 `protobuf:"varint,1,opt,name=minimum,proto3" json:"minimum,omitempty"` + Maximum uint64 `protobuf:"varint,2,opt,name=maximum,proto3" json:"maximum,omitempty"` + // If set, the value is cumulative and the subsequent value is value + delta + // where delta is randomly chosen between delta_min and delta_max. The range + // minimum and maximum are still respected and values will saturate at the + // boundaries if they are exceeded. If not set subsequent value is a value + // randomly chosen between minimum and maximum. + DeltaMin int64 `protobuf:"varint,3,opt,name=delta_min,json=deltaMin,proto3" json:"delta_min,omitempty"` + DeltaMax int64 `protobuf:"varint,4,opt,name=delta_max,json=deltaMax,proto3" json:"delta_max,omitempty"` +} + +func (x *UintRange) Reset() { + *x = UintRange{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UintRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UintRange) ProtoMessage() {} + +func (x *UintRange) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UintRange.ProtoReflect.Descriptor instead. +func (*UintRange) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{20} +} + +func (x *UintRange) GetMinimum() uint64 { + if x != nil { + return x.Minimum + } + return 0 +} + +func (x *UintRange) GetMaximum() uint64 { + if x != nil { + return x.Maximum + } + return 0 +} + +func (x *UintRange) GetDeltaMin() int64 { + if x != nil { + return x.DeltaMin + } + return 0 +} + +func (x *UintRange) GetDeltaMax() int64 { + if x != nil { + return x.DeltaMax + } + return 0 +} + +type UintList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of values which can be used. + Options []uint64 `protobuf:"varint,1,rep,packed,name=options,proto3" json:"options,omitempty"` + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + Random bool `protobuf:"varint,2,opt,name=random,proto3" json:"random,omitempty"` +} + +func (x *UintList) Reset() { + *x = UintList{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UintList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UintList) ProtoMessage() {} + +func (x *UintList) ProtoReflect() protoreflect.Message { + mi := &file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UintList.ProtoReflect.Descriptor instead. +func (*UintList) Descriptor() ([]byte, []int) { + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP(), []int{21} +} + +func (x *UintList) GetOptions() []uint64 { + if x != nil { + return x.Options + } + return nil +} + +func (x *UintList) GetRandom() bool { + if x != nil { + return x.Random + } + return false +} + +var File_github_com_openconfig_gnmi_testing_fake_proto_fake_proto protoreflect.FileDescriptor + +var file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDesc = []byte{ + 0x0a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x45, + 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x9b, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x04, + 0x73, 0x65, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, + 0x73, 0x65, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, + 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, + 0x6e, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x3d, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x65, 0x6f, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x6f, 0x66, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x73, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x63, + 0x65, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x65, + 0x6c, 0x61, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00, 0x52, 0x06, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x12, 0x34, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, + 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, + 0x6b, 0x65, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x48, 0x00, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x12, 0x31, 0x0a, 0x05, + 0x66, 0x69, 0x78, 0x65, 0x64, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6e, + 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x66, 0x69, 0x78, 0x65, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x64, 0x64, 0x72, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x72, 0x74, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x72, 0x74, 0x22, + 0x45, 0x0a, 0x0a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, + 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x55, 0x42, 0x42, + 0x59, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x52, 0x50, 0x43, 0x5f, 0x47, 0x4e, 0x4d, 0x49, + 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x47, 0x52, 0x50, 0x43, 0x5f, 0x47, 0x4e, 0x4d, 0x49, 0x5f, + 0x50, 0x52, 0x4f, 0x44, 0x10, 0x03, 0x42, 0x0b, 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x22, 0x47, 0x0a, 0x0e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x0f, + 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, + 0x65, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x0d, 0x0a, + 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb2, 0x04, 0x0a, + 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, + 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, + 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x09, 0x69, 0x6e, + 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, + 0x0a, 0x0c, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x65, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, + 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, + 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x66, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, + 0x18, 0x67, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x30, + 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x68, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x35, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x69, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, + 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x35, 0x0a, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6e, + 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x48, 0x00, 0x52, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x48, + 0x0a, 0x11, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, + 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x22, 0x63, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, + 0x74, 0x61, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, + 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x22, 0x87, 0x01, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, + 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x49, 0x6e, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, + 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, + 0x2e, 0x49, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, + 0x42, 0x0e, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x78, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, + 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x12, 0x1b, 0x0a, + 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x22, 0x3b, 0x0a, 0x07, 0x49, 0x6e, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x90, 0x01, 0x0a, 0x0b, 0x44, 0x6f, 0x75, 0x62, + 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x0a, + 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, + 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2b, 0x0a, + 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x6e, + 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x64, 0x69, + 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7b, 0x0a, 0x0b, 0x44, 0x6f, + 0x75, 0x62, 0x6c, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, + 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, + 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1b, 0x0a, + 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x08, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, + 0x6c, 0x74, 0x61, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x64, + 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x22, 0x3e, 0x0a, 0x0a, 0x44, 0x6f, 0x75, 0x62, 0x6c, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x01, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x60, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2b, 0x0a, 0x04, + 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, + 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x64, 0x69, 0x73, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x0a, 0x0a, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x64, 0x0a, 0x0f, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, + 0x0e, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x5c, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x42, 0x6f, 0x6f, + 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x0e, 0x0a, + 0x0c, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3c, 0x0a, + 0x08, 0x42, 0x6f, 0x6f, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x08, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x8a, 0x01, 0x0a, 0x09, + 0x55, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x2c, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x0a, + 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6e, + 0x6d, 0x69, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, + 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x79, 0x0a, 0x09, 0x55, 0x69, 0x6e, 0x74, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, + 0x74, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, + 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, + 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x74, 0x61, + 0x4d, 0x61, 0x78, 0x22, 0x3c, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, + 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x6e, + 0x64, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x2a, 0x2b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, + 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x49, 0x54, 0x10, + 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x32, 0x9b, + 0x01, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, + 0x2b, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, + 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x06, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, + 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x06, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x66, 0x61, + 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x11, 0x2e, 0x67, 0x6e, 0x6d, 0x69, + 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x39, 0x5a, 0x37, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x67, 0x6e, + 0x6d, 0x69, 0x5f, 0x66, 0x61, 0x6b, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescOnce sync.Once + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescData = file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDesc +) + +func file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescGZIP() []byte { + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescOnce.Do(func() { + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescData) + }) + return file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDescData +} + +var file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_goTypes = []interface{}{ + (State)(0), // 0: gnmi.fake.State + (Config_ClientType)(0), // 1: gnmi.fake.Config.ClientType + (*Configuration)(nil), // 2: gnmi.fake.Configuration + (*Credentials)(nil), // 3: gnmi.fake.Credentials + (*Config)(nil), // 4: gnmi.fake.Config + (*FixedGenerator)(nil), // 5: gnmi.fake.FixedGenerator + (*RandomGenerator)(nil), // 6: gnmi.fake.RandomGenerator + (*DeleteValue)(nil), // 7: gnmi.fake.DeleteValue + (*Value)(nil), // 8: gnmi.fake.Value + (*Timestamp)(nil), // 9: gnmi.fake.Timestamp + (*IntValue)(nil), // 10: gnmi.fake.IntValue + (*IntRange)(nil), // 11: gnmi.fake.IntRange + (*IntList)(nil), // 12: gnmi.fake.IntList + (*DoubleValue)(nil), // 13: gnmi.fake.DoubleValue + (*DoubleRange)(nil), // 14: gnmi.fake.DoubleRange + (*DoubleList)(nil), // 15: gnmi.fake.DoubleList + (*StringValue)(nil), // 16: gnmi.fake.StringValue + (*StringList)(nil), // 17: gnmi.fake.StringList + (*StringListValue)(nil), // 18: gnmi.fake.StringListValue + (*BoolValue)(nil), // 19: gnmi.fake.BoolValue + (*BoolList)(nil), // 20: gnmi.fake.BoolList + (*UintValue)(nil), // 21: gnmi.fake.UintValue + (*UintRange)(nil), // 22: gnmi.fake.UintRange + (*UintList)(nil), // 23: gnmi.fake.UintList + (*any.Any)(nil), // 24: google.protobuf.Any + (*gnmi.SubscribeResponse)(nil), // 25: gnmi.SubscribeResponse +} +var file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_depIdxs = []int32{ + 4, // 0: gnmi.fake.Configuration.config:type_name -> gnmi.fake.Config + 8, // 1: gnmi.fake.Config.values:type_name -> gnmi.fake.Value + 1, // 2: gnmi.fake.Config.client_type:type_name -> gnmi.fake.Config.ClientType + 3, // 3: gnmi.fake.Config.credentials:type_name -> gnmi.fake.Credentials + 24, // 4: gnmi.fake.Config.custom:type_name -> google.protobuf.Any + 6, // 5: gnmi.fake.Config.random:type_name -> gnmi.fake.RandomGenerator + 5, // 6: gnmi.fake.Config.fixed:type_name -> gnmi.fake.FixedGenerator + 25, // 7: gnmi.fake.FixedGenerator.responses:type_name -> gnmi.SubscribeResponse + 8, // 8: gnmi.fake.RandomGenerator.values:type_name -> gnmi.fake.Value + 9, // 9: gnmi.fake.Value.timestamp:type_name -> gnmi.fake.Timestamp + 10, // 10: gnmi.fake.Value.int_value:type_name -> gnmi.fake.IntValue + 13, // 11: gnmi.fake.Value.double_value:type_name -> gnmi.fake.DoubleValue + 16, // 12: gnmi.fake.Value.string_value:type_name -> gnmi.fake.StringValue + 7, // 13: gnmi.fake.Value.delete:type_name -> gnmi.fake.DeleteValue + 19, // 14: gnmi.fake.Value.bool_value:type_name -> gnmi.fake.BoolValue + 21, // 15: gnmi.fake.Value.uint_value:type_name -> gnmi.fake.UintValue + 18, // 16: gnmi.fake.Value.string_list_value:type_name -> gnmi.fake.StringListValue + 11, // 17: gnmi.fake.IntValue.range:type_name -> gnmi.fake.IntRange + 12, // 18: gnmi.fake.IntValue.list:type_name -> gnmi.fake.IntList + 14, // 19: gnmi.fake.DoubleValue.range:type_name -> gnmi.fake.DoubleRange + 15, // 20: gnmi.fake.DoubleValue.list:type_name -> gnmi.fake.DoubleList + 17, // 21: gnmi.fake.StringValue.list:type_name -> gnmi.fake.StringList + 17, // 22: gnmi.fake.StringListValue.list:type_name -> gnmi.fake.StringList + 20, // 23: gnmi.fake.BoolValue.list:type_name -> gnmi.fake.BoolList + 22, // 24: gnmi.fake.UintValue.range:type_name -> gnmi.fake.UintRange + 23, // 25: gnmi.fake.UintValue.list:type_name -> gnmi.fake.UintList + 4, // 26: gnmi.fake.AgentManager.Add:input_type -> gnmi.fake.Config + 4, // 27: gnmi.fake.AgentManager.Remove:input_type -> gnmi.fake.Config + 4, // 28: gnmi.fake.AgentManager.Status:input_type -> gnmi.fake.Config + 4, // 29: gnmi.fake.AgentManager.Add:output_type -> gnmi.fake.Config + 4, // 30: gnmi.fake.AgentManager.Remove:output_type -> gnmi.fake.Config + 4, // 31: gnmi.fake.AgentManager.Status:output_type -> gnmi.fake.Config + 29, // [29:32] is the sub-list for method output_type + 26, // [26:29] is the sub-list for method input_type + 26, // [26:26] is the sub-list for extension type_name + 26, // [26:26] is the sub-list for extension extendee + 0, // [0:26] is the sub-list for field type_name +} + +func init() { file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_init() } +func file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_init() { + if File_github_com_openconfig_gnmi_testing_fake_proto_fake_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Credentials); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FixedGenerator); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RandomGenerator); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Timestamp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IntValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IntRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IntList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DoubleValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DoubleRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DoubleList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringListValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BoolValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BoolList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UintValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UintRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UintList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Config_Custom)(nil), + (*Config_Random)(nil), + (*Config_Fixed)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[6].OneofWrappers = []interface{}{ + (*Value_IntValue)(nil), + (*Value_DoubleValue)(nil), + (*Value_StringValue)(nil), + (*Value_Sync)(nil), + (*Value_Delete)(nil), + (*Value_BoolValue)(nil), + (*Value_UintValue)(nil), + (*Value_StringListValue)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[8].OneofWrappers = []interface{}{ + (*IntValue_Range)(nil), + (*IntValue_List)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[11].OneofWrappers = []interface{}{ + (*DoubleValue_Range)(nil), + (*DoubleValue_List)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[14].OneofWrappers = []interface{}{ + (*StringValue_List)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[16].OneofWrappers = []interface{}{ + (*StringListValue_List)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[17].OneofWrappers = []interface{}{ + (*BoolValue_List)(nil), + } + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes[19].OneofWrappers = []interface{}{ + (*UintValue_Range)(nil), + (*UintValue_List)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDesc, + NumEnums: 2, + NumMessages: 22, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_goTypes, + DependencyIndexes: file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_depIdxs, + EnumInfos: file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_enumTypes, + MessageInfos: file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_msgTypes, + }.Build() + File_github_com_openconfig_gnmi_testing_fake_proto_fake_proto = out.File + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_rawDesc = nil + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_goTypes = nil + file_github_com_openconfig_gnmi_testing_fake_proto_fake_proto_depIdxs = nil +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/proto/fake.proto b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake.proto new file mode 100644 index 0000000000000000000000000000000000000000..00e6b11b5cd049e4ca56c44fb185489faa37396f --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake.proto @@ -0,0 +1,295 @@ +// fake.proto describes the message format for creating integration tests for +// streaming telemetry components by generating a reproducible stream of +// updates from fake targets. +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "github.com/openconfig/gnmi/proto/gnmi/gnmi.proto"; + +package gnmi.fake; + +option go_package = "github.com/openconfig/gnmi/testing/fake/proto;gnmi_fake"; + +service AgentManager { + // Add adds an agent to the server. + rpc Add(Config) returns (Config); + // Remove removes an agent from the server. + rpc Remove(Config) returns (Config); + // Status returns the current status of an agent on the server. + rpc Status(Config) returns (Config); +} + +enum State { + STOPPED = 0; + INIT = 1; + RUNNING = 2; +} + +// Configuration is used to store all agent configuration for the fake agent +// server. Each config describes a single agent hosted on the server. +message Configuration { + // Repeated list of targets to emulate. + repeated Config config = 1; +} + +message Credentials { + string username = 1; + string password = 2; +} + +// Config is a collection of values that together represent the update streams +// for one or more fake devices. +message Config { + enum ClientType { + GRPC = 0; + STUBBY = 1; + GRPC_GNMI = 2; + GRPC_GNMI_PROD = 3; + } + // The target for which the fake will publish values for. + string target = 1; + // Port for the agent to listen on. If 0 or unset the agent will pick a port + // for this agent. + int32 port = 2; + // A global random seed used in generating subsequent values. Set to have + // reproducible results. + int64 seed = 6 [deprecated = true]; + // The list of values generated. Each value will contain its corresponding + // target as the first string in the event.GetValue().path meaning that it is + // possible to generate streams that will be rejected by the cache for testing + // purposes. + repeated Value values = 3 [deprecated = true]; + // Setting disable sync will keep the configured client from autogenerating a + // sync message. This allows negative testing on sync handling. + bool disable_sync = 4; + // Type of client to fake either Stubby or GRPC based fake. + ClientType client_type = 5; + // Disable EOF will hold open the subscription and not automagically close + // the stream once the value queue is empty. + bool disable_eof = 7; + // Per RPC credentials for the agent. If not set no per RPC auth will be used. + Credentials credentials = 8; + // TLS cert for use on the agent. If not set the transport will not be TLS. + bytes cert = 9; + // Honor the delay between events in the generated value streams. Default will + // play events as fast as the can be streamed. + bool enable_delay = 10; + // Generator for value series for the target. + oneof generator { + google.protobuf.Any custom = 100; + RandomGenerator random = 101; + FixedGenerator fixed = 102; + } + // tunnel_addr is the address of the tunnel server. + string tunnel_addr = 11; + // tunnel_crt is the certificate file for the tunnel conection. + string tunnel_crt = 12; +} + +message FixedGenerator { + repeated gnmi.SubscribeResponse responses = 1; +} + +message RandomGenerator { + int64 seed = 1; + repeated Value values = 2; +} + +// Delete will cause the value to be deleted at the Value's path. +message DeleteValue {} + +// Value is the main message that will trigger a stream of updates for a given +// path. A file containing a list of values can be used to simulate a network +// device for integration tests. +message Value { + // The device specific, or OpenConfig path corresponding to a value. + repeated string path = 1; + // The initial timestamp and configuration on how the timestamp will change + // for subsequent values. If timestamp is not set the default will assume to + // be the current system time. + Timestamp timestamp = 2; + // If set, repeat indicates that the value should be repeated this many times, + // otherwise it is repeated indefinitely. + int32 repeat = 6; + // A local random seed used in generating subsequent values for this path. If + // not set, will share the global random source with seed defined in Config. + int64 seed = 7; + // The initial value of the chosen type including configuration on how the + // value will mutate for subsequent updates. + oneof value { + IntValue int_value = 100; + DoubleValue double_value = 101; + StringValue string_value = 102; + uint64 sync = 103; + DeleteValue delete = 104; + BoolValue bool_value = 105; + UintValue uint_value = 106; + StringListValue string_list_value = 107; + } +} + +message Timestamp { + // Initial timestamp for the corresponding value, nanoseconds since epoch. + // This value need have no relation to absolute real-time as the stream of + // of updates is generated without regard to the real clock and can be run + // repeatably at any time if the seed is set in the corresponding Value. + int64 timestamp = 1; + // These values will vary the change in the timestamp for subsequent outputs + // by a value between delta_min and delta_max. Set to the same value to force + // a set periodic interval. + int64 delta_min = 2; + int64 delta_max = 3; +} + +message IntValue { + // If distribution is IntRange, value is used as the initial value + // inside [minimum, maximum] and hold the value as it mutates. + // If distribution is IntList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + int64 value = 1; + oneof distribution { + IntRange range = 2; + IntList list = 3; + } +} + +message IntRange { + // The range of the value allowed. + int64 minimum = 1; + int64 maximum = 2; + // If set, the value is cumulative and the subsequent value is value + delta + // where delta is randomly chosen between delta_min and delta_max. The range + // minimum and maximum are still respected and values will saturate at the + // boundaries if they are exceeded. If not set subsequent value is a value + // randomly chosen between minimum and maximum. + int64 delta_min = 3; + int64 delta_max = 4; +} + +message IntList { + // The set of values which can be used. + repeated int64 options = 1; + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + bool random = 2; +} + +message DoubleValue { + // If distribution is DoubleRange, value is used as the initial value + // inside [minimum, maximum] and hold the value as it mutates. + // If distribution is DoubleList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + double value = 1; + oneof distribution { + DoubleRange range = 2; + DoubleList list = 3; + } +} + +message DoubleRange { + // The range of the value allowed. + double minimum = 1; + double maximum = 2; + // If set, the value is cumulative and the subsequent value is value + delta + // where delta is randomly chosen between delta_min and delta_max. The range + // minimum and maximum are still respected. If not set subsequent value is a + // value randomly chosen between minimum and maximum. + double delta_min = 3; + double delta_max = 4; +} + +message DoubleList { + // The set of values which can be used. + repeated double options = 1; + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order. + bool random = 2; +} + +message StringValue { + // If distribution is StringList, value is used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + string value = 1; + oneof distribution { + StringList list = 2; + } +} + +message StringList { + // The set of strings which can be used. + repeated string options = 1; + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + bool random = 2; +} + +message StringListValue { + // If distribution is StringList, value is used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + repeated string value = 1; + oneof distribution { + StringList list = 2; + } +} + +message BoolValue { + // If distribution is BoolList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + bool value = 1; + oneof distribution { + BoolList list = 2; + } +} + +message BoolList { + // The set of values which can be used. + repeated bool options = 1; + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + bool random = 2; +} + +message UintValue { + // If distribution is UintRange, value is used as the initial value + // inside [minimum, maximum] and hold the value as it mutates. + // If distribution is UintList, value is only used to hold the value as it + // mutates. + // If no distribution is set, value is used as it mutates, i.e. constant + // update. + uint64 value = 1; + oneof distribution { + UintRange range = 2; + UintList list = 3; + } +} + +message UintRange { + // The range of the value allowed. + uint64 minimum = 1; + uint64 maximum = 2; + // If set, the value is cumulative and the subsequent value is value + delta + // where delta is randomly chosen between delta_min and delta_max. The range + // minimum and maximum are still respected and values will saturate at the + // boundaries if they are exceeded. If not set subsequent value is a value + // randomly chosen between minimum and maximum. + int64 delta_min = 3; + int64 delta_max = 4; +} + +message UintList { + // The set of values which can be used. + repeated uint64 options = 1; + // Set to true to randomize selection of value from options. If false, the + // values are cycled in order, starting at index 0. + bool random = 2; +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_grpc.pb.go b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_grpc.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..9cb1e043952e4a8548ee72f6da8f2ad3e916ceef --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_grpc.pb.go @@ -0,0 +1,177 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package gnmi_fake + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// AgentManagerClient is the client API for AgentManager service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type AgentManagerClient interface { + // Add adds an agent to the server. + Add(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) + // Remove removes an agent from the server. + Remove(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) + // Status returns the current status of an agent on the server. + Status(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) +} + +type agentManagerClient struct { + cc grpc.ClientConnInterface +} + +func NewAgentManagerClient(cc grpc.ClientConnInterface) AgentManagerClient { + return &agentManagerClient{cc} +} + +func (c *agentManagerClient) Add(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) { + out := new(Config) + err := c.cc.Invoke(ctx, "/gnmi.fake.AgentManager/Add", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *agentManagerClient) Remove(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) { + out := new(Config) + err := c.cc.Invoke(ctx, "/gnmi.fake.AgentManager/Remove", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *agentManagerClient) Status(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) { + out := new(Config) + err := c.cc.Invoke(ctx, "/gnmi.fake.AgentManager/Status", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AgentManagerServer is the server API for AgentManager service. +// All implementations should embed UnimplementedAgentManagerServer +// for forward compatibility +type AgentManagerServer interface { + // Add adds an agent to the server. + Add(context.Context, *Config) (*Config, error) + // Remove removes an agent from the server. + Remove(context.Context, *Config) (*Config, error) + // Status returns the current status of an agent on the server. + Status(context.Context, *Config) (*Config, error) +} + +// UnimplementedAgentManagerServer should be embedded to have forward compatible implementations. +type UnimplementedAgentManagerServer struct { +} + +func (UnimplementedAgentManagerServer) Add(context.Context, *Config) (*Config, error) { + return nil, status.Errorf(codes.Unimplemented, "method Add not implemented") +} +func (UnimplementedAgentManagerServer) Remove(context.Context, *Config) (*Config, error) { + return nil, status.Errorf(codes.Unimplemented, "method Remove not implemented") +} +func (UnimplementedAgentManagerServer) Status(context.Context, *Config) (*Config, error) { + return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") +} + +// UnsafeAgentManagerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AgentManagerServer will +// result in compilation errors. +type UnsafeAgentManagerServer interface { + mustEmbedUnimplementedAgentManagerServer() +} + +func RegisterAgentManagerServer(s grpc.ServiceRegistrar, srv AgentManagerServer) { + s.RegisterService(&AgentManager_ServiceDesc, srv) +} + +func _AgentManager_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Config) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AgentManagerServer).Add(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.fake.AgentManager/Add", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AgentManagerServer).Add(ctx, req.(*Config)) + } + return interceptor(ctx, in, info, handler) +} + +func _AgentManager_Remove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Config) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AgentManagerServer).Remove(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.fake.AgentManager/Remove", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AgentManagerServer).Remove(ctx, req.(*Config)) + } + return interceptor(ctx, in, info, handler) +} + +func _AgentManager_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Config) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AgentManagerServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gnmi.fake.AgentManager/Status", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AgentManagerServer).Status(ctx, req.(*Config)) + } + return interceptor(ctx, in, info, handler) +} + +// AgentManager_ServiceDesc is the grpc.ServiceDesc for AgentManager service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AgentManager_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gnmi.fake.AgentManager", + HandlerType: (*AgentManagerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Add", + Handler: _AgentManager_Add_Handler, + }, + { + MethodName: "Remove", + Handler: _AgentManager_Remove_Handler, + }, + { + MethodName: "Status", + Handler: _AgentManager_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/openconfig/gnmi/testing/fake/proto/fake.proto", +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_pb2.py b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..72be7e031addb94b190d81e36af456746ff5fd4a --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_pb2.py @@ -0,0 +1,1518 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: testing/fake/proto/fake.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from github.com.openconfig.gnmi.proto.gnmi import gnmi_pb2 as github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi_dot_gnmi__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='testing/fake/proto/fake.proto', + package='gnmi.fake', + syntax='proto3', + serialized_options=b'Z7github.com/openconfig/gnmi/testing/fake/proto;gnmi_fake', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x1dtesting/fake/proto/fake.proto\x12\tgnmi.fake\x1a\x19google/protobuf/any.proto\x1a\x30github.com/openconfig/gnmi/proto/gnmi/gnmi.proto\"2\n\rConfiguration\x12!\n\x06\x63onfig\x18\x01 \x03(\x0b\x32\x11.gnmi.fake.Config\"1\n\x0b\x43redentials\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\"\x8c\x04\n\x06\x43onfig\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\x12\x10\n\x04seed\x18\x06 \x01(\x03\x42\x02\x18\x01\x12$\n\x06values\x18\x03 \x03(\x0b\x32\x10.gnmi.fake.ValueB\x02\x18\x01\x12\x14\n\x0c\x64isable_sync\x18\x04 \x01(\x08\x12\x31\n\x0b\x63lient_type\x18\x05 \x01(\x0e\x32\x1c.gnmi.fake.Config.ClientType\x12\x13\n\x0b\x64isable_eof\x18\x07 \x01(\x08\x12+\n\x0b\x63redentials\x18\x08 \x01(\x0b\x32\x16.gnmi.fake.Credentials\x12\x0c\n\x04\x63\x65rt\x18\t \x01(\x0c\x12\x14\n\x0c\x65nable_delay\x18\n \x01(\x08\x12&\n\x06\x63ustom\x18\x64 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x12,\n\x06random\x18\x65 \x01(\x0b\x32\x1a.gnmi.fake.RandomGeneratorH\x00\x12*\n\x05\x66ixed\x18\x66 \x01(\x0b\x32\x19.gnmi.fake.FixedGeneratorH\x00\x12\x13\n\x0btunnel_addr\x18\x0b \x01(\t\x12\x12\n\ntunnel_crt\x18\x0c \x01(\t\"E\n\nClientType\x12\x08\n\x04GRPC\x10\x00\x12\n\n\x06STUBBY\x10\x01\x12\r\n\tGRPC_GNMI\x10\x02\x12\x12\n\x0eGRPC_GNMI_PROD\x10\x03\x42\x0b\n\tgenerator\"<\n\x0e\x46ixedGenerator\x12*\n\tresponses\x18\x01 \x03(\x0b\x32\x17.gnmi.SubscribeResponse\"A\n\x0fRandomGenerator\x12\x0c\n\x04seed\x18\x01 \x01(\x03\x12 \n\x06values\x18\x02 \x03(\x0b\x32\x10.gnmi.fake.Value\"\r\n\x0b\x44\x65leteValue\"\xba\x03\n\x05Value\x12\x0c\n\x04path\x18\x01 \x03(\t\x12\'\n\ttimestamp\x18\x02 \x01(\x0b\x32\x14.gnmi.fake.Timestamp\x12\x0e\n\x06repeat\x18\x06 \x01(\x05\x12\x0c\n\x04seed\x18\x07 \x01(\x03\x12(\n\tint_value\x18\x64 \x01(\x0b\x32\x13.gnmi.fake.IntValueH\x00\x12.\n\x0c\x64ouble_value\x18\x65 \x01(\x0b\x32\x16.gnmi.fake.DoubleValueH\x00\x12.\n\x0cstring_value\x18\x66 \x01(\x0b\x32\x16.gnmi.fake.StringValueH\x00\x12\x0e\n\x04sync\x18g \x01(\x04H\x00\x12(\n\x06\x64\x65lete\x18h \x01(\x0b\x32\x16.gnmi.fake.DeleteValueH\x00\x12*\n\nbool_value\x18i \x01(\x0b\x32\x14.gnmi.fake.BoolValueH\x00\x12*\n\nuint_value\x18j \x01(\x0b\x32\x14.gnmi.fake.UintValueH\x00\x12\x37\n\x11string_list_value\x18k \x01(\x0b\x32\x1a.gnmi.fake.StringListValueH\x00\x42\x07\n\x05value\"D\n\tTimestamp\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x11\n\tdelta_min\x18\x02 \x01(\x03\x12\x11\n\tdelta_max\x18\x03 \x01(\x03\"s\n\x08IntValue\x12\r\n\x05value\x18\x01 \x01(\x03\x12$\n\x05range\x18\x02 \x01(\x0b\x32\x13.gnmi.fake.IntRangeH\x00\x12\"\n\x04list\x18\x03 \x01(\x0b\x32\x12.gnmi.fake.IntListH\x00\x42\x0e\n\x0c\x64istribution\"R\n\x08IntRange\x12\x0f\n\x07minimum\x18\x01 \x01(\x03\x12\x0f\n\x07maximum\x18\x02 \x01(\x03\x12\x11\n\tdelta_min\x18\x03 \x01(\x03\x12\x11\n\tdelta_max\x18\x04 \x01(\x03\"*\n\x07IntList\x12\x0f\n\x07options\x18\x01 \x03(\x03\x12\x0e\n\x06random\x18\x02 \x01(\x08\"|\n\x0b\x44oubleValue\x12\r\n\x05value\x18\x01 \x01(\x01\x12\'\n\x05range\x18\x02 \x01(\x0b\x32\x16.gnmi.fake.DoubleRangeH\x00\x12%\n\x04list\x18\x03 \x01(\x0b\x32\x15.gnmi.fake.DoubleListH\x00\x42\x0e\n\x0c\x64istribution\"U\n\x0b\x44oubleRange\x12\x0f\n\x07minimum\x18\x01 \x01(\x01\x12\x0f\n\x07maximum\x18\x02 \x01(\x01\x12\x11\n\tdelta_min\x18\x03 \x01(\x01\x12\x11\n\tdelta_max\x18\x04 \x01(\x01\"-\n\nDoubleList\x12\x0f\n\x07options\x18\x01 \x03(\x01\x12\x0e\n\x06random\x18\x02 \x01(\x08\"S\n\x0bStringValue\x12\r\n\x05value\x18\x01 \x01(\t\x12%\n\x04list\x18\x02 \x01(\x0b\x32\x15.gnmi.fake.StringListH\x00\x42\x0e\n\x0c\x64istribution\"-\n\nStringList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x0e\n\x06random\x18\x02 \x01(\x08\"W\n\x0fStringListValue\x12\r\n\x05value\x18\x01 \x03(\t\x12%\n\x04list\x18\x02 \x01(\x0b\x32\x15.gnmi.fake.StringListH\x00\x42\x0e\n\x0c\x64istribution\"O\n\tBoolValue\x12\r\n\x05value\x18\x01 \x01(\x08\x12#\n\x04list\x18\x02 \x01(\x0b\x32\x13.gnmi.fake.BoolListH\x00\x42\x0e\n\x0c\x64istribution\"+\n\x08\x42oolList\x12\x0f\n\x07options\x18\x01 \x03(\x08\x12\x0e\n\x06random\x18\x02 \x01(\x08\"v\n\tUintValue\x12\r\n\x05value\x18\x01 \x01(\x04\x12%\n\x05range\x18\x02 \x01(\x0b\x32\x14.gnmi.fake.UintRangeH\x00\x12#\n\x04list\x18\x03 \x01(\x0b\x32\x13.gnmi.fake.UintListH\x00\x42\x0e\n\x0c\x64istribution\"S\n\tUintRange\x12\x0f\n\x07minimum\x18\x01 \x01(\x04\x12\x0f\n\x07maximum\x18\x02 \x01(\x04\x12\x11\n\tdelta_min\x18\x03 \x01(\x03\x12\x11\n\tdelta_max\x18\x04 \x01(\x03\"+\n\x08UintList\x12\x0f\n\x07options\x18\x01 \x03(\x04\x12\x0e\n\x06random\x18\x02 \x01(\x08*+\n\x05State\x12\x0b\n\x07STOPPED\x10\x00\x12\x08\n\x04INIT\x10\x01\x12\x0b\n\x07RUNNING\x10\x02\x32\x9b\x01\n\x0c\x41gentManager\x12+\n\x03\x41\x64\x64\x12\x11.gnmi.fake.Config\x1a\x11.gnmi.fake.Config\x12.\n\x06Remove\x12\x11.gnmi.fake.Config\x1a\x11.gnmi.fake.Config\x12.\n\x06Status\x12\x11.gnmi.fake.Config\x1a\x11.gnmi.fake.ConfigB9Z7github.com/openconfig/gnmi/testing/fake/proto;gnmi_fakeb\x06proto3' + , + dependencies=[google_dot_protobuf_dot_any__pb2.DESCRIPTOR,github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi_dot_gnmi__pb2.DESCRIPTOR,]) + +_STATE = _descriptor.EnumDescriptor( + name='State', + full_name='gnmi.fake.State', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='STOPPED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='INIT', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RUNNING', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2512, + serialized_end=2555, +) +_sym_db.RegisterEnumDescriptor(_STATE) + +State = enum_type_wrapper.EnumTypeWrapper(_STATE) +STOPPED = 0 +INIT = 1 +RUNNING = 2 + + +_CONFIG_CLIENTTYPE = _descriptor.EnumDescriptor( + name='ClientType', + full_name='gnmi.fake.Config.ClientType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='GRPC', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STUBBY', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='GRPC_GNMI', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='GRPC_GNMI_PROD', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=667, + serialized_end=736, +) +_sym_db.RegisterEnumDescriptor(_CONFIG_CLIENTTYPE) + + +_CONFIGURATION = _descriptor.Descriptor( + name='Configuration', + full_name='gnmi.fake.Configuration', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config', full_name='gnmi.fake.Configuration.config', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=121, + serialized_end=171, +) + + +_CREDENTIALS = _descriptor.Descriptor( + name='Credentials', + full_name='gnmi.fake.Credentials', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='username', full_name='gnmi.fake.Credentials.username', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='password', full_name='gnmi.fake.Credentials.password', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=173, + serialized_end=222, +) + + +_CONFIG = _descriptor.Descriptor( + name='Config', + full_name='gnmi.fake.Config', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='target', full_name='gnmi.fake.Config.target', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='port', full_name='gnmi.fake.Config.port', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='seed', full_name='gnmi.fake.Config.seed', index=2, + number=6, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='values', full_name='gnmi.fake.Config.values', index=3, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disable_sync', full_name='gnmi.fake.Config.disable_sync', index=4, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_type', full_name='gnmi.fake.Config.client_type', index=5, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disable_eof', full_name='gnmi.fake.Config.disable_eof', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='credentials', full_name='gnmi.fake.Config.credentials', index=7, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cert', full_name='gnmi.fake.Config.cert', index=8, + number=9, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enable_delay', full_name='gnmi.fake.Config.enable_delay', index=9, + number=10, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='custom', full_name='gnmi.fake.Config.custom', index=10, + number=100, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='random', full_name='gnmi.fake.Config.random', index=11, + number=101, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fixed', full_name='gnmi.fake.Config.fixed', index=12, + number=102, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='tunnel_addr', full_name='gnmi.fake.Config.tunnel_addr', index=13, + number=11, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='tunnel_crt', full_name='gnmi.fake.Config.tunnel_crt', index=14, + number=12, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _CONFIG_CLIENTTYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='generator', full_name='gnmi.fake.Config.generator', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=225, + serialized_end=749, +) + + +_FIXEDGENERATOR = _descriptor.Descriptor( + name='FixedGenerator', + full_name='gnmi.fake.FixedGenerator', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='responses', full_name='gnmi.fake.FixedGenerator.responses', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=751, + serialized_end=811, +) + + +_RANDOMGENERATOR = _descriptor.Descriptor( + name='RandomGenerator', + full_name='gnmi.fake.RandomGenerator', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='seed', full_name='gnmi.fake.RandomGenerator.seed', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='values', full_name='gnmi.fake.RandomGenerator.values', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=813, + serialized_end=878, +) + + +_DELETEVALUE = _descriptor.Descriptor( + name='DeleteValue', + full_name='gnmi.fake.DeleteValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=880, + serialized_end=893, +) + + +_VALUE = _descriptor.Descriptor( + name='Value', + full_name='gnmi.fake.Value', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='gnmi.fake.Value.path', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='timestamp', full_name='gnmi.fake.Value.timestamp', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='repeat', full_name='gnmi.fake.Value.repeat', index=2, + number=6, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='seed', full_name='gnmi.fake.Value.seed', index=3, + number=7, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='int_value', full_name='gnmi.fake.Value.int_value', index=4, + number=100, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='double_value', full_name='gnmi.fake.Value.double_value', index=5, + number=101, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='string_value', full_name='gnmi.fake.Value.string_value', index=6, + number=102, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sync', full_name='gnmi.fake.Value.sync', index=7, + number=103, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delete', full_name='gnmi.fake.Value.delete', index=8, + number=104, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='bool_value', full_name='gnmi.fake.Value.bool_value', index=9, + number=105, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uint_value', full_name='gnmi.fake.Value.uint_value', index=10, + number=106, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='string_list_value', full_name='gnmi.fake.Value.string_list_value', index=11, + number=107, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='value', full_name='gnmi.fake.Value.value', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=896, + serialized_end=1338, +) + + +_TIMESTAMP = _descriptor.Descriptor( + name='Timestamp', + full_name='gnmi.fake.Timestamp', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', full_name='gnmi.fake.Timestamp.timestamp', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_min', full_name='gnmi.fake.Timestamp.delta_min', index=1, + number=2, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_max', full_name='gnmi.fake.Timestamp.delta_max', index=2, + number=3, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1340, + serialized_end=1408, +) + + +_INTVALUE = _descriptor.Descriptor( + name='IntValue', + full_name='gnmi.fake.IntValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.fake.IntValue.value', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='range', full_name='gnmi.fake.IntValue.range', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list', full_name='gnmi.fake.IntValue.list', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='distribution', full_name='gnmi.fake.IntValue.distribution', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1410, + serialized_end=1525, +) + + +_INTRANGE = _descriptor.Descriptor( + name='IntRange', + full_name='gnmi.fake.IntRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='minimum', full_name='gnmi.fake.IntRange.minimum', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='maximum', full_name='gnmi.fake.IntRange.maximum', index=1, + number=2, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_min', full_name='gnmi.fake.IntRange.delta_min', index=2, + number=3, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_max', full_name='gnmi.fake.IntRange.delta_max', index=3, + number=4, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1527, + serialized_end=1609, +) + + +_INTLIST = _descriptor.Descriptor( + name='IntList', + full_name='gnmi.fake.IntList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='options', full_name='gnmi.fake.IntList.options', index=0, + number=1, type=3, cpp_type=2, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='random', full_name='gnmi.fake.IntList.random', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1611, + serialized_end=1653, +) + + +_DOUBLEVALUE = _descriptor.Descriptor( + name='DoubleValue', + full_name='gnmi.fake.DoubleValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.fake.DoubleValue.value', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='range', full_name='gnmi.fake.DoubleValue.range', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list', full_name='gnmi.fake.DoubleValue.list', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='distribution', full_name='gnmi.fake.DoubleValue.distribution', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1655, + serialized_end=1779, +) + + +_DOUBLERANGE = _descriptor.Descriptor( + name='DoubleRange', + full_name='gnmi.fake.DoubleRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='minimum', full_name='gnmi.fake.DoubleRange.minimum', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='maximum', full_name='gnmi.fake.DoubleRange.maximum', index=1, + number=2, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_min', full_name='gnmi.fake.DoubleRange.delta_min', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_max', full_name='gnmi.fake.DoubleRange.delta_max', index=3, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1781, + serialized_end=1866, +) + + +_DOUBLELIST = _descriptor.Descriptor( + name='DoubleList', + full_name='gnmi.fake.DoubleList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='options', full_name='gnmi.fake.DoubleList.options', index=0, + number=1, type=1, cpp_type=5, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='random', full_name='gnmi.fake.DoubleList.random', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1868, + serialized_end=1913, +) + + +_STRINGVALUE = _descriptor.Descriptor( + name='StringValue', + full_name='gnmi.fake.StringValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.fake.StringValue.value', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list', full_name='gnmi.fake.StringValue.list', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='distribution', full_name='gnmi.fake.StringValue.distribution', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1915, + serialized_end=1998, +) + + +_STRINGLIST = _descriptor.Descriptor( + name='StringList', + full_name='gnmi.fake.StringList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='options', full_name='gnmi.fake.StringList.options', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='random', full_name='gnmi.fake.StringList.random', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2000, + serialized_end=2045, +) + + +_STRINGLISTVALUE = _descriptor.Descriptor( + name='StringListValue', + full_name='gnmi.fake.StringListValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.fake.StringListValue.value', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list', full_name='gnmi.fake.StringListValue.list', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='distribution', full_name='gnmi.fake.StringListValue.distribution', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=2047, + serialized_end=2134, +) + + +_BOOLVALUE = _descriptor.Descriptor( + name='BoolValue', + full_name='gnmi.fake.BoolValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.fake.BoolValue.value', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list', full_name='gnmi.fake.BoolValue.list', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='distribution', full_name='gnmi.fake.BoolValue.distribution', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=2136, + serialized_end=2215, +) + + +_BOOLLIST = _descriptor.Descriptor( + name='BoolList', + full_name='gnmi.fake.BoolList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='options', full_name='gnmi.fake.BoolList.options', index=0, + number=1, type=8, cpp_type=7, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='random', full_name='gnmi.fake.BoolList.random', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2217, + serialized_end=2260, +) + + +_UINTVALUE = _descriptor.Descriptor( + name='UintValue', + full_name='gnmi.fake.UintValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='value', full_name='gnmi.fake.UintValue.value', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='range', full_name='gnmi.fake.UintValue.range', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list', full_name='gnmi.fake.UintValue.list', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='distribution', full_name='gnmi.fake.UintValue.distribution', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=2262, + serialized_end=2380, +) + + +_UINTRANGE = _descriptor.Descriptor( + name='UintRange', + full_name='gnmi.fake.UintRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='minimum', full_name='gnmi.fake.UintRange.minimum', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='maximum', full_name='gnmi.fake.UintRange.maximum', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_min', full_name='gnmi.fake.UintRange.delta_min', index=2, + number=3, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='delta_max', full_name='gnmi.fake.UintRange.delta_max', index=3, + number=4, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2382, + serialized_end=2465, +) + + +_UINTLIST = _descriptor.Descriptor( + name='UintList', + full_name='gnmi.fake.UintList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='options', full_name='gnmi.fake.UintList.options', index=0, + number=1, type=4, cpp_type=4, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='random', full_name='gnmi.fake.UintList.random', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2467, + serialized_end=2510, +) + +_CONFIGURATION.fields_by_name['config'].message_type = _CONFIG +_CONFIG.fields_by_name['values'].message_type = _VALUE +_CONFIG.fields_by_name['client_type'].enum_type = _CONFIG_CLIENTTYPE +_CONFIG.fields_by_name['credentials'].message_type = _CREDENTIALS +_CONFIG.fields_by_name['custom'].message_type = google_dot_protobuf_dot_any__pb2._ANY +_CONFIG.fields_by_name['random'].message_type = _RANDOMGENERATOR +_CONFIG.fields_by_name['fixed'].message_type = _FIXEDGENERATOR +_CONFIG_CLIENTTYPE.containing_type = _CONFIG +_CONFIG.oneofs_by_name['generator'].fields.append( + _CONFIG.fields_by_name['custom']) +_CONFIG.fields_by_name['custom'].containing_oneof = _CONFIG.oneofs_by_name['generator'] +_CONFIG.oneofs_by_name['generator'].fields.append( + _CONFIG.fields_by_name['random']) +_CONFIG.fields_by_name['random'].containing_oneof = _CONFIG.oneofs_by_name['generator'] +_CONFIG.oneofs_by_name['generator'].fields.append( + _CONFIG.fields_by_name['fixed']) +_CONFIG.fields_by_name['fixed'].containing_oneof = _CONFIG.oneofs_by_name['generator'] +_FIXEDGENERATOR.fields_by_name['responses'].message_type = github_dot_com_dot_openconfig_dot_gnmi_dot_proto_dot_gnmi_dot_gnmi__pb2._SUBSCRIBERESPONSE +_RANDOMGENERATOR.fields_by_name['values'].message_type = _VALUE +_VALUE.fields_by_name['timestamp'].message_type = _TIMESTAMP +_VALUE.fields_by_name['int_value'].message_type = _INTVALUE +_VALUE.fields_by_name['double_value'].message_type = _DOUBLEVALUE +_VALUE.fields_by_name['string_value'].message_type = _STRINGVALUE +_VALUE.fields_by_name['delete'].message_type = _DELETEVALUE +_VALUE.fields_by_name['bool_value'].message_type = _BOOLVALUE +_VALUE.fields_by_name['uint_value'].message_type = _UINTVALUE +_VALUE.fields_by_name['string_list_value'].message_type = _STRINGLISTVALUE +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['int_value']) +_VALUE.fields_by_name['int_value'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['double_value']) +_VALUE.fields_by_name['double_value'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['string_value']) +_VALUE.fields_by_name['string_value'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['sync']) +_VALUE.fields_by_name['sync'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['delete']) +_VALUE.fields_by_name['delete'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['bool_value']) +_VALUE.fields_by_name['bool_value'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['uint_value']) +_VALUE.fields_by_name['uint_value'].containing_oneof = _VALUE.oneofs_by_name['value'] +_VALUE.oneofs_by_name['value'].fields.append( + _VALUE.fields_by_name['string_list_value']) +_VALUE.fields_by_name['string_list_value'].containing_oneof = _VALUE.oneofs_by_name['value'] +_INTVALUE.fields_by_name['range'].message_type = _INTRANGE +_INTVALUE.fields_by_name['list'].message_type = _INTLIST +_INTVALUE.oneofs_by_name['distribution'].fields.append( + _INTVALUE.fields_by_name['range']) +_INTVALUE.fields_by_name['range'].containing_oneof = _INTVALUE.oneofs_by_name['distribution'] +_INTVALUE.oneofs_by_name['distribution'].fields.append( + _INTVALUE.fields_by_name['list']) +_INTVALUE.fields_by_name['list'].containing_oneof = _INTVALUE.oneofs_by_name['distribution'] +_DOUBLEVALUE.fields_by_name['range'].message_type = _DOUBLERANGE +_DOUBLEVALUE.fields_by_name['list'].message_type = _DOUBLELIST +_DOUBLEVALUE.oneofs_by_name['distribution'].fields.append( + _DOUBLEVALUE.fields_by_name['range']) +_DOUBLEVALUE.fields_by_name['range'].containing_oneof = _DOUBLEVALUE.oneofs_by_name['distribution'] +_DOUBLEVALUE.oneofs_by_name['distribution'].fields.append( + _DOUBLEVALUE.fields_by_name['list']) +_DOUBLEVALUE.fields_by_name['list'].containing_oneof = _DOUBLEVALUE.oneofs_by_name['distribution'] +_STRINGVALUE.fields_by_name['list'].message_type = _STRINGLIST +_STRINGVALUE.oneofs_by_name['distribution'].fields.append( + _STRINGVALUE.fields_by_name['list']) +_STRINGVALUE.fields_by_name['list'].containing_oneof = _STRINGVALUE.oneofs_by_name['distribution'] +_STRINGLISTVALUE.fields_by_name['list'].message_type = _STRINGLIST +_STRINGLISTVALUE.oneofs_by_name['distribution'].fields.append( + _STRINGLISTVALUE.fields_by_name['list']) +_STRINGLISTVALUE.fields_by_name['list'].containing_oneof = _STRINGLISTVALUE.oneofs_by_name['distribution'] +_BOOLVALUE.fields_by_name['list'].message_type = _BOOLLIST +_BOOLVALUE.oneofs_by_name['distribution'].fields.append( + _BOOLVALUE.fields_by_name['list']) +_BOOLVALUE.fields_by_name['list'].containing_oneof = _BOOLVALUE.oneofs_by_name['distribution'] +_UINTVALUE.fields_by_name['range'].message_type = _UINTRANGE +_UINTVALUE.fields_by_name['list'].message_type = _UINTLIST +_UINTVALUE.oneofs_by_name['distribution'].fields.append( + _UINTVALUE.fields_by_name['range']) +_UINTVALUE.fields_by_name['range'].containing_oneof = _UINTVALUE.oneofs_by_name['distribution'] +_UINTVALUE.oneofs_by_name['distribution'].fields.append( + _UINTVALUE.fields_by_name['list']) +_UINTVALUE.fields_by_name['list'].containing_oneof = _UINTVALUE.oneofs_by_name['distribution'] +DESCRIPTOR.message_types_by_name['Configuration'] = _CONFIGURATION +DESCRIPTOR.message_types_by_name['Credentials'] = _CREDENTIALS +DESCRIPTOR.message_types_by_name['Config'] = _CONFIG +DESCRIPTOR.message_types_by_name['FixedGenerator'] = _FIXEDGENERATOR +DESCRIPTOR.message_types_by_name['RandomGenerator'] = _RANDOMGENERATOR +DESCRIPTOR.message_types_by_name['DeleteValue'] = _DELETEVALUE +DESCRIPTOR.message_types_by_name['Value'] = _VALUE +DESCRIPTOR.message_types_by_name['Timestamp'] = _TIMESTAMP +DESCRIPTOR.message_types_by_name['IntValue'] = _INTVALUE +DESCRIPTOR.message_types_by_name['IntRange'] = _INTRANGE +DESCRIPTOR.message_types_by_name['IntList'] = _INTLIST +DESCRIPTOR.message_types_by_name['DoubleValue'] = _DOUBLEVALUE +DESCRIPTOR.message_types_by_name['DoubleRange'] = _DOUBLERANGE +DESCRIPTOR.message_types_by_name['DoubleList'] = _DOUBLELIST +DESCRIPTOR.message_types_by_name['StringValue'] = _STRINGVALUE +DESCRIPTOR.message_types_by_name['StringList'] = _STRINGLIST +DESCRIPTOR.message_types_by_name['StringListValue'] = _STRINGLISTVALUE +DESCRIPTOR.message_types_by_name['BoolValue'] = _BOOLVALUE +DESCRIPTOR.message_types_by_name['BoolList'] = _BOOLLIST +DESCRIPTOR.message_types_by_name['UintValue'] = _UINTVALUE +DESCRIPTOR.message_types_by_name['UintRange'] = _UINTRANGE +DESCRIPTOR.message_types_by_name['UintList'] = _UINTLIST +DESCRIPTOR.enum_types_by_name['State'] = _STATE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Configuration = _reflection.GeneratedProtocolMessageType('Configuration', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGURATION, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.Configuration) + }) +_sym_db.RegisterMessage(Configuration) + +Credentials = _reflection.GeneratedProtocolMessageType('Credentials', (_message.Message,), { + 'DESCRIPTOR' : _CREDENTIALS, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.Credentials) + }) +_sym_db.RegisterMessage(Credentials) + +Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), { + 'DESCRIPTOR' : _CONFIG, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.Config) + }) +_sym_db.RegisterMessage(Config) + +FixedGenerator = _reflection.GeneratedProtocolMessageType('FixedGenerator', (_message.Message,), { + 'DESCRIPTOR' : _FIXEDGENERATOR, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.FixedGenerator) + }) +_sym_db.RegisterMessage(FixedGenerator) + +RandomGenerator = _reflection.GeneratedProtocolMessageType('RandomGenerator', (_message.Message,), { + 'DESCRIPTOR' : _RANDOMGENERATOR, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.RandomGenerator) + }) +_sym_db.RegisterMessage(RandomGenerator) + +DeleteValue = _reflection.GeneratedProtocolMessageType('DeleteValue', (_message.Message,), { + 'DESCRIPTOR' : _DELETEVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.DeleteValue) + }) +_sym_db.RegisterMessage(DeleteValue) + +Value = _reflection.GeneratedProtocolMessageType('Value', (_message.Message,), { + 'DESCRIPTOR' : _VALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.Value) + }) +_sym_db.RegisterMessage(Value) + +Timestamp = _reflection.GeneratedProtocolMessageType('Timestamp', (_message.Message,), { + 'DESCRIPTOR' : _TIMESTAMP, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.Timestamp) + }) +_sym_db.RegisterMessage(Timestamp) + +IntValue = _reflection.GeneratedProtocolMessageType('IntValue', (_message.Message,), { + 'DESCRIPTOR' : _INTVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.IntValue) + }) +_sym_db.RegisterMessage(IntValue) + +IntRange = _reflection.GeneratedProtocolMessageType('IntRange', (_message.Message,), { + 'DESCRIPTOR' : _INTRANGE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.IntRange) + }) +_sym_db.RegisterMessage(IntRange) + +IntList = _reflection.GeneratedProtocolMessageType('IntList', (_message.Message,), { + 'DESCRIPTOR' : _INTLIST, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.IntList) + }) +_sym_db.RegisterMessage(IntList) + +DoubleValue = _reflection.GeneratedProtocolMessageType('DoubleValue', (_message.Message,), { + 'DESCRIPTOR' : _DOUBLEVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.DoubleValue) + }) +_sym_db.RegisterMessage(DoubleValue) + +DoubleRange = _reflection.GeneratedProtocolMessageType('DoubleRange', (_message.Message,), { + 'DESCRIPTOR' : _DOUBLERANGE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.DoubleRange) + }) +_sym_db.RegisterMessage(DoubleRange) + +DoubleList = _reflection.GeneratedProtocolMessageType('DoubleList', (_message.Message,), { + 'DESCRIPTOR' : _DOUBLELIST, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.DoubleList) + }) +_sym_db.RegisterMessage(DoubleList) + +StringValue = _reflection.GeneratedProtocolMessageType('StringValue', (_message.Message,), { + 'DESCRIPTOR' : _STRINGVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.StringValue) + }) +_sym_db.RegisterMessage(StringValue) + +StringList = _reflection.GeneratedProtocolMessageType('StringList', (_message.Message,), { + 'DESCRIPTOR' : _STRINGLIST, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.StringList) + }) +_sym_db.RegisterMessage(StringList) + +StringListValue = _reflection.GeneratedProtocolMessageType('StringListValue', (_message.Message,), { + 'DESCRIPTOR' : _STRINGLISTVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.StringListValue) + }) +_sym_db.RegisterMessage(StringListValue) + +BoolValue = _reflection.GeneratedProtocolMessageType('BoolValue', (_message.Message,), { + 'DESCRIPTOR' : _BOOLVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.BoolValue) + }) +_sym_db.RegisterMessage(BoolValue) + +BoolList = _reflection.GeneratedProtocolMessageType('BoolList', (_message.Message,), { + 'DESCRIPTOR' : _BOOLLIST, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.BoolList) + }) +_sym_db.RegisterMessage(BoolList) + +UintValue = _reflection.GeneratedProtocolMessageType('UintValue', (_message.Message,), { + 'DESCRIPTOR' : _UINTVALUE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.UintValue) + }) +_sym_db.RegisterMessage(UintValue) + +UintRange = _reflection.GeneratedProtocolMessageType('UintRange', (_message.Message,), { + 'DESCRIPTOR' : _UINTRANGE, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.UintRange) + }) +_sym_db.RegisterMessage(UintRange) + +UintList = _reflection.GeneratedProtocolMessageType('UintList', (_message.Message,), { + 'DESCRIPTOR' : _UINTLIST, + '__module__' : 'testing.fake.proto.fake_pb2' + # @@protoc_insertion_point(class_scope:gnmi.fake.UintList) + }) +_sym_db.RegisterMessage(UintList) + + +DESCRIPTOR._options = None +_CONFIG.fields_by_name['seed']._options = None +_CONFIG.fields_by_name['values']._options = None + +_AGENTMANAGER = _descriptor.ServiceDescriptor( + name='AgentManager', + full_name='gnmi.fake.AgentManager', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=2558, + serialized_end=2713, + methods=[ + _descriptor.MethodDescriptor( + name='Add', + full_name='gnmi.fake.AgentManager.Add', + index=0, + containing_service=None, + input_type=_CONFIG, + output_type=_CONFIG, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='Remove', + full_name='gnmi.fake.AgentManager.Remove', + index=1, + containing_service=None, + input_type=_CONFIG, + output_type=_CONFIG, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='Status', + full_name='gnmi.fake.AgentManager.Status', + index=2, + containing_service=None, + input_type=_CONFIG, + output_type=_CONFIG, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_AGENTMANAGER) + +DESCRIPTOR.services_by_name['AgentManager'] = _AGENTMANAGER + +# @@protoc_insertion_point(module_scope) diff --git a/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_pb2_grpc.py b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_pb2_grpc.py new file mode 100644 index 0000000000000000000000000000000000000000..d42c5eec02aabe15089d9c30a89f2b2802cd5087 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/proto/fake_pb2_grpc.py @@ -0,0 +1,135 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from testing.fake.proto import fake_pb2 as testing_dot_fake_dot_proto_dot_fake__pb2 + + +class AgentManagerStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Add = channel.unary_unary( + '/gnmi.fake.AgentManager/Add', + request_serializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + response_deserializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + ) + self.Remove = channel.unary_unary( + '/gnmi.fake.AgentManager/Remove', + request_serializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + response_deserializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + ) + self.Status = channel.unary_unary( + '/gnmi.fake.AgentManager/Status', + request_serializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + response_deserializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + ) + + +class AgentManagerServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Add(self, request, context): + """Add adds an agent to the server. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Remove(self, request, context): + """Remove removes an agent from the server. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Status(self, request, context): + """Status returns the current status of an agent on the server. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_AgentManagerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Add': grpc.unary_unary_rpc_method_handler( + servicer.Add, + request_deserializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + response_serializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + ), + 'Remove': grpc.unary_unary_rpc_method_handler( + servicer.Remove, + request_deserializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + response_serializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + ), + 'Status': grpc.unary_unary_rpc_method_handler( + servicer.Status, + request_deserializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + response_serializer=testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'gnmi.fake.AgentManager', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class AgentManager(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Add(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.fake.AgentManager/Add', + testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Remove(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.fake.AgentManager/Remove', + testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Status(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/gnmi.fake.AgentManager/Status', + testing_dot_fake_dot_proto_dot_fake__pb2.Config.SerializeToString, + testing_dot_fake_dot_proto_dot_fake__pb2.Config.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/deps/github.com/openconfig/gnmi/testing/fake/queue/fixed_queue.go b/deps/github.com/openconfig/gnmi/testing/fake/queue/fixed_queue.go new file mode 100644 index 0000000000000000000000000000000000000000..0b3ae06e4faeaf6f7d35adc01a9aba23c453ddd9 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/queue/fixed_queue.go @@ -0,0 +1,86 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package queue + +import ( + "sync" + "time" + + gpb "github.com/openconfig/gnmi/proto/gnmi" +) + +// FixedQueue provides a strict delivery of provides updates. If checkDelay, +// the Next() will sleep for the duration between the timestamps provided in the +// updates. +type FixedQueue struct { + mu sync.Mutex + resp []*gpb.SubscribeResponse + delay time.Duration + checkDelay bool + lastTS int64 +} + +// NewFixed creates a new FixedQueue with resp list of updates enqueued for +// iterating through. +func NewFixed(resp []*gpb.SubscribeResponse, delay bool) *FixedQueue { + return &FixedQueue{ + resp: resp, + checkDelay: delay, + } +} + +// Add will append resp to the current tail of the queue. +func (q *FixedQueue) Add(resp *gpb.SubscribeResponse) { + q.mu.Lock() + defer q.mu.Unlock() + q.resp = append(q.resp, resp) +} + +// Next returns the next update in the queue or an error. If the queue is +// exhausted, a nil is returned for the update. The return will always be a +// *gpb.SubscribeResponse for proper type assertion. +func (q *FixedQueue) Next() (interface{}, error) { + q.mu.Lock() + defer q.mu.Unlock() + if len(q.resp) == 0 { + return nil, nil + } + if q.delay != 0 { + time.Sleep(q.delay) + } + resp := q.resp[0] + q.resp = q.resp[1:] + var n *gpb.SubscribeResponse_Update + if len(q.resp) > 0 && q.checkDelay { + var nOk bool + n, nOk = resp.Response.(*gpb.SubscribeResponse_Update) + if nOk && n.Update.Timestamp > q.lastTS { + q.lastTS = n.Update.Timestamp + } + + next, nextOk := q.resp[0].Response.(*gpb.SubscribeResponse_Update) + if !nextOk { + q.delay = 0 + } else { + q.delay = time.Duration(next.Update.Timestamp-q.lastTS) * time.Nanosecond + if q.delay < 0 { + q.delay = 0 + } + } + } + return resp, nil +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/queue/queue.go b/deps/github.com/openconfig/gnmi/testing/fake/queue/queue.go new file mode 100644 index 0000000000000000000000000000000000000000..673a768694e2047d7eda2f9e1039f56a58bcaefe --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/queue/queue.go @@ -0,0 +1,502 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package queue implements an update queue for use in testing a telemetry +// stream. +package queue + +import ( + "fmt" + "math/rand" + "sync" + "time" + + "github.com/golang/protobuf/proto" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +// Queue is a generic interface for getting the next element from either a +// FixedQueue or UpdateQueue. +type Queue interface { + Next() (interface{}, error) +} + +// UpdateQueue is a structure that orders a set of device updates by their +// timestamps and repeatedly generates new pseudo-random updates based on +// a set of device path configurations. +type UpdateQueue struct { + mu sync.Mutex + delay bool + duration time.Duration + latest int64 + q [][]*value + r *rand.Rand +} + +// New creates a new UpdateQueue. If delay is true, a call to Next() +// will invoke a sleep based on the duration between timestamps of the last +// returned update and the update to be returned by the current call. The +// values are the configuration for the updates stored in the queue. +func New(delay bool, seed int64, values []*fpb.Value) *UpdateQueue { + if seed == 0 { + seed = time.Now().UnixNano() + } + u := &UpdateQueue{delay: delay, r: rand.New(rand.NewSource(seed))} + for _, v := range values { + u.addValue(newValue(v, u.r)) + } + return u +} + +// Add inserts v into the queue in correct timestamp order. +func (u *UpdateQueue) Add(v *fpb.Value) { + u.mu.Lock() + defer u.mu.Unlock() + u.addValue(newValue(v, u.r)) +} + +// Latest returns the maximum timestamp in the queue. +func (u *UpdateQueue) Latest() int64 { + return u.latest +} + +// Next returns the next update in the queue or an error. If the queue is +// exhausted, a nil is returned for the update. The return will always be a +// *fpb.Value for proper type assertion. +func (u *UpdateQueue) Next() (interface{}, error) { + u.mu.Lock() + defer u.mu.Unlock() + if len(u.q) == 0 { + return nil, nil + } + // Incur a real delay if requested. + if u.duration > 0 { + time.Sleep(u.duration) + u.duration = 0 + } + v := u.q[0][0] + // Make a copy of the current proto message to return. + val := v.v + // Update the value. + if err := v.nextValue(); err != nil { + return nil, err + } + var checkDelay bool + // If this is the last update in the queue move to the next update set. + // else move to the next update in the same update set. + if len(u.q[0]) == 1 { + u.q = u.q[1:] + checkDelay = true + } else { + u.q[0] = u.q[0][1:] + } + // Add the updated value to the queue if the repeats are not exhausted. + if v.v != nil { + u.addValue(v) + } + // Set up a delay for the next retrieval if realtime delays are requested. + if u.delay && checkDelay && len(u.q) > 0 { + u.duration = time.Duration(u.q[0][0].v.Timestamp.Timestamp-val.Timestamp.Timestamp) * time.Nanosecond + } + return val, nil +} + +func (u *UpdateQueue) addValue(v *value) { + // If Timestamp was not provided create a default. + if v.v.Timestamp == nil { + v.v.Timestamp = &fpb.Timestamp{} + } + t := v.v.Timestamp.Timestamp + l, r := 0, len(u.q) + if t > u.latest { + u.latest = t + } + // Binary search for sorted placement in queue. + for { + if l == r { + // Insert a new list of updates for a new timestamp. + u.q = append(u.q[:r], append([][]*value{{v}}, u.q[r:]...)...) + return + } + i := (r-l)/2 + l + t2 := u.q[i][0].v.Timestamp.Timestamp + switch { + case t == t2: + // Append update to a list for an existing timestamp. + u.q[i] = append(u.q[i], v) + return + case t < t2: + r = i + case t > t2: + l = i + 1 + } + } +} + +type value struct { + v *fpb.Value // The configuration for a stream of updates for a single value. + r *rand.Rand // The PRNG used to generate subsequent updates for this value. +} + +func (v value) String() string { + return v.v.String() +} + +func newValue(v *fpb.Value, r *rand.Rand) *value { + if v.Seed == 0 { + return &value{v: v, r: r} + } + return &value{v: v, r: rand.New(rand.NewSource(v.Seed))} +} + +func (v *value) updateTimestamp() error { + if v.v.Timestamp == nil { + return fmt.Errorf("timestamp not set for %q", v.v) + } + t := v.v.Timestamp.Timestamp + if t < 0 { + return fmt.Errorf("timestamp must be positive for %q", v.v) + } + min, max := v.v.Timestamp.DeltaMin, v.v.Timestamp.DeltaMax + if min > max || min < 0 { + return fmt.Errorf("invalid delta_min/delta_max on timestamp for %q", v.v) + } + v.v.Timestamp.Timestamp = t + v.r.Int63n(max-min+1) + min + return nil +} + +func (v *value) updateIntValue() error { + val := v.v.GetIntValue() + if val == nil { + return fmt.Errorf("invalid IntValue for %q", v.v) + } + var newval int64 + switch val.Distribution.(type) { + case *fpb.IntValue_Range: + rng := val.GetRange() + if rng.Minimum > rng.Maximum { + return fmt.Errorf("invalid minimum/maximum in IntRange for %q", v.v) + } + if val.Value < rng.Minimum || val.Value > rng.Maximum { + return fmt.Errorf("value not in [minimum, maximum] in IntRange for %q", v.v) + } + + left, right := rng.GetMinimum(), rng.GetMaximum() + if rng.DeltaMin != 0 || rng.DeltaMax != 0 { + if rng.DeltaMin > rng.DeltaMax { + return fmt.Errorf("invalid delta_min/delta_max in IntRange for %q", v.v) + } + left, right = rng.GetDeltaMin(), rng.GetDeltaMax() + newval = val.Value + } + + newval += v.r.Int63n(right-left+1) + left + if newval > rng.Maximum { + newval = rng.Maximum + } + if newval < rng.Minimum { + newval = rng.Minimum + } + case *fpb.IntValue_List: + list := val.GetList() + options := list.GetOptions() + if len(options) == 0 { + return fmt.Errorf("missing options on IntValue_List for %q", v.v) + } + if list.GetRandom() { + newval = options[v.r.Intn(len(options))] + } else { + newval = options[0] + list.Options = append(options[1:], options[0]) + } + default: + newval = val.Value + } + val.Value = newval + return nil +} + +func (v *value) updateDoubleValue() error { + val := v.v.GetDoubleValue() + if val == nil { + return fmt.Errorf("invalid DoubleValue for %q", v.v) + } + var newval float64 + switch val.Distribution.(type) { + case *fpb.DoubleValue_Range: + rng := val.GetRange() + if rng.Minimum > rng.Maximum { + return fmt.Errorf("invalid minimum/maximum on DoubleValue_Range for %q", v.v) + } + if val.Value < rng.Minimum || val.Value > rng.Maximum { + return fmt.Errorf("value not in [minimum, maximum] on DoubleValue_Range for %q", v.v) + } + + left, right := rng.GetMinimum(), rng.GetMaximum() + if rng.DeltaMin != 0 || rng.DeltaMax != 0 { + if rng.DeltaMin > rng.DeltaMax { + return fmt.Errorf("invalid delta_min/delta_max on DoubleValue_Range for %q", v.v) + } + left, right = rng.GetDeltaMin(), rng.GetDeltaMax() + newval = val.Value + } + + newval += v.r.Float64()*(right-left) + left + if newval > rng.Maximum { + newval = rng.Maximum + } + if newval < rng.Minimum { + newval = rng.Minimum + } + case *fpb.DoubleValue_List: + list := val.GetList() + options := list.GetOptions() + if len(options) == 0 { + return fmt.Errorf("missing options on DoubleValue_List for %q", v.v) + } + if list.GetRandom() { + newval = options[v.r.Intn(len(options))] + } else { + newval = options[0] + list.Options = append(options[1:], options[0]) + } + default: + newval = val.Value + } + val.Value = newval + return nil +} + +func (v *value) updateStringValue() error { + val := v.v.GetStringValue() + if val == nil { + return fmt.Errorf("invalid StringValue for %q", v.v) + } + var newval string + switch val.Distribution.(type) { + case *fpb.StringValue_List: + list := val.GetList() + options := list.Options + if len(options) == 0 { + return fmt.Errorf("missing options on StringValue_List for %q", v.v) + } + if list.Random { + newval = options[v.r.Intn(len(options))] + } else { + newval = options[0] + list.Options = append(options[1:], options[0]) + } + default: + newval = val.Value + } + val.Value = newval + return nil +} + +func (v *value) updateStringListValue() error { + val := v.v.GetStringListValue() + if val == nil { + return fmt.Errorf("invalid StringListValue for %q", v.v) + } + newval := val.Value + switch val.Distribution.(type) { + case *fpb.StringListValue_List: + list := val.GetList() + options := list.Options + if len(options) == 0 { + return fmt.Errorf("missing options on StringListValue_List for %q", v.v) + } + if list.Random { + v.r.Shuffle(len(options), func(i, j int) { options[i], options[j] = options[j], options[i] }) + newval = options[:v.r.Intn(len(options))] + } else { + list.Options = append(options[1:], options[0]) + newval = list.Options + } + } + val.Value = newval + return nil +} + +func (v *value) updateBoolValue() error { + val := v.v.GetBoolValue() + if val == nil { + return fmt.Errorf("invalid BoolValue for %q", v.v) + } + var newval bool + switch val.Distribution.(type) { + case *fpb.BoolValue_List: + list := val.GetList() + options := list.Options + if len(options) == 0 { + return fmt.Errorf("missing options on BoolValue_List for %q", v.v) + } + if list.Random { + newval = options[v.r.Intn(len(options))] + } else { + newval = options[0] + list.Options = append(options[1:], options[0]) + } + default: + newval = val.Value + } + val.Value = newval + return nil +} + +func (v *value) updateUintValue() error { + val := v.v.GetUintValue() + if val == nil { + return fmt.Errorf("invalid UintValue for %q", v.v) + } + var newval uint64 + switch val.Distribution.(type) { + case *fpb.UintValue_Range: + rng := val.GetRange() + if rng.Minimum > rng.Maximum { + return fmt.Errorf("invalid minimum/maximum in UintRange for %q", v.v) + } + if val.Value < rng.Minimum || val.Value > rng.Maximum { + return fmt.Errorf("value not in [minimum, maximum] in UintRange for %q", v.v) + } + + left, right := int64(rng.GetMinimum()), int64(rng.GetMaximum()) + if rng.DeltaMin != 0 || rng.DeltaMax != 0 { + if rng.DeltaMin > rng.DeltaMax { + return fmt.Errorf("invalid delta_min/delta_max in UintRange for %q", v.v) + } + left, right = rng.GetDeltaMin(), rng.GetDeltaMax() + newval = val.Value + } + tmpVal := int64(newval) + v.r.Int63n(right-left+1) + left + if tmpVal < 0 { + newval = rng.Minimum + } else { + newval = uint64(tmpVal) + } + if newval > rng.Maximum { + newval = rng.Maximum + } + if newval < rng.Minimum { + newval = rng.Minimum + } + case *fpb.UintValue_List: + list := val.GetList() + options := list.GetOptions() + if len(options) == 0 { + return fmt.Errorf("missing options on UintValue_List for %q", v.v) + } + if list.GetRandom() { + newval = options[v.r.Intn(len(options))] + } else { + newval = options[0] + list.Options = append(options[1:], options[0]) + } + default: + newval = val.Value + } + val.Value = newval + return nil +} + +func (v *value) nextValue() error { + if v.v.Repeat == 1 { + // This value has exhausted all of its updates, drop it. + v.v = nil + return nil + } + // Make a new proto message for the next value. + v.v = proto.Clone(v.v).(*fpb.Value) + if v.v.Repeat > 1 { + v.v.Repeat-- + } + if err := v.updateTimestamp(); err != nil { + return err + } + switch v.v.GetValue().(type) { + case *fpb.Value_IntValue: + return v.updateIntValue() + case *fpb.Value_DoubleValue: + return v.updateDoubleValue() + case *fpb.Value_StringValue: + return v.updateStringValue() + case *fpb.Value_StringListValue: + return v.updateStringListValue() + case *fpb.Value_BoolValue: + return v.updateBoolValue() + case *fpb.Value_UintValue: + return v.updateUintValue() + case *fpb.Value_Sync: + return nil + case *fpb.Value_Delete: + return nil + default: + return fmt.Errorf("value type not found in %q", v.v) + } +} + +// ValueOf returns the concrete value of v. +func ValueOf(v *fpb.Value) interface{} { + switch val := v.GetValue().(type) { + case *fpb.Value_IntValue: + return val.IntValue.Value + case *fpb.Value_DoubleValue: + return val.DoubleValue.Value + case *fpb.Value_StringValue: + return val.StringValue.Value + case *fpb.Value_StringListValue: + return val.StringListValue.Value + case *fpb.Value_BoolValue: + return val.BoolValue.Value + case *fpb.Value_UintValue: + return val.UintValue.Value + case *fpb.Value_Sync: + return val.Sync + case *fpb.Value_Delete: + return val.Delete + default: + return nil + } +} + +// TypedValueOf returns the gNMI TypedValue of v. If v is a Sync or Delete, +// TypedValueOf returns nil. +func TypedValueOf(v *fpb.Value) *gpb.TypedValue { + tv := &gpb.TypedValue{} + switch val := v.GetValue().(type) { + case *fpb.Value_IntValue: + tv.Value = &gpb.TypedValue_IntVal{val.IntValue.Value} + case *fpb.Value_DoubleValue: + tv.Value = &gpb.TypedValue_FloatVal{float32(val.DoubleValue.Value)} + case *fpb.Value_StringValue: + tv.Value = &gpb.TypedValue_StringVal{val.StringValue.Value} + case *fpb.Value_StringListValue: + leaflist := &gpb.ScalarArray{} + for _, s := range val.StringListValue.Value { + leaflist.Element = append(leaflist.Element, &gpb.TypedValue{Value: &gpb.TypedValue_StringVal{s}}) + } + tv.Value = &gpb.TypedValue_LeaflistVal{leaflist} + case *fpb.Value_BoolValue: + tv.Value = &gpb.TypedValue_BoolVal{val.BoolValue.Value} + case *fpb.Value_UintValue: + tv.Value = &gpb.TypedValue_UintVal{val.UintValue.Value} + default: + return nil + } + return tv +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/queue/queue_test.go b/deps/github.com/openconfig/gnmi/testing/fake/queue/queue_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d51c0068bd22f5782e3369aac8e028dca5205d1c --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/queue/queue_test.go @@ -0,0 +1,1249 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package queue + +import ( + "math/rand" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/golang/protobuf/proto" + "github.com/openconfig/gnmi/errdiff" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + fpb "github.com/openconfig/gnmi/testing/fake/proto" +) + +var seed = int64(100) + +func TestUpdateTimestamp(t *testing.T) { + tests := []struct { + desc string + in *fpb.Value + want *fpb.Timestamp + err string + }{{ + desc: "No timestamp", + in: &fpb.Value{}, + err: "timestamp not set", + }, { + desc: "Negative timestamp", + in: &fpb.Value{Timestamp: &fpb.Timestamp{Timestamp: -1}}, + err: "timestamp must be positive", + }, { + desc: "Invalid timestamp", + in: &fpb.Value{Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 2, DeltaMax: 1}}, + err: "invalid delta_min/delta_max on timestamp", + }, { + desc: "Valid timestamp", + in: &fpb.Value{ + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}}, + want: &fpb.Timestamp{Timestamp: 1235, DeltaMin: 1, DeltaMax: 1}, + }, { + desc: "Using global seed", + in: &fpb.Value{ + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 10}}, + want: &fpb.Timestamp{Timestamp: 1243, DeltaMin: 1, DeltaMax: 10}, + }, { + desc: "Using local seed", + in: &fpb.Value{ + Seed: 10, + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 10}}, + want: &fpb.Timestamp{Timestamp: 1240, DeltaMin: 1, DeltaMax: 10}}, + } + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.in, rand.New(rand.NewSource(seed))) + err := v.updateTimestamp() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).updateTimestamp() %v", tc.in, diff) + } + if diff := cmp.Diff(v.v.GetTimestamp(), tc.want, cmp.Comparer(proto.Equal)); err == nil && diff != "" { + t.Errorf("newValue(%q).updateTimestamp() %v", tc.in, diff) + } + }) + } +} + +func TestUpdateIntValue(t *testing.T) { + tests := []struct { + desc string + value *fpb.Value + want *fpb.Value + err string + }{{ + desc: "Nil value", + value: &fpb.Value{}, + err: "invalid IntValue", + }, { + desc: "Invalid min/max in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 100, Maximum: 0}}}}}, + err: "invalid minimum/maximum in IntRange", + }, { + desc: "Invalid init value (value < min) in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: -100, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + err: "value not in [minimum, maximum] in IntRange", + }, { + desc: "Invalid init value (value > max) in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 200, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + err: "value not in [minimum, maximum] in IntRange", + }, { + desc: "Invalid delta_min/delta_max in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 5}}}}}, + err: "invalid delta_min/delta_max in IntRange", + }, { + desc: "Non-empty value, non-cumulative in range, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 65, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Non-empty value, non-cumulative in range, using local seed", + value: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 69, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Non-empty value, cumulative in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 60, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 10}}}}}, + }, { + desc: "Non-empty value, cumulative, maximum capped in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 51, DeltaMin: 10, DeltaMax: 10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 51, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 51, DeltaMin: 10, DeltaMax: 10}}}}}, + }, { + desc: "Non-empty value, cumulative, minimum capped in range", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 45, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 45, + Distribution: &fpb.IntValue_Range{ + Range: &fpb.IntRange{Minimum: 45, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + }, { + desc: "no options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{Distribution: &fpb.IntValue_List{ + List: &fpb.IntList{Random: true}}}}}, + err: "missing options on IntValue_List", + }, { + desc: "four options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{Distribution: &fpb.IntValue_List{ + List: &fpb.IntList{Options: []int64{100, 200, 300, 400}, Random: true}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 400, + Distribution: &fpb.IntValue_List{ + List: &fpb.IntList{Options: []int64{100, 200, 300, 400}, Random: true}}}}}, + }, { + desc: "four options, non-random in list", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{Distribution: &fpb.IntValue_List{ + List: &fpb.IntList{Options: []int64{100, 200, 300, 400}, Random: false}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 100, + Distribution: &fpb.IntValue_List{ + List: &fpb.IntList{Options: []int64{200, 300, 400, 100}, Random: false}}}}}, + }, { + desc: "constant", + value: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{Value: 100}}}, + want: &fpb.Value{ + Value: &fpb.Value_IntValue{&fpb.IntValue{Value: 100}}}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.value, rand.New(rand.NewSource(seed))) + err := v.updateIntValue() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).updateIntValue() %v", tc.value, diff) + } + if diff := cmp.Diff(v.v, tc.want, cmp.Comparer(proto.Equal)); err == nil && diff != "" { + t.Errorf("newValue(%q).updatedIntValue() %v", tc.value, diff) + } + }) + } +} + +func TestUpdateDoubleValue(t *testing.T) { + tests := []struct { + desc string + value *fpb.Value + want *fpb.Value + err string + }{{ + desc: "Nil Value", + value: &fpb.Value{}, + err: "invalid DoubleValue", + }, { + desc: "Invalid min/max in range", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 100, Maximum: 0}}}}}, + err: "invalid minimum/maximum on DoubleValue_Range", + }, { + desc: "Invalid init value (value > max) in range", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 200, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100}}}}}, + err: "value not in [minimum, maximum] on DoubleValue_Range", + }, { + desc: "Invalid delta_min/delta_max in range", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 5}}}}}, + err: "invalid delta_min/delta_max on DoubleValue_Range", + }, { + desc: "Non-empty value, non-cumulative in range, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 81.65026937796166, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Non-empty value, non-cumulative in range, using local seed", + value: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 56.60920659323543, + Distribution: &fpb.DoubleValue_Range{ + Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Non-empty value, cumulative in range", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 60, + Distribution: &fpb.DoubleValue_Range{ + Range: &fpb.DoubleRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 10}}}}}, + }, { + desc: "Non-empty value, cumulative, maximum capped in range", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 0, Maximum: 51, DeltaMin: 10, DeltaMax: 10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 51, + Distribution: &fpb.DoubleValue_Range{ + Range: &fpb.DoubleRange{Minimum: 0, Maximum: 51, DeltaMin: 10, DeltaMax: 10}}}}}, + }, { + desc: "Non-empty value, cumulative, minimum capped in range", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 50, + Distribution: &fpb.DoubleValue_Range{Range: &fpb.DoubleRange{Minimum: 45, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 45, + Distribution: &fpb.DoubleValue_Range{ + Range: &fpb.DoubleRange{Minimum: 45, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + }, { + desc: "no options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Distribution: &fpb.DoubleValue_List{ + List: &fpb.DoubleList{Random: true}}}}}, + err: "missing options on DoubleValue_List", + }, { + desc: "four options, random in list, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Distribution: &fpb.DoubleValue_List{ + List: &fpb.DoubleList{Options: []float64{100, 200, 300, 400}, Random: true}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 400, + Distribution: &fpb.DoubleValue_List{ + List: &fpb.DoubleList{Options: []float64{100, 200, 300, 400}, Random: true}}}}}, + }, { + desc: "four options, non-random in list, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Distribution: &fpb.DoubleValue_List{ + List: &fpb.DoubleList{Options: []float64{100, 200, 300, 400}, Random: false}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{ + Value: 100, + Distribution: &fpb.DoubleValue_List{ + List: &fpb.DoubleList{Options: []float64{200, 300, 400, 100}, Random: false}}}}}, + }, { + desc: "constant", + value: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Value: 100}}}, + want: &fpb.Value{ + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Value: 100}}}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.value, rand.New(rand.NewSource(seed))) + err := v.updateDoubleValue() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).updateDoubleValue() %v", tc.value, diff) + } + if err != nil { + return + } + if diff := cmp.Diff(v.v, tc.want, cmp.Comparer(proto.Equal)); diff != "" { + t.Errorf("newValue(%q).updatedDoubleValue() %v", tc.value, diff) + } + }) + } +} + +func TestUpdateBoolValue(t *testing.T) { + tests := []struct { + desc string + value *fpb.Value + want *fpb.Value + err string + }{{ + desc: "Nil Value", + value: &fpb.Value{}, + err: "invalid BoolValue", + }, { + desc: "no options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{Random: true}}}}}, + err: "missing options on BoolValue_List", + }, { + desc: "Four options, random in list, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{Options: []bool{true, true, false, false}, Random: true}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{ + Value: false, + Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{ + Options: []bool{true, true, false, false}, Random: true}}}}}, + }, { + desc: "Four options, random in list, using local seed", + value: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_BoolValue{&fpb.BoolValue{Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{Random: true, Options: []bool{true, true, false, false}}}}}}, + want: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_BoolValue{&fpb.BoolValue{ + Value: false, + Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{Random: true, Options: []bool{true, true, false, false}}}}}}, + }, { + desc: "Four options, non-random in list", + value: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{Random: false, Options: []bool{true, true, false, false}}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{ + Value: true, + Distribution: &fpb.BoolValue_List{ + List: &fpb.BoolList{Random: false, Options: []bool{true, false, false, true}}}}}}, + }, { + desc: "constant", + value: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{Value: true}}}, + want: &fpb.Value{ + Value: &fpb.Value_BoolValue{&fpb.BoolValue{Value: true}}}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.value, rand.New(rand.NewSource(seed))) + err := v.updateBoolValue() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).updateBoolValue() %v", tc.value, diff) + } + if diff := cmp.Diff(v.v, tc.want, cmp.Comparer(proto.Equal)); err == nil && diff != "" { + t.Errorf("newValue(%q).updatedBoolValue() %v", tc.value, diff) + } + }) + } +} + +func TestUpdateStringValue(t *testing.T) { + tests := []struct { + desc string + value *fpb.Value + want *fpb.Value + err string + }{{ + desc: "Nil Value", + value: &fpb.Value{}, + err: "invalid StringValue", + }, { + desc: "no options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{Random: true}}}}}, + err: "missing options on StringValue_List", + }, { + desc: "Four options, random in list, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{Options: []string{"a", "b", "c", "d"}, Random: true}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{ + Value: "d", + Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{ + Options: []string{"a", "b", "c", "d"}, Random: true}}}}}, + }, { + desc: "Four options, random in list, using local seed", + value: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_StringValue{&fpb.StringValue{Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{Random: true, Options: []string{"a", "b", "c", "d"}}}}}}, + want: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_StringValue{&fpb.StringValue{ + Value: "c", + Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{Random: true, Options: []string{"a", "b", "c", "d"}}}}}}, + }, { + desc: "Four options, non-random in list", + value: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{Random: false, Options: []string{"a", "b", "c", "d"}}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{ + Value: "a", + Distribution: &fpb.StringValue_List{ + List: &fpb.StringList{Random: false, Options: []string{"b", "c", "d", "a"}}}}}}, + }, { + desc: "constant", + value: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "a"}}}, + want: &fpb.Value{ + Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "a"}}}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.value, rand.New(rand.NewSource(seed))) + err := v.updateStringValue() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).updateStringValue() %v", tc.value, diff) + } + if diff := cmp.Diff(v.v, tc.want, cmp.Comparer(proto.Equal)); err == nil && diff != "" { + t.Errorf("newValue(%q).updatedStringValue() %v", tc.value, diff) + } + }) + } +} + +func TestUpdateUintValue(t *testing.T) { + tests := []struct { + desc string + value *fpb.Value + want *fpb.Value + err string + }{{ + desc: "Nil value", + value: &fpb.Value{}, + err: "invalid UintValue", + }, { + desc: "Invalid min/max in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 100, Maximum: 0}}}}}, + err: "invalid minimum/maximum in UintRange", + }, { + desc: "Invalid init value (value < min) in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 1, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 10, Maximum: 100}}}}}, + err: "value not in [minimum, maximum] in UintRange", + }, { + desc: "Invalid init value (value > max) in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 200, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100}}}}}, + err: "value not in [minimum, maximum] in UintRange", + }, { + desc: "Invalid delta_min/delta_max in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 5}}}}}, + err: "invalid delta_min/delta_max in UintRange", + }, { + desc: "Non-empty value, non-cumulative in range, using global seed", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 65, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Non-empty value, non-cumulative in range, using local seed", + value: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Seed: 10, + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 69, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Non-empty value, cumulative in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 60, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 100, DeltaMin: 10, DeltaMax: 10}}}}}, + }, { + desc: "Non-empty value, cumulative, maximum capped in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 51, DeltaMin: 10, DeltaMax: 10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 51, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 0, Maximum: 51, DeltaMin: 10, DeltaMax: 10}}}}}, + }, { + desc: "Non-empty value, cumulative, minimum capped in range", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 50, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 45, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 45, + Distribution: &fpb.UintValue_Range{ + Range: &fpb.UintRange{Minimum: 45, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + }, { + desc: "Non-empty value, cumulative, minimum capped due to negative value", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 8, + Distribution: &fpb.UintValue_Range{Range: &fpb.UintRange{Minimum: 5, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 5, + Distribution: &fpb.UintValue_Range{ + Range: &fpb.UintRange{Minimum: 5, Maximum: 60, DeltaMin: -10, DeltaMax: -10}}}}}, + }, { + desc: "no options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{Distribution: &fpb.UintValue_List{ + List: &fpb.UintList{Random: true}}}}}, + err: "missing options on UintValue_List", + }, { + desc: "four options, random in list", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{Distribution: &fpb.UintValue_List{ + List: &fpb.UintList{Options: []uint64{100, 200, 300, 400}, Random: true}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 400, + Distribution: &fpb.UintValue_List{ + List: &fpb.UintList{Options: []uint64{100, 200, 300, 400}, Random: true}}}}}, + }, { + desc: "four options, non-random in list", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{Distribution: &fpb.UintValue_List{ + List: &fpb.UintList{Options: []uint64{100, 200, 300, 400}, Random: false}}}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 100, + Distribution: &fpb.UintValue_List{ + List: &fpb.UintList{Options: []uint64{200, 300, 400, 100}, Random: false}}}}}, + }, { + desc: "constant", + value: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{Value: 100}}}, + want: &fpb.Value{ + Value: &fpb.Value_UintValue{&fpb.UintValue{Value: 100}}}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.value, rand.New(rand.NewSource(seed))) + err := v.updateUintValue() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).updateUintValue() %v", tc.value, diff) + } + if diff := cmp.Diff(v.v, tc.want, cmp.Comparer(proto.Equal)); err == nil && diff != "" { + t.Errorf("newValue(%q).updatedUintValue() %v", tc.value, diff) + } + }) + } +} + +func TestNextValue(t *testing.T) { + tests := []struct { + desc string + in *fpb.Value + want *fpb.Value + err string + }{{ + desc: "Empty value", + in: &fpb.Value{}, + want: &fpb.Value{}, + err: "timestamp not set", + }, { + desc: "Just timestamp", + in: &fpb.Value{Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}}, + want: &fpb.Value{Timestamp: &fpb.Timestamp{Timestamp: 1235, DeltaMin: 1, DeltaMax: 1}}, + err: "value type not found", + }, { + desc: "Indefinite updates", + in: &fpb.Value{ + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + want: &fpb.Value{ + Timestamp: &fpb.Timestamp{Timestamp: 1235, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 80, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{Minimum: 0, Maximum: 100}}}}}, + }, { + desc: "Repeat", + in: &fpb.Value{ + Repeat: 5, + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_IntValue{&fpb.IntValue{Distribution: &fpb.IntValue_List{List: &fpb.IntList{Options: []int64{10, 20, 30}, Random: false}}}}}, + want: &fpb.Value{ + Repeat: 4, + Timestamp: &fpb.Timestamp{Timestamp: 1235, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 10, + Distribution: &fpb.IntValue_List{List: &fpb.IntList{Options: []int64{20, 30, 10}, Random: false}}}}}, + }, { + desc: "Repeat with constant double value", + in: &fpb.Value{ + Repeat: 5, + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Value: 50.1}}, + }, + want: &fpb.Value{ + Repeat: 4, + Timestamp: &fpb.Timestamp{Timestamp: 1235, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Value: 50.1}}, + }, + }, { + desc: "Repeat UintValue", + in: &fpb.Value{ + Repeat: 5, + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_UintValue{&fpb.UintValue{Distribution: &fpb.UintValue_List{List: &fpb.UintList{Options: []uint64{10, 20, 30}, Random: false}}}}}, + want: &fpb.Value{ + Repeat: 4, + Timestamp: &fpb.Timestamp{Timestamp: 1235, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_UintValue{&fpb.UintValue{ + Value: 10, + Distribution: &fpb.UintValue_List{List: &fpb.UintList{Options: []uint64{20, 30, 10}, Random: false}}}}}, + }, { + desc: "Last repeat", + in: &fpb.Value{ + Repeat: 1, + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_IntValue{&fpb.IntValue{Value: 50}}, + }, + }, { + desc: "Last repeat with constant string value", + in: &fpb.Value{ + Repeat: 1, + Timestamp: &fpb.Timestamp{Timestamp: 1234, DeltaMin: 1, DeltaMax: 1}, + Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "a"}}, + }, + }, { + desc: "String value", + in: &fpb.Value{ + Repeat: 2, + Timestamp: &fpb.Timestamp{Timestamp: 1234}, + Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "a"}}, + }, + want: &fpb.Value{ + Repeat: 1, + Timestamp: &fpb.Timestamp{Timestamp: 1234}, + Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "a"}}, + }, + }, { + desc: "Sync value", + in: &fpb.Value{ + Repeat: 2, + Timestamp: &fpb.Timestamp{Timestamp: 1234}, + Value: &fpb.Value_Sync{uint64(1)}, + }, + want: &fpb.Value{ + Repeat: 1, + Timestamp: &fpb.Timestamp{Timestamp: 1234}, + Value: &fpb.Value_Sync{uint64(1)}, + }, + }, { + desc: "Delete value", + in: &fpb.Value{ + Repeat: 2, + Timestamp: &fpb.Timestamp{Timestamp: 1234}, + Value: &fpb.Value_Delete{&fpb.DeleteValue{}}, + }, + want: &fpb.Value{ + Repeat: 1, + Timestamp: &fpb.Timestamp{Timestamp: 1234}, + Value: &fpb.Value_Delete{&fpb.DeleteValue{}}, + }, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + v := newValue(tc.in, rand.New(rand.NewSource(seed))) + err := v.nextValue() + if diff := errdiff.Substring(err, tc.err); diff != "" { + t.Errorf("newValue(%q).nextValue() %v", tc.in, diff) + } + if diff := cmp.Diff(v.v, tc.want, cmp.Comparer(proto.Equal)); err == nil && diff != "" { + t.Errorf("value of newValue(%q).nextValue() %v", tc.in, diff) + } + }) + } +} + +func TestEmptyQueue(t *testing.T) { + q := New(false, seed, nil) + u, err := q.Next() + if err != nil { + t.Fatalf("New(false, nil).Next() unexpected error: got %q, want nil", err) + } + if u != nil { + t.Errorf("New(false, nil).Next() got %v, want nil", u) + } +} + +func TestQueueFiniteUpdates(t *testing.T) { + for _, x := range []int{1, 5, 100} { + in := &fpb.Value{ + Repeat: int32(x), + Timestamp: &fpb.Timestamp{ + Timestamp: 1234, + DeltaMin: 1, + DeltaMax: 1, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + } + q := New(false, seed, []*fpb.Value{in}) + for i := 0; i < x; i++ { + u, err := q.Next() + if err != nil { + continue + } + if u == nil { + t.Errorf("New(false, %q).Next() got nil, expected an update %d of %d", in, i, x) + } + } + // Try one more time to valid nil next value. + u, err := q.Next() + if err != nil { + t.Errorf("New(false, %q).Next() unexpected error: got %q for %d updates", in, err, x) + } + if u != nil { + t.Errorf("New(false, %q).Next() got update %v, want nil for %d updates", in, u, x) + } + } +} + +func TestQueueInfiniteUpdates(t *testing.T) { + in := &fpb.Value{ + Timestamp: &fpb.Timestamp{ + Timestamp: 1234, + DeltaMin: 1, + DeltaMax: 1, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + } + q := New(false, seed, []*fpb.Value{in}) + // Not really infinite testing, but without repeat set as above, should + // continue indefinitely. We check a large definite number as a proxy. + for i := 0; i < 10000; i++ { + u, err := q.Next() + if err != nil { + t.Errorf("New(false, %q).Next() unexpected error: got %q trying to receive update %d from an infinite queue", in, err, i) + continue + } + if u == nil { + t.Errorf("New(false, %q).Next() got nil, want update %d from an infinite queue", in, i) + } + } +} + +func TestQueueDelay(t *testing.T) { + in := &fpb.Value{ + Timestamp: &fpb.Timestamp{ + Timestamp: 1234, + DeltaMin: 250 * time.Millisecond.Nanoseconds(), + DeltaMax: 250 * time.Millisecond.Nanoseconds(), + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + } + q := New(true, seed, []*fpb.Value{in}) + // No delay to get the first value. + if _, err := q.Next(); err != nil { + t.Errorf("New(true, %q).Next() unexpected error: got %q receiving a value from an infinite queue", in, err) + } + b := time.Now() + // Second value should be delayed 250ms. + if _, err := q.Next(); err != nil { + t.Errorf("New(true, %q).Next() unexpected error: got %q receiving a value from an infinite queue", in, err) + } + if e := time.Since(b); e < 250*time.Millisecond { + t.Errorf("New(true, %q).Next() got delayed %dms, want delay >= 250ms", in, e/time.Millisecond) + } +} + +func TestQueueAddValue(t *testing.T) { + in := []*fpb.Value{{ + Repeat: 1, + Timestamp: &fpb.Timestamp{ + Timestamp: 2, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + }, { + Repeat: 1, + Timestamp: &fpb.Timestamp{ + Timestamp: 1, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + }, { + Repeat: 1, + Timestamp: &fpb.Timestamp{ + Timestamp: 3, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 50, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + }, { + Repeat: 1, + Timestamp: &fpb.Timestamp{ + Timestamp: 3, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 60, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + }, { + Repeat: 1, + Timestamp: &fpb.Timestamp{ + Timestamp: 1, + }, + Value: &fpb.Value_IntValue{&fpb.IntValue{ + Value: 60, + Distribution: &fpb.IntValue_Range{Range: &fpb.IntRange{ + Minimum: 0, + Maximum: 100, + }}}}, + }} + q := New(true, seed, in) + if got, want := q.Latest(), int64(3); got != want { + t.Errorf("New(true, %q) unexpected Latest(): got %q, want %q", in, got, want) + } + if len(q.q) != 3 { + t.Errorf("New(true, %q) unexpected value set: got %q, want %q", in, q.q, in) + } + for i, q := range q.q { + t.Logf("queue:%d %q", i, q[0].v) + if q[0].v.Timestamp.Timestamp != int64(i+1) { + t.Errorf("New(true, %q) unexpected value set: got %q, want Timestamp=%d", in, q[0].v, i+1) + } + } + for i := 0; i < len(in); i++ { + if _, err := q.Next(); err != nil { + t.Errorf("New(true, %q).Next() unexpected error: got %q, want nil", in, err) + } + } + u, err := q.Next() + if err != nil { + t.Errorf("New(true, %q).Next() unexpected error: got %q, want nil", in, u) + } + if u != nil { + t.Errorf("New(true, %q).Next() unexpected update: got %q, want nil", in, u) + } +} + +func TestValueOf(t *testing.T) { + tests := []struct { + desc string + in *fpb.Value + want interface{} + }{{ + desc: "string value", + in: &fpb.Value{Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "UP"}}}, + want: "UP", + }, { + desc: "int value", + in: &fpb.Value{Value: &fpb.Value_IntValue{&fpb.IntValue{Value: 100}}}, + want: int64(100), + }, { + desc: "double value", + in: &fpb.Value{Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Value: float64(101)}}}, + want: float64(101), + }, { + desc: "Delete value", + in: &fpb.Value{Value: &fpb.Value_Delete{&fpb.DeleteValue{}}}, + want: &fpb.DeleteValue{}, + }, { + desc: "Sync value", + in: &fpb.Value{Value: &fpb.Value_Sync{uint64(1)}}, + want: uint64(1), + }, { + desc: "Bool value", + in: &fpb.Value{Value: &fpb.Value_BoolValue{&fpb.BoolValue{Value: true}}}, + want: true, + }, { + desc: "Uint value", + in: &fpb.Value{Value: &fpb.Value_UintValue{&fpb.UintValue{Value: 100}}}, + want: uint64(100), + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + if got, want := ValueOf(tc.in), tc.want; !cmp.Equal(got, want, cmp.Comparer(proto.Equal)) { + t.Errorf("ValueOf(%q) failed: got %q, want %q", tc.in, got, want) + } + }) + } +} + +func TestFixedQueue(t *testing.T) { + tests := []struct { + desc string + in []*gpb.SubscribeResponse + delay bool + want *gpb.TypedValue + updates []*gpb.SubscribeResponse + }{{ + desc: "empty notifications", + }, { + desc: "single sync", + in: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, + }}, + }, { + desc: "sync then updates", + in: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 1, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 1}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 2}}, + }}, + }, + }, + }}, + }, { + desc: "sync then updates with add", + in: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 1, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 1}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 2}}, + }}, + }, + }, + }}, + updates: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 100, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 10}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 20}}, + }}, + }, + }, + }}, + }, { + desc: "multi update sync", + in: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 1, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 1}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 2}}, + }}, + }, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 2, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 10}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 20}}, + }}, + }, + }, + }}, + }, { + desc: "multi update sync with delay", + delay: true, + in: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 1, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 1}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 2}}, + }}, + }, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 2, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 10}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 20}}, + }}, + }, + }, + }}, + }, { + desc: "multi update sync with delay <negative>", + delay: true, + in: []*gpb.SubscribeResponse{{ + Response: &gpb.SubscribeResponse_SyncResponse{ + SyncResponse: true, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 2, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 1}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 2}}, + }}, + }, + }, + }, { + Response: &gpb.SubscribeResponse_Update{ + Update: &gpb.Notification{ + Timestamp: 1, + Update: []*gpb.Update{{ + Path: &gpb.Path{Element: []string{"a", "b"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 10}}, + }, { + Path: &gpb.Path{Element: []string{"a", "c"}}, + Val: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{IntVal: 20}}, + }}, + }, + }, + }}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + want := make([]*gpb.SubscribeResponse, len(tc.in)) + copy(want, tc.in) + q := NewFixed(want, tc.delay) + for _, u := range tc.updates { + q.Add(u) + want = append(want, u) + } + var got []*gpb.SubscribeResponse + Loop: + for { + v, err := q.Next() + switch { + case err != nil: + t.Fatalf("NewFixed(%q, %v).Next() unexpected error: got %q, want nil", want, tc.delay, err) + case v == nil: + break Loop + } + got = append(got, v.(*gpb.SubscribeResponse)) + } + if gotL, wantL := len(got), len(want); gotL != wantL { + t.Fatalf("q.Next() failed: got length(%d), want length(%d): %v", gotL, wantL, got) + } + }) + } +} + +func TestTypedValueOf(t *testing.T) { + tests := []struct { + desc string + in *fpb.Value + want *gpb.TypedValue + }{{ + desc: "string value", + in: &fpb.Value{Value: &fpb.Value_StringValue{&fpb.StringValue{Value: "UP"}}}, + want: &gpb.TypedValue{Value: &gpb.TypedValue_StringVal{"UP"}}, + }, { + desc: "int value", + in: &fpb.Value{Value: &fpb.Value_IntValue{&fpb.IntValue{Value: 100}}}, + want: &gpb.TypedValue{Value: &gpb.TypedValue_IntVal{int64(100)}}, + }, { + desc: "double value", + in: &fpb.Value{Value: &fpb.Value_DoubleValue{&fpb.DoubleValue{Value: float64(101)}}}, + want: &gpb.TypedValue{Value: &gpb.TypedValue_FloatVal{float32(101)}}, + }, { + desc: "delete value", + in: &fpb.Value{Value: &fpb.Value_Delete{&fpb.DeleteValue{}}}, + want: nil, + }, { + desc: "sync value", + in: &fpb.Value{Value: &fpb.Value_Sync{uint64(1)}}, + want: nil, + }, { + desc: "bool value", + in: &fpb.Value{Value: &fpb.Value_BoolValue{&fpb.BoolValue{Value: true}}}, + want: &gpb.TypedValue{Value: &gpb.TypedValue_BoolVal{true}}, + }, { + desc: "Uint value", + in: &fpb.Value{Value: &fpb.Value_UintValue{&fpb.UintValue{Value: 100}}}, + want: &gpb.TypedValue{Value: &gpb.TypedValue_UintVal{uint64(100)}}, + }} + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + if got, want := TypedValueOf(tc.in), tc.want; !proto.Equal(got, want) { + t.Errorf("TypedValueOf(%q) failed: got %q, want %q", tc.in, got, want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/testing/grpc/config/config_test.go b/deps/github.com/openconfig/gnmi/testing/fake/testing/grpc/config/config_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f3a63795b36c972fc8d87cd571ea4d49746f3e80 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/testing/grpc/config/config_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "testing" +) + +func TestWithSelfTLSCert(t *testing.T) { + _, err := WithSelfTLSCert() + if err != nil { + t.Fatalf("WithSelfTLSCert() failed: %v", err) + } +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/testing/grpc/config/server_option.go b/deps/github.com/openconfig/gnmi/testing/fake/testing/grpc/config/server_option.go new file mode 100644 index 0000000000000000000000000000000000000000..253505aff1c45c09e5be0702c9ec4580f5b922c5 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/testing/grpc/config/server_option.go @@ -0,0 +1,40 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package config provides gRPC configuration methods used by tests to +// facilitate easier setup of gRPC clients and servers. +package config + +import ( + "crypto/tls" + + "google.golang.org/grpc/credentials" + "google.golang.org/grpc" + + gtls "github.com/openconfig/gnmi/testing/fake/testing/tls" +) + +// WithSelfTLSCert generates a new self-signed in-memory TLS certificate and +// returns a grpc.ServerOption containing it. This is only for use in tests. +func WithSelfTLSCert() (grpc.ServerOption, error) { + cert, err := gtls.NewCert() + if err != nil { + return nil, err + } + return grpc.Creds(credentials.NewTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, + })), nil +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/testing/tls/tls.go b/deps/github.com/openconfig/gnmi/testing/fake/testing/tls/tls.go new file mode 100644 index 0000000000000000000000000000000000000000..e4247f77fec46a11029bbc54e3b1b4c003ac8158 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/testing/tls/tls.go @@ -0,0 +1,73 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package tls provides helpers for generation of local tls.Certificates for +// use in testing. +package tls + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "time" +) + +// NewCert will create a new tls.Certificate for use in testing. +// This cert is self signed and must not be used in production code. +func NewCert() (tls.Certificate, error) { + notBefore := time.Now() + notAfter := notBefore.Add(time.Hour) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return tls.Certificate{}, err + } + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Example Co"}, + }, + DNSNames: []string{"example.com"}, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + priv, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return tls.Certificate{}, err + } + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return tls.Certificate{}, err + } + + certBuf := &bytes.Buffer{} + pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + keyBuf := &bytes.Buffer{} + pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + + return tls.X509KeyPair(certBuf.Bytes(), keyBuf.Bytes()) +} diff --git a/deps/github.com/openconfig/gnmi/testing/fake/testing/tls/tls_test.go b/deps/github.com/openconfig/gnmi/testing/fake/testing/tls/tls_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8e01a800182a58c78bdf98d6040c1eacf6fc5401 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/testing/fake/testing/tls/tls_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tls + +import ( + "crypto/tls" + "fmt" + "io" + "io/ioutil" + "testing" +) + +func TestGenCert(t *testing.T) { + // Check that generation works at all. + cert, err := NewCert() + if err != nil { + t.Fatal(err) + } + // Check that cert is valid by making a dummy connection. + l, err := tls.Listen("tcp", fmt.Sprint(":0"), &tls.Config{ + Certificates: []tls.Certificate{cert}, + }) + if err != nil { + t.Fatal(err) + } + defer l.Close() + go func() { + // Just accept the connection to unblock Dial below. + con, err := l.Accept() + if err != nil { + t.Error(err) + } + io.Copy(ioutil.Discard, con) + }() + + con, err := tls.Dial("tcp", l.Addr().String(), &tls.Config{ + InsecureSkipVerify: true, + }) + if err != nil { + t.Error(err) + } + + if err := con.Handshake(); err != nil { + t.Error(err) + } +} diff --git a/deps/github.com/openconfig/gnmi/value/value.go b/deps/github.com/openconfig/gnmi/value/value.go new file mode 100644 index 0000000000000000000000000000000000000000..5b442a4f6b97f6e79956c2271fd4152e92f44e93 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/value/value.go @@ -0,0 +1,226 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package value provides utility functions for working with gNMI TypedValue +// messages. +package value + +import ( + "encoding/json" + "fmt" + "math" + "unicode/utf8" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +// FromScalar will convert common scalar types to their TypedValue equivalent. +// It will return an error if the type cannot be mapped to a scalar value. +func FromScalar(i interface{}) (*pb.TypedValue, error) { + tv := &pb.TypedValue{} + switch v := i.(type) { + case string: + if utf8.ValidString(v) { + tv.Value = &pb.TypedValue_StringVal{v} + } else { + return nil, fmt.Errorf("string %q contains non-UTF-8 bytes", v) + } + case int: + tv.Value = &pb.TypedValue_IntVal{int64(v)} + case int8: + tv.Value = &pb.TypedValue_IntVal{int64(v)} + case int16: + tv.Value = &pb.TypedValue_IntVal{int64(v)} + case int32: + tv.Value = &pb.TypedValue_IntVal{int64(v)} + case int64: + tv.Value = &pb.TypedValue_IntVal{v} + case uint: + tv.Value = &pb.TypedValue_UintVal{uint64(v)} + case uint8: + tv.Value = &pb.TypedValue_UintVal{uint64(v)} + case uint16: + tv.Value = &pb.TypedValue_UintVal{uint64(v)} + case uint32: + tv.Value = &pb.TypedValue_UintVal{uint64(v)} + case uint64: + tv.Value = &pb.TypedValue_UintVal{v} + case float32: + tv.Value = &pb.TypedValue_FloatVal{v} + case float64: + tv.Value = &pb.TypedValue_FloatVal{float32(v)} + case bool: + tv.Value = &pb.TypedValue_BoolVal{v} + case []string: + sa := &pb.ScalarArray{Element: make([]*pb.TypedValue, len(v))} + for x, s := range v { + sa.Element[x] = &pb.TypedValue{Value: &pb.TypedValue_StringVal{s}} + } + tv.Value = &pb.TypedValue_LeaflistVal{sa} + case []byte: + tv.Value = &pb.TypedValue_BytesVal{v} + case []interface{}: + sa := &pb.ScalarArray{Element: make([]*pb.TypedValue, len(v))} + var err error + for x, intf := range v { + sa.Element[x], err = FromScalar(intf) + if err != nil { + return nil, fmt.Errorf("in []interface{}: %v", err) + } + } + tv.Value = &pb.TypedValue_LeaflistVal{sa} + default: + return nil, fmt.Errorf("non-scalar type %+v", i) + } + return tv, nil +} + +// DeprecatedScalar is a scalar wrapper for communicating our desire to remove +// this encoding from gNMI support. +type DeprecatedScalar struct { + Message string + Value interface{} +} + +// ToScalar will convert TypedValue scalar types to a Go native type. It will +// return an error if the TypedValue does not contain a scalar type. +func ToScalar(tv *pb.TypedValue) (interface{}, error) { + var i interface{} + switch tv.Value.(type) { + case *pb.TypedValue_DecimalVal: + i = decimalToFloat(tv.GetDecimalVal()) + case *pb.TypedValue_StringVal: + i = tv.GetStringVal() + case *pb.TypedValue_IntVal: + i = tv.GetIntVal() + case *pb.TypedValue_UintVal: + i = tv.GetUintVal() + case *pb.TypedValue_BoolVal: + i = tv.GetBoolVal() + case *pb.TypedValue_FloatVal: + i = tv.GetFloatVal() + case *pb.TypedValue_LeaflistVal: + elems := tv.GetLeaflistVal().GetElement() + ss := make([]interface{}, len(elems)) + for x, e := range elems { + v, err := ToScalar(e) + if err != nil { + return nil, fmt.Errorf("ToScalar for ScalarArray %+v: %v", e.Value, err) + } + ss[x] = v + } + i = ss + case *pb.TypedValue_BytesVal: + i = tv.GetBytesVal() + case *gpb.TypedValue_JsonVal: + v := tv.GetJsonVal() + if err := json.Unmarshal(v, &i); err != nil { + return nil, err + } + uVal := DeprecatedScalar{ + Message: "Deprecated TypedValue_JsonVal", + Value: i, + } + return uVal, nil + case *gpb.TypedValue_JsonIetfVal: + v := tv.GetJsonIetfVal() + if err := json.Unmarshal(v, &i); err != nil { + return nil, err + } + uVal := DeprecatedScalar{ + Message: "Deprecated TypedValue_JsonIetfVal", + Value: i, + } + return uVal, nil + default: + return nil, fmt.Errorf("non-scalar type %+v", tv.Value) + } + return i, nil +} + +// decimalToFloat converts a *gnmi_proto.Decimal64 to a float32. Downcasting to +// float32 is performed as the precision of a float64 is not required. +func decimalToFloat(d *pb.Decimal64) float32 { + return float32(float64(d.Digits) / math.Pow(10, float64(d.Precision))) +} + +// Equal returns true if the values in a and b are the same. This method +// handles only the primitive types and ScalarArrays and returns false for all +// other types. +func Equal(a, b *pb.TypedValue) bool { + switch av := a.Value.(type) { + case *pb.TypedValue_StringVal: + bv, ok := b.Value.(*pb.TypedValue_StringVal) + if !ok { + return false + } + return av.StringVal == bv.StringVal + case *pb.TypedValue_IntVal: + bv, ok := b.Value.(*pb.TypedValue_IntVal) + if !ok { + return false + } + return av.IntVal == bv.IntVal + case *pb.TypedValue_UintVal: + bv, ok := b.Value.(*pb.TypedValue_UintVal) + if !ok { + return false + } + return av.UintVal == bv.UintVal + case *pb.TypedValue_BoolVal: + bv, ok := b.Value.(*pb.TypedValue_BoolVal) + if !ok { + return false + } + return av.BoolVal == bv.BoolVal + case *pb.TypedValue_BytesVal: + bv, ok := b.Value.(*pb.TypedValue_BytesVal) + if !ok { + return false + } + return string(av.BytesVal) == string(bv.BytesVal) + case *pb.TypedValue_FloatVal: + bv, ok := b.Value.(*pb.TypedValue_FloatVal) + if !ok { + return false + } + return av.FloatVal == bv.FloatVal + case *pb.TypedValue_DecimalVal: + bv, ok := b.Value.(*pb.TypedValue_DecimalVal) + if !ok { + return false + } + return av.DecimalVal.Digits == bv.DecimalVal.Digits && av.DecimalVal.Precision == bv.DecimalVal.Precision + case *pb.TypedValue_LeaflistVal: + bv, ok := b.Value.(*pb.TypedValue_LeaflistVal) + if !ok { + return false + } + ae, be := av.LeaflistVal.Element, bv.LeaflistVal.Element + if len(ae) != len(be) { + return false + } + for i := range ae { + if !Equal(ae[i], be[i]) { + return false + } + } + return true + } + // Types that are not primitive or ScalarArray are not considered. + return false +} diff --git a/deps/github.com/openconfig/gnmi/value/value_test.go b/deps/github.com/openconfig/gnmi/value/value_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d7410d4fe007cf84d60ba9d2852a2f0677d8ecbe --- /dev/null +++ b/deps/github.com/openconfig/gnmi/value/value_test.go @@ -0,0 +1,356 @@ +/* +Copyright 2017 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package value + +import ( + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + pb "github.com/openconfig/gnmi/proto/gnmi" +) + +type scalarTest struct { + intf interface{} + msg *pb.TypedValue + err bool +} + +func TestFromScalar(t *testing.T) { + tests := []scalarTest{ + {intf: "foo", msg: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"foo"}}}, + {intf: "a longer multiword string", msg: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"a longer multiword string"}}}, + {intf: 500, msg: &pb.TypedValue{Value: &pb.TypedValue_IntVal{500}}}, + {intf: int8(50), msg: &pb.TypedValue{Value: &pb.TypedValue_IntVal{50}}}, + {intf: int16(500), msg: &pb.TypedValue{Value: &pb.TypedValue_IntVal{500}}}, + {intf: int32(500), msg: &pb.TypedValue{Value: &pb.TypedValue_IntVal{500}}}, + {intf: int64(500), msg: &pb.TypedValue{Value: &pb.TypedValue_IntVal{500}}}, + {intf: uint(500), msg: &pb.TypedValue{Value: &pb.TypedValue_UintVal{500}}}, + {intf: uint8(50), msg: &pb.TypedValue{Value: &pb.TypedValue_UintVal{50}}}, + {intf: uint16(500), msg: &pb.TypedValue{Value: &pb.TypedValue_UintVal{500}}}, + {intf: uint32(500), msg: &pb.TypedValue{Value: &pb.TypedValue_UintVal{500}}}, + {intf: uint64(500), msg: &pb.TypedValue{Value: &pb.TypedValue_UintVal{500}}}, + {intf: float32(3.5), msg: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{3.5}}}, + {intf: true, msg: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{true}}}, + {intf: false, msg: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{false}}}, + {intf: float64(3.5), msg: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{3.5}}}, + {intf: []byte("foo"), msg: &pb.TypedValue{Value: &pb.TypedValue_BytesVal{[]byte("foo")}}}, + {intf: "a non-utf-8 string \377", err: true}, + { + intf: []string{"a", "b"}, + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{ + Element: []*pb.TypedValue{ + {Value: &pb.TypedValue_StringVal{"a"}}, + {Value: &pb.TypedValue_StringVal{"b"}}, + }, + }}}, + }, + { + intf: []string{}, + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{}}}, + }, + { + intf: []interface{}{"a", "b"}, + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{ + Element: []*pb.TypedValue{ + {Value: &pb.TypedValue_StringVal{"a"}}, + {Value: &pb.TypedValue_StringVal{"b"}}, + }, + }}}, + }, + { + intf: []interface{}{}, + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{}}}, + }, + { + intf: []interface{}{"a", 1}, + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{ + Element: []*pb.TypedValue{ + {Value: &pb.TypedValue_StringVal{"a"}}, + {Value: &pb.TypedValue_IntVal{1}}, + }, + }}}, + }, + } + for _, tt := range tests { + v, err := FromScalar(tt.intf) + switch { + case tt.err: + if err == nil { + t.Errorf("FromScalar(%v): got nil, wanted err", tt.intf) + } + case err != nil: + t.Errorf("FromScalar(%v): got error %v, want %s", tt.intf, err, tt.msg) + case !proto.Equal(v, tt.msg): + t.Errorf("FromScalar(%v): got %s, want %s", tt.intf, v, tt.msg) + } + } +} + +func TestToScalar(t *testing.T) { + tests := []scalarTest{ + {intf: "foo", msg: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"foo"}}}, + {intf: "a longer multiword string", msg: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"a longer multiword string"}}}, + {intf: int64(500), msg: &pb.TypedValue{Value: &pb.TypedValue_IntVal{500}}}, + {intf: uint64(500), msg: &pb.TypedValue{Value: &pb.TypedValue_UintVal{500}}}, + {intf: float32(3.5), msg: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{3.5}}}, + {intf: true, msg: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{true}}}, + {intf: false, msg: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{false}}}, + { + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{ + Element: []*pb.TypedValue{ + {Value: &pb.TypedValue_StringVal{"a"}}, + {Value: &pb.TypedValue_StringVal{"b"}}, + }, + }}}, + intf: []interface{}{"a", "b"}, + }, + { + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{}}}, + intf: []interface{}{}, + }, + { + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{}}, + intf: []interface{}{}, + }, + { + msg: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{ + Element: []*pb.TypedValue{ + {Value: &pb.TypedValue_StringVal{"a"}}, + {Value: &pb.TypedValue_IntVal{1}}, + {Value: &pb.TypedValue_UintVal{1}}, + }, + }}}, + intf: []interface{}{"a", int64(1), uint64(1)}, + }, + {intf: []byte("foo"), msg: &pb.TypedValue{Value: &pb.TypedValue_BytesVal{[]byte("foo")}}}, + { + msg: &pb.TypedValue{ + Value: &pb.TypedValue_DecimalVal{ + DecimalVal: &pb.Decimal64{ + Digits: 312, + Precision: 1, + }, + }, + }, + intf: float32(31.2), + }, + { + msg: &pb.TypedValue{ + Value: &pb.TypedValue_DecimalVal{ + DecimalVal: &pb.Decimal64{ + Digits: 5, + }, + }, + }, + intf: float32(5), + }, + { + msg: &pb.TypedValue{ + Value: &pb.TypedValue_DecimalVal{ + DecimalVal: &pb.Decimal64{ + Digits: 5678, + Precision: 18, + }, + }, + }, + intf: float32(.000000000000005678), + }, + {msg: &pb.TypedValue{Value: &pb.TypedValue_AnyVal{}}, err: true}, + {msg: &pb.TypedValue{Value: &pb.TypedValue_JsonVal{}}, err: true}, + {msg: &pb.TypedValue{Value: &pb.TypedValue_JsonIetfVal{}}, err: true}, + { + msg: &pb.TypedValue{Value: &pb.TypedValue_JsonVal{ + JsonVal: []byte(`{"a":1,"b":"foo"}`)}, + }, + intf: DeprecatedScalar{ + Message: "Deprecated TypedValue_JsonVal", + Value: map[string]interface{}{"a": float64(1), "b": "foo"}, + }, + }, + { + msg: &pb.TypedValue{Value: &pb.TypedValue_JsonIetfVal{ + JsonIetfVal: []byte(`{"a":1,"b":"foo"}`)}, + }, + intf: DeprecatedScalar{ + Message: "Deprecated TypedValue_JsonIetfVal", + Value: map[string]interface{}{"a": float64(1), "b": "foo"}, + }, + }, + {msg: &pb.TypedValue{Value: &pb.TypedValue_AsciiVal{}}, err: true}, + } + for _, tt := range tests { + v, err := ToScalar(tt.msg) + switch { + case tt.err: + if err == nil { + t.Errorf("ToScalar(%v): got nil, wanted err", tt.msg) + } + case err != nil: + t.Errorf("ToScalar(%v): got error %v, want %+v", tt.msg, err, tt.intf) + case !reflect.DeepEqual(v, tt.intf): + t.Errorf("ToScalar(%v): got %#v, want %#v", tt.msg, v, tt.intf) + } + } +} + +func TestEqual(t *testing.T) { + anyVal, err := ptypes.MarshalAny(&pb.TypedValue{Value: &pb.TypedValue_StringVal{"any val"}}) + if err != nil { + t.Errorf("MarshalAny: %v", err) + } + for _, test := range []struct { + name string + a, b *pb.TypedValue + want bool + }{ + // Equality is true. + { + name: "String equal", + a: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"foo"}}, + b: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"foo"}}, + want: true, + }, { + name: "Int equal", + a: &pb.TypedValue{Value: &pb.TypedValue_IntVal{1234}}, + b: &pb.TypedValue{Value: &pb.TypedValue_IntVal{1234}}, + want: true, + }, { + name: "Uint equal", + a: &pb.TypedValue{Value: &pb.TypedValue_UintVal{1234}}, + b: &pb.TypedValue{Value: &pb.TypedValue_UintVal{1234}}, + want: true, + }, { + name: "Bool equal", + a: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{true}}, + b: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{true}}, + want: true, + }, { + name: "Bytes equal", + a: &pb.TypedValue{Value: &pb.TypedValue_BytesVal{[]byte{1, 2, 3}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_BytesVal{[]byte{1, 2, 3}}}, + want: true, + }, { + name: "Float equal", + a: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{1234.56789}}, + b: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{1234.56789}}, + want: true, + }, { + name: "Decimal equal", + a: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + want: true, + }, { + name: "Leaflist equal", + a: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{Element: []*pb.TypedValue{{Value: &pb.TypedValue_StringVal{"one"}}, {Value: &pb.TypedValue_StringVal{"two"}}, {Value: &pb.TypedValue_StringVal{"three"}}}}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{Element: []*pb.TypedValue{{Value: &pb.TypedValue_StringVal{"one"}}, {Value: &pb.TypedValue_StringVal{"two"}}, {Value: &pb.TypedValue_StringVal{"three"}}}}}}, + want: true, + }, + // Equality is false. + { + name: "String not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"foo"}}, + b: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"bar"}}, + }, { + name: "Int not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_IntVal{1234}}, + b: &pb.TypedValue{Value: &pb.TypedValue_IntVal{123456789}}, + }, { + name: "Uint not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_UintVal{1234}}, + b: &pb.TypedValue{Value: &pb.TypedValue_UintVal{123456789}}, + }, { + name: "Bool not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{false}}, + b: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{true}}, + }, { + name: "Bytes not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_BytesVal{[]byte{2, 3}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_BytesVal{[]byte{1, 2, 3}}}, + }, { + name: "Float not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{12340.56789}}, + b: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{1234.56789}}, + }, { + name: "Decimal not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 11}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + }, { + name: "Leaflist not equal", + a: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{Element: []*pb.TypedValue{{Value: &pb.TypedValue_StringVal{"one"}}, {Value: &pb.TypedValue_StringVal{"two"}}, {Value: &pb.TypedValue_StringVal{"three"}}}}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{Element: []*pb.TypedValue{{Value: &pb.TypedValue_StringVal{"one"}}, {Value: &pb.TypedValue_StringVal{"two"}}, {Value: &pb.TypedValue_StringVal{"four"}}}}}}, + }, { + name: "Types not equal - String", + a: &pb.TypedValue{Value: &pb.TypedValue_StringVal{"foo"}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + }, { + name: "Types not equal - Int", + a: &pb.TypedValue{Value: &pb.TypedValue_IntVal{5}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + }, { + name: "Types not equal - Uint", + a: &pb.TypedValue{Value: &pb.TypedValue_UintVal{5}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + }, { + name: "Types not equal - Bool", + a: &pb.TypedValue{Value: &pb.TypedValue_BoolVal{true}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + }, { + name: "Types not equal - Float", + a: &pb.TypedValue{Value: &pb.TypedValue_FloatVal{5.25}}, + b: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + }, { + name: "Types not equal - Decimal", + a: &pb.TypedValue{Value: &pb.TypedValue_DecimalVal{&pb.Decimal64{Digits: 1234, Precision: 10}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_IntVal{5}}, + }, { + name: "Types not equal - Leaflist", + a: &pb.TypedValue{Value: &pb.TypedValue_LeaflistVal{&pb.ScalarArray{Element: []*pb.TypedValue{{Value: &pb.TypedValue_StringVal{"one"}}, {Value: &pb.TypedValue_StringVal{"two"}}, {Value: &pb.TypedValue_StringVal{"three"}}}}}}, + b: &pb.TypedValue{Value: &pb.TypedValue_IntVal{5}}, + }, + // Equality is not checked, expect false. + { + name: "Empty values not compared", + a: &pb.TypedValue{}, + b: &pb.TypedValue{}, + }, { + name: "Proto values not compared", + a: &pb.TypedValue{Value: &pb.TypedValue_AnyVal{anyVal}}, + b: &pb.TypedValue{Value: &pb.TypedValue_AnyVal{anyVal}}, + }, { + name: "JSON values not compared", + a: &pb.TypedValue{Value: &pb.TypedValue_JsonVal{[]byte("5")}}, + b: &pb.TypedValue{Value: &pb.TypedValue_JsonVal{[]byte("5")}}, + }, { + name: "JSON IETF values not compared", + a: &pb.TypedValue{Value: &pb.TypedValue_JsonIetfVal{[]byte("10.5")}}, + b: &pb.TypedValue{Value: &pb.TypedValue_JsonIetfVal{[]byte("10.5")}}, + }, { + name: "ASCII values not compared", + a: &pb.TypedValue{Value: &pb.TypedValue_AsciiVal{"foo"}}, + b: &pb.TypedValue{Value: &pb.TypedValue_AsciiVal{"foo"}}, + }, + } { + t.Run(test.name, func(t *testing.T) { + got := Equal(test.a, test.b) + if got != test.want { + t.Errorf("got: %t, want: %t", got, test.want) + } + }) + } +} diff --git a/deps/github.com/openconfig/gnmi/watch/watch.go b/deps/github.com/openconfig/gnmi/watch/watch.go new file mode 100644 index 0000000000000000000000000000000000000000..06edab46da741ccd9bca205f48a77fef7ab8e968 --- /dev/null +++ b/deps/github.com/openconfig/gnmi/watch/watch.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 Google Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package watch detects raw changes to files. The interpretation of a file is +// filesystem-specific. +package watch + +import ( + "context" +) + +// Watcher watches files at the given paths for changes. +type Watcher interface { + // Read blocks and returns the next update for a file. + // + // An error is returned when there is an underyling issue in the Watcher + // preventing Read, or ctx is cancelled. The returned error may indicate a + // fatal issue requiring a new Watcher to be created. + // + // Subsequent calls block until the underlying + // contents or error changes. When multiple updates have occurred for a file, + // Read coalesces and returns the latest update. + Read(ctx context.Context) (Update, error) + + // Add causes Watcher to monitor an additional path. The format is + // filesystem-specific. If Close has been called, this has no effect. + Add(path string) error + + // Remove causes Watcher to stop monitoring a path. The path must match one + // already monitored in the same format. The format is filesystem-specific. + Remove(path string) error + + // Close causes Watcher to stop watching all files and release its resources. + Close() +} + +// Update represents contents for a file. Path can represent fan-out on +// individual files when watching a path that contains multiple files. +// Update contains an error reflecting path-specific problems. +type Update struct { + Path string + Contents []byte + Err error +}