为了更好的分享体验,博客搬迁至极客驿站 ,欢迎查阅
基于OVSDB协议在SDN控制器ONOS开发配置管理OVS API
- 介绍
- 准备工作
- 开发步骤
- 问题排查
介绍
目前protocol 中ovsdb协议层面已经实现,但是基于ovsdb protocol的上层接口并不完善,ONOS只支持以下几种
网桥操作
boolean addBridge(BridgeDescription bridgeDescription);
void deleteBridge(BridgeName bridgeName);
Collection getBridges();
端口操作
void addPort(BridgeName bridgeName, String portName);
void deletePort(BridgeName bridgeName, String portName);
Collection getPorts();
Set getPortNumbers();
List getLocalPorts(Iterable ifaceIds);
Controller操作
List getControllers();
void setControllers(List controllers);
接口操作
boolean addPatchMode(String ifaceName, PatchDescription patchInterface);
boolean removePatchMode(String ifaceName);
boolean addTunnelMode(String intf, TunnelDescription tunnelDesc);
boolean removeTunnelMode(String intf);
这些配置远远不够
准备工作
增加api配置ovs,首先需要熟悉ovsdb rfc7047内容,其中规定了协议内容,如何操作ovsdb数据库,有哪些限制这一点非常重要,不然可能排查bug时很难找到其中原因,另外还需要熟悉ovsdb数据库各表的各个字段以及属性,这个可以从ovs-vswitchd.conf.db.5.pdf中查找或者在数据模板/usr/share/openvswitch/vswitch.ovsschema中查看,不需记忆;需要值得一提的对于写操作,下发的transcat string不能出现字段类型为immutable类型的column值,例如:可以vswitch.ovsschema查看到Interface Name字段是不可更改的,这个字段在通过ovsdb协议下发update或mutate操作,不能再value字段中携带,否则会导致更新失败。这个后面有举例说明。
开发步骤
Api框架图
基于ONOS整体框架良好的封装特性,在ONOS增加对底层接口配置特性,可分为以下四个步骤
1、 在onos-api模块中找到所属分类,增加接口,BridgeConfig,ControllerConfig,InterfaceConfig,PortAdmin,PortConfig,PowerConfig,QueueConfig这些配置类是我们需要关注的,在onos api这一层,并没有暴露很多细节,这里它只是定义一个接口参数和返回类型,至于底层如何实现,使用ovsdb还是netconf它并不关心,只要匹配对应的driver就可以了
2、 在driver层实现1步骤中的接口,比如现在BridgeConfig中的接口目前只有ovsdbBridgeConfig一种实现,而ControllerConfig中的接口有若干种实现;
以ovsdbBridgeConfig为例,它会获取一个当前设备的ovsdb协议连接,然后调用ovsdb协议封装的API进行具体实现,所有的driver接口实现都类似,另外需要注意,因为java接口implements关系,如果一个onos api在driver有多种实现,除了要实现特定协议的处理流程,其他类型协议需要打桩处理,否则编译出错;另外如果新增driver接口,比如新增ovsdbQueueConfig Driver,需要在onos/drivers/ovsdb/src/main/resources/ovsdb-driver.xml增加对应配置,否则behavior执行时会提示not support。
3、 第二步骤中提到的client中的接口实际上是ovsdb协议实现封装的一层对外API,这一步就需要在这里增加接口,这里增加的接口和第一步中的api类似的,只不过第一步中api是屏蔽底层细节的,底层可以是任何协议,而这一步的api是具体某种协议的api,ovsdb协议的api位置在onos\protocols\ovsdb\api,所有对外接口放在OvsdbClientService抽象接口中;另外其中的一些测试实现类也需要打桩处理。
4、 实现第三步骤中定义的接口,这里就是调用协议接口,来实现自己想要的功能,以创建tunnel隧道接口为例
这里面其实是对数据库的操作,最终会通过ovsdb协议下发到openvswitch,其中的操作适合ovsdb rfc定义是一致的
具体每个表的操作,可以到onos\protocols\ovsdb\rfc\src\main\java\org\onosproject\ovsdb\rfc\table中去参考,其中的表属性是和OpenvSwitch一一对应的
问题排查
按照上述流程开发,所有的ovs操作都是可以支持的,如果遇到问题,改如何排查?
大致可分为两种:1、查看onos log ,如果log中有导致该问题的异常,那么这个问题就比较容易解决,解决掉异常即可
2、如果onos log中没有异常,但是执行配置操作,转发平面ovs没有任何反应,没有看到想要的配置下发,例如onos执行qos的配置,但是转发面没有出现任何相关的配置,而onos也没有报任何错误。这个时候可能就是下发ovsdb协议可能违背了一些限制,导致更新失败,但是onos并不会报违背了哪些限制导致更新失败,如何知道具体什么原因导致的配置失败呢?答案是使用ovsdb-client –v transact命令模拟onos下发ovsdb协议配置操作,在ovs这个地方如果因为某种类型不匹配,它会打印出详细错误信息
但是ovsdb-client –v transact [SERVER] TRANSACTION这条命令的TRANSACTION本身很复杂,手工敲可能不太现实,这个TRANSACTION可以在onos 代码加log打印出来,如下图:
transactString就是最终要下发的操作,将这个打印出来,然后使用ovsdb-client命令验证下发操作失败的具体原因,需要强调的是transactString的内容是和rfc7047协议规定吻合的。
以下发ingress policing rate/burst为例,打印出来如下:
transact string{“id”:“e676f626-4e80-45cb-ae45-2f5500b016d6”,“method”:“transact”,“params”:[“Open_vSwitch”,{“op”:“update”,“row”:{“ingress_policing_rate”:[“set”,[100]],“ingress_policing_burst”:[“set”,[100]]},“where”:[[“_uuid”,“==”,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”}]}
这个时候就可以使用ovsdb-client验证其正确性,
o[root@localhost ~]# ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“update”,“row”:{“ingress_policing_rate”:[“set”,[200]],“ingress_policing_burst”:[“set”,[200]]},“where”:[[”_uuid",““,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”}]”
下面是执行上面命令打印的信息
[root@localhost ~]# ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“update”,“row”:{“ingress_policing_rate”:[“set”,[200]],“ingress_policing_burst”:[“set”,[200]]},“where”:[[”_uuid","”,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”}]"
2016-09-08T06:29:50Z|00001|jsonrpc|DBG|unix:/var/run/openvswitch/db.sock: send request, method=“transact”, params=[“Open_vSwitch”,{“where”:[[“_uuid”,““,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”,“op”:“update”,“row”:{“ingress_policing_burst”:[“set”,[200]],“ingress_policing_rate”:[“set”,[200]]}}], id=0
2016-09-08T06:29:50Z|00002|poll_loop|DBG|wakeup due to 0-ms timeout
2016-09-08T06:29:50Z|00003|poll_loop|DBG|wakeup due to [POLLIN] on fd 3 (<->/var/run/openvswitch/db.sock) at lib/stream-fd.c:155
2016-09-08T06:29:50Z|00004|jsonrpc|DBG|unix:/var/run/openvswitch/db.sock: received reply, result=[{“count”:1}], id=0
[{“count”:1}]
证明有一个row更新了,这个说明transaction是正确的,如果是不正确的呢,比如在上面的transaction中row中增加一个name字段,它就会提示name字段不可更改
[root@localhost ~]# ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“update”,“row”:{“name”:“vnet0”,“ingress_policing_rate”:[“set”,[200]],“ingress_policing_burst”:[“set”,[200]]},“where”:[[”_uuid”,"”,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”}]"
2016-09-08T06:34:52Z|00001|jsonrpc|DBG|unix:/var/run/openvswitch/db.sock: send request, method=“transact”, params=[“Open_vSwitch”,{“where”:[[“_uuid”,““,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”,“op”:“update”,“row”:{“name”:“vnet0”,“ingress_policing_burst”:[“set”,[200]],“ingress_policing_rate”:[“set”,[200]]}}], id=0
2016-09-08T06:34:52Z|00002|poll_loop|DBG|wakeup due to 0-ms timeout
2016-09-08T06:34:52Z|00003|poll_loop|DBG|wakeup due to [POLLIN] on fd 3 (<->/var/run/openvswitch/db.sock) at lib/stream-fd.c:155
2016-09-08T06:34:52Z|00004|jsonrpc|DBG|unix:/var/run/openvswitch/db.sock: received reply, result=[{“syntax”:”{“op”:“update”,“row”:{“ingress_policing_burst”:[“set”,[200]],“ingress_policing_rate”:[“set”,[200]],“name”:“vnet0”},“table”:“Interface”,“where”:[[“_uuid”,"”,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]]}“,“details”:“Cannot update immutable column name in table Interface.”,“error”:“constraint violation”}], id=0
[{“details”:“Cannot update immutable column name in table Interface.”,“error”:“constraint violation”,“syntax”:”{“op”:“update”,“row”:{“ingress_policing_burst”:[“set”,[200]],“ingress_policing_rate”:[“set”,[200]],“name”:“vnet0”},“table”:“Interface”,“where”:[[“_uuid”,““,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]]}”}]
另外删除Ovsdb Table时需要考虑到各表之间的依赖关系,对于有strong reference关系的表,在删除子表时需要先将父表的引用删除;对于调用deleteConfig接口时,这个接口会根据传入的参数查找父表的引用进行删除,这里特别要注意附表引用的数据类型,以Port、QoS与Queue关系为例。
“Port”: {
“columns”: {
“name”: {
“type”: “string”,
“mutable”: false},
…
“qos”: {
“type”: {“key”: {“type”: “uuid”,
“refTable”: “QoS”},
“min”: 0, “max”: 1}},
“QoS”: {
“columns”: {
“type”: {
“type”: “string”},
“queues”: {
“type”: {“key”: {“type”: “integer”,
“minInteger”: 0,
“maxInteger”: 4294967295},
“value”: {“type”: “uuid”,
“refTable”: “Queue”},
“min”: 0, “max”: “unlimited”}},
…
这里Port引用Qos的时候,单数据接口type为uuid,Qos引用Queue时的数据类型为map类型,既存在key又有value;关于表元素的数据类型,可以在vswitch.ovsschema的定义中找到
错误的删除Queue元素的操作
ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“mutate”,“where”:[[“queues”,“includes”,[“uuid”,“dd2de53c-e4e7-4252-a191-72968a0f7859”]]],“mutations”:[[“queues”,“delete”,[“uuid”,“dd2de53c-e4e7-4252-a191-72968a0f7859”]]],“table”:“QoS”},{“op”:“delete”,“where”:[[”_uuid","”,[“uuid”,“dd2de53c-e4e7-4252-a191-72968a0f7859”]]],“table”:“Queue”}]"
正确
ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“mutate”,“where”:[[“queues”,“includes”,[“map”,[[0,[“uuid”,“dd2de53c-e4e7-4252-a191-72968a0f7859”]]]]]],“mutations”:[[“queues”,“delete”,[“map”,[[0,[“uuid”,“dd2de53c-e4e7-4252-a191-72968a0f7859”]]]]]],“table”:“QoS”},{“op”:“delete”,“where”:[[”_uuid",“==”,[“uuid”,“dd2de53c-e4e7-4252-a191-72968a0f7859”]]],“table”:“Queue”}]"
OpenvSwitch transact命令 example
设置interface rate, mutate采用insert操作失败,采用+=可以
ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“mutate”,“where”:[[”_uuid",“==”,[“uuid”,“4aa0e983-29fd-4661-a05f-623e60fdf8e2”]]],“mutations”:[[“ingress_policing_rate”,“+=”,200]],“table”:“Interface”}]"
设置interface rate, update操作,去掉interface name不可更新的column
ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“update”,“row”:{“mac_in_use”:[“set”,[“fe:54:00:78:6d:17”]],“duplex”:[“set”,[“full”]],“admin_state”:[“set”,[“up”]],“error”:[“set”,[]],“type”:”“,“ingress_policing_burst”:[“set”,[100]],“cfm_mpid”:[“set”,[]],“mac”:[“set”,[]],“cfm_fault”:[“set”,[]],“cfm_flap_count”:[“set”,[]],“options”:[“map”,[]],“cfm_health”:[“set”,[]],“ingress_policing_rate”:[“set”,[100]],“bfd”:[“map”,[]],“ifindex”:[“set”,[36]],“ofport”:[“set”,[6]],“other_config”:[“map”,[]],“bfd_status”:[“map”,[]],“cfm_remote_mpids”:[“set”,[]],“cfm_fault_status”:[“set”,[]],“link_speed”:[“set”,[10000000]],“external_ids”:[“map”,[[“vm-id”,“e570935c-693a-458c-8a59-ca687e0473a3”],[“attached-mac”,“52:54:00:78:6d:17”],[“iface-status”,“active”],[“iface-id”,“2cf8fa66-3b07-4db1-8e0b-ec635af99237”]]],“mtu”:[“set”,[1500]],“lacp_current”:[“set”,[]],“link_resets”:[“set”,[1]],“cfm_remote_opstate”:[“set”,[]],“link_state”:[“set”,[“up”]],“ofport_request”:[“set”,[]],“lldp”:[“map”,[]],“statistics”:[“map”,[[“tx_packets”,1732],[“tx_errors”,0],[“collisions”,0],[“tx_dropped”,0],[“rx_dropped”,0],[“rx_crc_err”,0],[“rx_bytes”,0],[“rx_frame_err”,0],[“rx_over_err”,0],[“tx_bytes”,140292],[“rx_errors”,0],[“rx_packets”,0]]],“status”:[“map”,[[“driver_name”,“tun”],[“driver_version”,“1.6”],[“firmware_version”,”“]]]},“where”:[[”_uuid",“==”,[“uuid”,“4aa0e983-29fd-4661-a05f-623e60fdf8e2”]]],“table”:“Interface”}]"
ovsdb-client -v transact “[“Open_vSwitch”,{“op”:“update”,“row”:{“ingress_policing_rate”:[“set”,[200]],“ingress_policing_burst”:[“set”,[200]]},“where”:[[”_uuid",“==”,[“uuid”,“58dfdd10-43b9-45c7-aca6-e6fe6b5b6a7b”]]],“table”:“Interface”}]"