OpenConfig实战(一)

本节主要依据OpenConfig初识(二)在linux环境下进行gNMI客户端和服务端的实际部署,以进一步了解gNMI的工作原理和使用方法。

本节go语言采用go1.15.2 linux/amd64版本,在$GOPATH/src目录下,创建gnmi-test文件夹,并在gnmi-test文件夹下创建client_app和gnxi_target子文件夹,分别用来存放gNMI客户端和服务端相关文件,为了方便环境部署,go环境变量设置如下:

export GO111MODULE=on
export GOPROXY=https://goproxy.cn

创建gNMI服务端

gNxI是一种使用gNMI(gRPC网络管理接口)和gNOI(gRPC网络操作接口)协议的网络管理工具集。此处,使用该工具集中的gnmi_target作为gNMI服务端,创建gNMI服务前,先简单了解一下gnxi工具集。

gnxi工具集

通过下面的命令可以编译并安装gnxi相关工具:

cd $GOPATH/bin
go install github.com/google/gnxi/...

gnxi/certs目录下的generate.sh脚本可用于生成客户端和服务端之间的认证证书,运行该脚本,结果如下:

[root@localhost certs]# ./generate.sh
[root@localhost certs]# ls
ca.crt  ca.key  client.crt  client.key   README.md   target.csr
ca.csr  ca.srl  client.csr  generate.sh  target.crt  target.key

其中,ca.crt是CA证书授权;ca.key是CA私有秘钥;client.crt/target.crt是由CA授权的client/target证书;client.key/target.key是相应client/target的私有秘钥。

将ca.crt, target.crt, target.key和gnxi/gnmi_target/openconfig-openflow.json文件放到$GOPATH/src/gnmi-test/gnxi_target/文件夹下,同样将ca.crt, client.crt, client.key拷贝到$GOPATH/src/gnmi-test/client_app目录下,分别在两个目录下,开启两个终端,执行如下命令:

[root@localhost gnxi_target]# gnmi_target -bind_address :9339 -config openconfig-openflow.json \
-key target.key -cert target.crt -ca ca.crt -alsologtostderr
I1014 08:58:23.265109   40368 gnmi_target.go:119] starting to listen on :9339
I1014 08:58:23.346864   40368 gnmi_target.go:125] starting to serve
[root@localhost client_app]# gnmi_get -xpath "/system/openflow/agent/config/datapath-id" \
-xpath "/system/openflow/controllers/controller[name=main]/connections/connection[aux-id=0]/state/address" \
-target_addr localhost:9339 -target_name target.com \
-key client.key -cert client.crt -ca ca.crt -alsologtostderr
== getRequest:
path: <
  elem: <
    name: "system"
  >
  elem: <
    name: "openflow"
  >
  elem: <
    name: "agent"
  >
  elem: <
    name: "config"
  >
  elem: <
    name: "datapath-id"
  >
>
path: <
  elem: <
    name: "system"
  >
  elem: <
    name: "openflow"
  >
  elem: <
    name: "controllers"
  >
  elem: <
    name: "controller"
    key: <
      key: "name"
      value: "main"
    >
  >
  elem: <
    name: "connections"
  >
  elem: <
    name: "connection"
    key: <
      key: "aux-id"
      value: "0"
    >
  >
  elem: <
    name: "state"
  >
  elem: <
    name: "address"
  >
>
encoding: JSON_IETF

== getResponse:
notification: <
  timestamp: 1602637327544603349
  update: <
    path: <
      elem: <
        name: "system"
      >
      elem: <
        name: "openflow"
      >
      elem: <
        name: "agent"
      >
      elem: <
        name: "config"
      >
      elem: <
        name: "datapath-id"
      >
    >
    val: <
      string_val: "00:16:3e:00:00:00:00:00"
    >
  >
>
notification: <
  timestamp: 1602637327550461350
  update: <
    path: <
      elem: <
        name: "system"
      >
      elem: <
        name: "openflow"
      >
      elem: <
        name: "controllers"
      >
      elem: <
        name: "controller"
        key: <
          key: "name"
          value: "main"
        >
      >
      elem: <
        name: "connections"
      >
      elem: <
        name: "connection"
        key: <
          key: "aux-id"
          value: "0"
        >
      >
      elem: <
        name: "state"
      >
      elem: <
        name: "address"
      >
    >
    val: <
      string_val: "192.0.2.10"
    >
  >
>

可以看到客户端通过gnmi_get可以获取到服务端openconfig-openflow.json配置文件中的配置信息。同样的,gnmi_capabilities可以获取到服务端支持的能力集;gnmi_set可以设置服务端的配置信息;gnmi_subscribe可以订阅服务端的信息,当然还有其他工具,详细请参考gNxI

基于openconfig-interface.yang创建gnmi服务

openconfig-interface.yang在OpenConfig初识(二)中已经给出,下面根据openconfig-interface.yang创建gnmi服务,是的启动该服务后,服务会加载由openconfig-interface.yang绑定的go数据结构,然后客户端和服务端就可以根据该YANG模型定义的数据结构进行信息交互了。

gnxi/gnmi/modeldata/gostruct目录下有两个go文件:gen.go和generated.go。gen.go给出了利用YANG文件生成generated.go的方法,已经提供的generated.go文件头注释部分如下:

/*
Package gostruct is a generated package which contains definitions
of structs which represent a YANG schema. The generated schema can be
compressed by a series of transformations (compression was false
in this case).

This package was generated by go/src/github.com/openconfig/ygot/genutil/names.go
using the following YANG input files:
	- github.com/openconfig/public/release/models/interfaces/openconfig-interfaces.yang
	- github.com/openconfig/public/release/models/openflow/openconfig-openflow.yang
	- github.com/openconfig/public/release/models/platform/openconfig-platform.yang
	- github.com/openconfig/public/release/models/system/openconfig-system.yang
Imported modules were sourced from:
	- github.com/openconfig/public/...
	- github.com/YangModels/yang/...
*/

从文件头可以看出,该文件由github.com/openconfig/ygot/genutil/names.go生成,使用的yang文件包括:openconfig-interfaces.yang,openconfig-openflow.yang,openconfig-platform.yang,openconfig-system.yang等,基于此,我们利用自己写的openconfig-interface.yang文件生成generated.go文件。

首先将openconfig-interface.yang文件及其依赖openconfig-extensions.yang文件放入$GOPATH/src/gnmi-test/gnxi_target/目录下,执行如下命令:

[root@localhost gostruct]# go run /root/go/pkg/mod/github.com/openconfig/ygot@v0.8.0/generator/generator.go \
-generate_fakeroot -output_file /root/go/src/gnxi-master/gnmi/modeldata/gostruct/generated.go \
-package_name gostruct /root/go/src/gnmi-test/gnxi_target/openconfig-interface.yang

生成的generated.go文件头部注释变为:

/*
Package gostruct is a generated package which contains definitions
of structs which represent a YANG schema. The generated schema can be
compressed by a series of transformations (compression was false
in this case).

This package was generated by /root/go/pkg/mod/github.com/openconfig/ygot@v0.8.0/genutil/names.go
using the following YANG input files:
	- /root/go/src/gnmi-test/gnxi_target/openconfig-interface.yang
Imported modules were sourced from:
*/

OpenConfig初识(二)节中的interface.json文件拷贝到$GOPATH/src/gnmi-test/gnxi_target/目录下,执行如下命令:

go run /root/go/src/gnxi-master/gnmi_target/gnmi_target.go \
-bind_address :10161 -config interface.json -key target.key \
-cert target.crt -ca ca.crt -username foo -password bar -alsologtostderr

如果出现类似错误内容:…/…/gnxi-master/gnmi_target/gnmi_target.go:27:2: cannot find module providing package github.com/golang/glog: working directory is not part of a module
当前目录下执行:

[root@localhost gnxi_target]# go mod init

出现类似错误内容:

F1014 10:00:46.662203   45195 gnmi_target.go:114] error in creating gnmi target: parent container interfaces (type *gostruct.OpenconfigInterfaces_Interfaces): JSON contains unexpected field config
exit status 1

错误原因可能是生成generated.go文件的路径不对,程序找不到生成的generated.go,为此,将文件输出到/root/go/pkg/mod/github.com/google/gnxi@v0.0.0-20201002114532-88db804e4a55/gnmi/modeldata/gostruct/目录下,执行如下命令:

[root@localhost gnxi_target]# go run /root/go/pkg/mod/github.com/openconfig/ygot@v0.8.0/generator/generator.go \
-generate_fakeroot -output_file /root/go/pkg/mod/github.com/google/gnxi@v0.0.0-20201002114532-88db804e4a55/gnmi/modeldata/gostruct/generated.go \
-package_name gostruct /root/go/src/gnmi-test/gnxi_target/openconfig-interface.yang

出现如下错误:

../../gnxi-master/gnmi_target/gnmi_target.go:87:3: undefined: gostruct.ΛEnum

将文件gnmi_target.go的87行参数修改为nil, 继续执行,输出结果如下:

[root@localhost gnxi_target]# go run /root/go/src/gnxi-master/gnmi_target/gnmi_target.go -bind_address :10161 \
-config interface.json -key target.key -cert target.crt -ca ca.crt \
-username foo -password bar -alsologtostderr
I1014 10:45:36.842286   46996 gnmi_target.go:119] starting to listen on :10161
I1014 10:45:36.872633   46996 gnmi_target.go:125] starting to serve

如此,gNMI服务成功启动了。

创建gNMI客户端

在$GOPATH/src/gnmi-test/client_app目录下,执行如下命令:

[root@localhost client_app]# python -m grpc.tools.protoc --proto_path=/root/go/src/gnmi-master/proto/gnmi_ext/ \
--proto_path=/root/go/src/gnmi-master/proto/gnmi/ --python_out=./ --grpc_python_out=./  \
/root/go/src/gnmi-master/proto/gnmi_ext/gnmi_ext.proto

[root@localhost client_app]# python -m grpc.tools.protoc --proto_path=/root/go/src/gnmi-master/proto/gnmi_ext/ \
--proto_path=/root/go/src/gnmi-master/proto/gnmi/ --python_out=./ --grpc_python_out=./ \
/root/go/src/gnmi-master/proto/gnmi_ext/gnmi_ext.proto

生成gnmi_pb2.py & gnmi_pb2_grpc.py, gnmi_ext_pb2.py & gnmi_ext_pb2_grpc.py文件,将OpenConfig初识(二)节中的simple_client.py脚本稍作修改,内容如下:

import sys
sys.path.append('/root/go/src/gnmi-test/client_app')  # For importing gnmi protos

import gnmi_pb2
import gnmi_pb2_grpc
import grpc
import json

def SetUpChannel():
  """Set up the gNMI RPC."""
  wdir = '/root/go/src/gnmi-test/client_app/'
  # Specify the credentials in use.
  creds = grpc.ssl_channel_credentials(root_certificates=open(wdir +
      'ca.crt').read(), private_key=open(wdir +
          'client.key').read(), certificate_chain=open(wdir +
              'client.crt').read())
  # Assgin the gRPC channel to our gNMI Target.
  channel = grpc.secure_channel('target.com:10161', creds) """ www.example.com修改target.com"""
  stub = gnmi_pb2_grpc.gNMIStub(channel)
  return stub
def GetRequest(stub, path):
  """Issue a gNMI GetRequest for the given path."""
  path_list = [gnmi_pb2.PathElem(name=path, key={})]
  paths = gnmi_pb2.Path(elem=path_list)
  # Metadata is User/pass for our example gNMI Target
  response = stub.Get(gnmi_pb2.GetRequest(path=[paths]), metadata=[
      ('username', 'foo'), ('password', 'bar')])
  return response
 
if __name__ == '__main__':
  stub = SetUpChannel()
  gnmi_response = GetRequest(stub, 'interfaces')
  print(gnmi_response)  #Print as PB

执行该脚本文件:

[root@localhost client_app]# python simple_client.py 

结果有如下错误输出:

E1014 11:04:29.212096072   48266 ev_epollex_linux.cc:516]    Error shutting down fd 9. errno: 9
Traceback (most recent call last):
  File "simple_client.py", line 32, in <module>
    gnmi_response = GetRequest(stub, 'interfaces')
  File "simple_client.py", line 27, in GetRequest
    ('username', 'foo'), ('password', 'bar')])
  File "/usr/lib64/python2.7/site-packages/grpc/_channel.py", line 826, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/usr/lib64/python2.7/site-packages/grpc/_channel.py", line 729, in _end_unary_response_blocking
    raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
	status = StatusCode.UNAVAILABLE
	details = "DNS resolution failed for service: target.com:10161"
	debug_error_string = "{"created":"@1602644669.212669048","description":"Resolver transient failure","file":"src/core/ext/filters/client_channel/resolving_lb_policy.cc","file_line":215,"referenced_errors":[{"created":"@1602644669.212663340","description":"DNS resolution failed for service: target.com:10161","file":"src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc","file_line":378,"grpc_status":14,"referenced_errors":[{"created":"@1602644669.212057152","description":"C-ares status is not ARES_SUCCESS qtype=AAAA name=target.com is_balancer=0: Could not contact DNS servers","file":"src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc","file_line":287,"referenced_errors":[{"created":"@1602644667.213547029","description":"C-ares status is not ARES_SUCCESS qtype=A name=target.com is_balancer=0: Could not contact DNS servers","file":"src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc","file_line":287}]}]}]}"

这是由于客户端和服务端进行TCP连接验证时找不到target.com,编辑/etc/hosts文件,文件末尾添加:

172.20.10.11 target.com

在此运行simple_client.py脚本,结果如下:

[root@localhost client_app]# python simple_client.py 
notification {
  timestamp: 1602645066579990669
  update {
    path {
      elem {
        name: "interfaces"
      }
    }
    val {
      json_val: "{\"config\":{\"enabled\":true},\"state\":{\"enabled\":true,\"rx-packets\":73}}"
    }
  }
}

如此客户端成功连接服务端,并且客户端可通过编程实现对服务端的配置和管理操作。

完毕!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值