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, &timestamp)
+
+	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, &timestamp)
+
+	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, &timestamp)
+
+	// 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, &timestamp)
+					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, &timestamp)
+
+	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, &timestamp)
+			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, &timestamp)
+
+	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, &timestamp)
+			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, &timestamp)
+
+	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, &timestamp)
+			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], &timestamp)
+	<-stall
+	sendUpdates(t, cache, paths, &timestamp)
+	<-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], &timestamp)
+	wg.Wait()
+	wg.Add(sc)
+	sendUpdates(t, cache, paths, &timestamp)
+	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, &timestamp)
+	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
+}