vpp-agent的REST API使用及其原理

启动程序

REST API 启动程序:vpp-agent/cmd/vpp-agent/main.go
API 文档:https://docs.ligato.io/en/latest/api/api-vpp-agent/

启动端口为9191,暂未找到修改点

监听的socket文件为/run/vpp/api.sock,暂未找到修改点

测试vpp配置文件如下:

unix {
    cli-listen /run/vpp/cli.sock
    cli-no-pager
    full-coredump
    log /home/data/vpp/vpp.log
    runtime-dir /home/data/vpp
    pidfile /home/data/vpp/agentvpp1.pid
}
plugins {
    plugin dpdk_plugin.so {
        disable
    }
}
socksvr {
    socket-name /run/vpp/api.sock
}
statseg {
    socket-name /run/vpp/stats.sock
    per-node-counters on
}

Api 介绍

VPP Agent 实现了两个 API:

NB API:外部客户端与 VPP Agent 进行交互的接口,客户端可以完成 VPP configuration management。

SB API:VPP Agent 与 VPP Data Plane 进行交互的接口。完成 VPP Data Plane 的 Events、Notifications 和 Configuration Dumps 操作。

通过 VPP Agent 提供的 REST plugin 我们可以完成以下两类操作:

NB configuration management

SB VPP configuration dumps

NB Configuration

NB Configuration 用于完成对 VPP Data Plane 的查询、配置、验证等操作。

PUT /configuration?replace
GET /configuration
GET /info/configuration/jsonschema
POST /configuration/validate
PUT /configuration

例如:

cat loop-bd.yaml

vppConfig:
  interfaces:
  - name: loop1
    type: SOFTWARE_LOOPBACK
    enabled: true
    ipAddresses:
    - 192.168.1.1/24
  bridgeDomains:
  - name: bd1
    forward: true
    learn: true
    interfaces:
    - name: loop1
curl -X PUT -H "Content-Type: application/yaml" --data-binary @loop-bd.yaml http://localhost:9191/configuration

SB Dumps

SB Dumps 仅用于获取 VPP Data Plane 的运行时配置信息。详细见 API 文档。

Execute VPP CLI commands through a REST API

VPP Agent REST Plugin 还支持通过 REST API 来执行 VPP CLI 指令,例如:

curl -X POST 'http://localhost:9191/vpp/command?Content-Type=application/json' \
-H 'Content-Type: application/json' \
-d '{"vppclicommand":"show version"}'

"vpp v21.10.1-release built by root on 4f6ead0c141f at 2021-11-17T14:25:30\n"

Proto文件

vpp-agent/proto/ligato

Vpp插件相关功能的proto文件存放在vpp-agent/proto/ligato/vpp

Linux插件相关功能的proto文件存放在vpp-agent/proto/ligato/linux

REST API文件

url文件:vpp-agent/plugins/restapi/resturl/urls.go

处理url的代码:vpp-agent/plugins/restapi/resturl/handlers.go

BinApi 文件

vpp-agent/plugins/vpp/binapi/

在Interface插件二次开发接口

使用2110版本的vpp调用的接口是2106的接口

定义url:vpp-agent/plugins/restapi/resturl/urls.go

const (
    ...
	// zhkj开发
	ZHKJ_CreateLoopback = "/zhkj/vpp/v2/interafces/loopback"
)

注册url:vpp-agent/plugins/restapi/resturl/plugin_restapi.go

// Fill index item lists
func getIndexPageItems() map[string][]indexItem {
	idxMap := map[string][]indexItem{
		...
		"Interface plugin": {
            ...
			// zhkj开发
			{Name: "zhkj create loopback interface", Path: resturl.ZHKJ_CreateLoopback},
		},
        ...
    }
	return idxMap
}


// Create permission groups (tracer, telemetry, dump - optionally add more in the future). Used only if
// REST security is enabled in plugin
func getPermissionsGroups() []*access.PermissionGroup {
	...
	dumpPg := &access.PermissionGroup{
		Name: "dump",
		Permissions: []*access.PermissionGroup_Permissions{
			...
			// zhkj 开发
			newPermission(resturl.ZHKJ_CreateLoopback, POST),
		},
	}

	return []*access.PermissionGroup{infoPg, tracerPg, telemetryPg, dumpPg,
		nbConfigValidationPg, nbConfigReadPg, nbConfigWritePg}
}

url调用处理(测试):vpp-agent/plugins/restapi/resturl/handlers.go

// Registers interface REST handlers
func (p *Plugin) registerInterfaceHandlers() {
	...
	// zhkj 开发 POST create loopback interface
	p.registerHTTPHandler(resturl.ZHKJ_CreateLoopback, POST, func() (interface{}, error) {
		return "zhkj gogogogo", nil
	})
}

调用测试一:

lxj@lxj:~/vpp$ curl -X POST http://localhost:9191/zhkj/vpp/v2/interafces/loopback
"zhkj gogogogo"

url调用处理:vpp-agent/plugins/restapi/resturl/handlers.go

// Registers interface REST handlers
func (p *Plugin) registerInterfaceHandlers() {
	...
	// zhkj 开发 POST create loopback interface
	p.registerHTTPHandler(resturl.ZHKJ_CreateLoopback, POST, func() (interface{}, error) {
		return "success", p.ifHandler.CreateLoopbackByZhkj(context.TODO())
	})
}

在接口中声明方法:vpp-agent/plugins/vpp/ifplugin/vppcalls/interface_hander_api.go

type InterfaceVppRead interface {
	...
	// zhkj 开发 创建loopback
	CreateLoopbackByZhkj(ctx context.Context) error
}

在所有版本下实现方法:vpp-agent/plugins/vpp/ifplugin/vppcalls/vpp2106/dump_interface_vppcalls.go

// zhkj 开发
func (h *InterfaceVppHandler) CreateLoopbackByZhkj(ctx context.Context) error {
	// Dump all

	fmt.Println("2106 CreateLoopback By Zhkj")

	// 一条消息的
	reqCtx := h.callsChannel.SendRequest(&vpp_ifs.CreateLoopback{})

	reply := &vpp_ifs.CreateLoopbackReply{}
	fmt.Println(reply, reply.Retval, reply.SwIfIndex)
	
	err := reqCtx.ReceiveReply(reply)
	if err != nil {
		return err
	}

	// 多条消息的
	// reqCtx := h.callsChannel.SendMultiRequest(&vpp_ifs.CreateLoopback{})
	// for {
	// 	reply := &vpp_ifs.CreateLoopbackReply{}
	// 	over, err := reqCtx.ReceiveReply(reply)

	// 	fmt.Println(reply, reply.Retval, reply.SwIfIndex)

	// 	if over {
	// 		break
	// 	}
	// 	if err != nil {
	// 		return err
	// 	}
	// }

	return nil
}

如果之后确定是哪个版本,可以将其他版本删了

NB API的调用过程

vpp-agent/plugins/kvscheduler/*.go

txn_processprocessTransaction方法

processTransaction 分 6 步处理事务:
 1. 预处理:事务参数被初始化,重试操作从过时的操作中过滤出来,并且为了重新同步,图形被刷新
 2. 排序:使用启发式的预排序操作以获得最短的平均图游走
 3. 模拟:模拟事务而不实际执行任何创建/删除/更新操作以获得“执行计划”
 4. 预记录:在执行前记录事务参数+计划,以保存一些信息,以防执行过程中发生崩溃
 5. 执行:执行事务,收集错误
 6. Recording:记录最终确定的事务(log + in-memory)
 7. 后处理:为失败的操作安排重试,将值状态更新传播给订阅者,并将error/nil返回给阻塞提交的调用者
 8. 交易统计更新

执行过程,以以下yaml为例

vppConfig:
  interfaces:
  - name: "loop1"
    type: SOFTWARE_LOOPBACK
    enabled: true
    ipAddresses:
    - 192.168.1.1/24
  bridgeDomains:
    - name: bd1
      forward: true
      learn: true
      interfaces:
        - name: loop1

以上yaml数据将会先转为json再转为kv结构

- key: config/vpp/l2/v2/bridge-domain/bd1
  val: { name:"bd1" forward:true learn:true interfaces:{name:"loop1"} }
- key: config/vpp/v2/interfaces/loop1
  val: { name:"loop1" type:SOFTWARE_LOOPBACK enabled:true ip_addresses:"192.168.1.1/24" }

其具体执行过程为:

[BEGIN] execute transaction (seqNum=1)
  [BEGIN] applyValue (key = config/vpp/l2/v2/bridge-domain/bd1)
    [BEGIN] applyCreate (key = config/vpp/l2/v2/bridge-domain/bd1)
      -> change value state from NONEXISTENT to CONFIGURED
      [BEGIN] runDepUpdates (key = config/vpp/l2/v2/bridge-domain/bd1)
      [END] runDepUpdates (key = config/vpp/l2/v2/bridge-domain/bd1)
      [BEGIN] applyDerived (key = config/vpp/l2/v2/bridge-domain/bd1)
        [BEGIN] applyValue (key = vpp/bd/bd1/interface/loop1)
          [BEGIN] applyCreate (key = vpp/bd/bd1/interface/loop1)
            -> change value state from NONEXISTENT to PENDING
          [END] applyCreate (key = vpp/bd/bd1/interface/loop1)
        [END] applyValue (key = vpp/bd/bd1/interface/loop1)
      [END] applyDerived (key = config/vpp/l2/v2/bridge-domain/bd1)
    [END] applyCreate (key = config/vpp/l2/v2/bridge-domain/bd1)
  [END] applyValue (key = config/vpp/l2/v2/bridge-domain/bd1)
  [BEGIN] applyValue (key = config/vpp/v2/interfaces/loop1)
    [BEGIN] applyCreate (key = config/vpp/v2/interfaces/loop1)
      -> change value state from NONEXISTENT to CONFIGURED
      [BEGIN] runDepUpdates (key = config/vpp/v2/interfaces/loop1)
        [BEGIN] applyValue (key = vpp/bd/bd1/interface/loop1)
          [BEGIN] applyCreate (key = vpp/bd/bd1/interface/loop1)
            -> change value state from PENDING to CONFIGURED
            [BEGIN] runDepUpdates (key = vpp/bd/bd1/interface/loop1)
            [END] runDepUpdates (key = vpp/bd/bd1/interface/loop1)
          [END] applyCreate (key = vpp/bd/bd1/interface/loop1)
        [END] applyValue (key = vpp/bd/bd1/interface/loop1)
      [END] runDepUpdates (key = config/vpp/v2/interfaces/loop1)
      [BEGIN] applyDerived (key = config/vpp/v2/interfaces/loop1)
        [BEGIN] applyValue (key = vpp/interface/loop1/address/static/192.168.1.1/24)
          [BEGIN] applyCreate (key = vpp/interface/loop1/address/static/192.168.1.1/24)
            -> change value state from NONEXISTENT to PENDING
          [END] applyCreate (key = vpp/interface/loop1/address/static/192.168.1.1/24)
        [END] applyValue (key = vpp/interface/loop1/address/static/192.168.1.1/24)
        [BEGIN] applyValue (key = vpp/interface/loop1/has-IP-address)
          [BEGIN] applyCreate (key = vpp/interface/loop1/has-IP-address)
            -> change value state from NONEXISTENT to PENDING
          [END] applyCreate (key = vpp/interface/loop1/has-IP-address)
        [END] applyValue (key = vpp/interface/loop1/has-IP-address)
        [BEGIN] applyValue (key = vpp/interface/loop1/vrf/0/ip-version/v4)
          [BEGIN] applyCreate (key = vpp/interface/loop1/vrf/0/ip-version/v4)
            -> change value state from NONEXISTENT to CONFIGURED
            [BEGIN] runDepUpdates (key = vpp/interface/loop1/vrf/0/ip-version/v4)
              [BEGIN] applyValue (key = vpp/interface/loop1/address/static/192.168.1.1/24)
                [BEGIN] applyCreate (key = vpp/interface/loop1/address/static/192.168.1.1/24)
                  -> change value state from PENDING to CONFIGURED
                  [BEGIN] runDepUpdates (key = vpp/interface/loop1/address/static/192.168.1.1/24)
                    [BEGIN] applyValue (key = vpp/interface/loop1/has-IP-address)
                      [BEGIN] applyCreate (key = vpp/interface/loop1/has-IP-address)
                        -> change value state from PENDING to CONFIGURED
                        [BEGIN] runDepUpdates (key = vpp/interface/loop1/has-IP-address)
                        [END] runDepUpdates (key = vpp/interface/loop1/has-IP-address)
                      [END] applyCreate (key = vpp/interface/loop1/has-IP-address)
                    [END] applyValue (key = vpp/interface/loop1/has-IP-address)
                  [END] runDepUpdates (key = vpp/interface/loop1/address/static/192.168.1.1/24)
                [END] applyCreate (key = vpp/interface/loop1/address/static/192.168.1.1/24)
              [END] applyValue (key = vpp/interface/loop1/address/static/192.168.1.1/24)
            [END] runDepUpdates (key = vpp/interface/loop1/vrf/0/ip-version/v4)
          [END] applyCreate (key = vpp/interface/loop1/vrf/0/ip-version/v4)
        [END] applyValue (key = vpp/interface/loop1/vrf/0/ip-version/v4)
      [END] applyDerived (key = config/vpp/v2/interfaces/loop1)
    [END] applyCreate (key = config/vpp/v2/interfaces/loop1)
  [END] applyValue (key = config/vpp/v2/interfaces/loop1)
[END] execute transaction (seqNum=1)

具体步骤为:
1. CREATE:
	- key: config/vpp/l2/v2/bridge-domain/bd1
	- value: { name:"bd1" forward:true learn:true interfaces:{name:"loop1"} } 
2. CREATE:
	- key: config/vpp/v2/interfaces/loop1
	- value: { name:"loop1" type:SOFTWARE_LOOPBACK enabled:true ip_addresses:"192.168.1.1/24" } 
3. CREATE [DERIVED]:
	- key: vpp/bd/bd1/interface/loop1
	- value: { name:"loop1" } 
4. CREATE [DERIVED]:
	- key: vpp/interface/loop1/vrf/0/ip-version/v4
	- value:  
5. CREATE [DERIVED]:
	- key: vpp/interface/loop1/address/static/192.168.1.1/24
	- value:  
6. CREATE [DERIVED]:
	- key: vpp/interface/loop1/has-IP-address
	- value:  

以第一个创建config/vpp/l2/v2/bridge-domain/bd1为例,执行调用函数过程为:

PUT /configuration
Plugin.configurationUpdateHandler (将yaml数据转成json之后,根据json中字段转成k-v结构)
Plugin.dispatcher.PushData
txn.Commit             (将配置提交到事务队列中)

preRecordTransaction   (从事务队列中拿出执行)
Scheduler.executeTransaction
Scheduler.applyValue
Scheduler.applyCreate 
Scheduler.registry.GetDescriptorForKey          (key是config/vpp/l2/v2/bridge-domain/bd1,获取到的desriptor名字是 vpp-bridge-domain,本方法在plugins/kvsheduler/internal/registry/registry_impl.go中)
descriptorHandler.create
BridgeDomainDescriptor.Create
BridgeDomainVppHandler.AddBridgeDomain
BridgeDomainVppHandler.callsChannel.SendRequest(req).ReceiveReply(reply) (实际也是调用GoVpp)

你可能感兴趣的:(vpp-agent,vpp,json,java,网络)