VPP-Agent MP2
MP2 接口的开发通过 VPP-Agent 进行开发,使用的是 UPF 通过 Watch VPP-Agent 的 KV 数据库来进行管理的。
架构
分两种接口,大多数通过 restful API 下发到数据库,然后 watch 数据库变化通过 binary api 下发给 UPF;第二种查询类接口,如 UE INFO 和 UE 位置信息直接通过 binary api 下发给 UPF。
VPP-Agent 北向接口
VPP-Agent 原生支持 Restful 的方法。通过使用 gorilla/mux 框架提供的基础 http 服务
那么,我们需要为 UPF Plugin 加 restful 的注册。
在 UPF Plugin 注册时为其添加 HTTP Handler
plugins/vpp/upfplugin/upfplugin.go
type Deps struct {
......
HTTPHandlers rest.HTTPHandlers
}
func (p *UpfPlugin) AfterInit() error {
......
p.registerHandlers(p.HTTPHandlers) /// 注册对应 url 的方法
}
plugins/vpp/upfplugin/options.go
func NewPlugin(opts ...Option) *UpfPlugin {
......
p.HTTPHandlers = &rest.DefaultPlugin
......
}
VPP-Agent 注册 URL
/plugins/vpp/upfplugin/ 下新建文件 rest_api.go
利用 http.RegisterHTTPHandler 注册 URL ,依次是 url,执行方法,CRUD。
func (p *UpfPlugin) registerHandlers(http rest.HTTPHandlers) {
// Register mp2 traffic_rule
http.RegisterHTTPHandler(mp2 + traffic_rule, p.mp2GetTrafficRuleHandler, "GET")
// Register ue info
http.RegisterHTTPHandler(mp2 + "/ueinformation/query/ue_id"
}
然后在定义方法即可,所有接口暂定写在此文件
根据方法中的调度,可以选择通过 p.RedisBroker 读写数据库,也可以通过 p.upfHandler 直接调用 UPF API
VPP-Agent 所调用的 UPF API
UPF 编译安装后在 /usr/share/vpp/api/plugins 提供 upf.api.json 文件,可以通过 binapi-genreate 命令生成 接口使用代码 upf.ba.go,里面定义了调接口时 request 和 reply 的结构体。
如 UE Information 的结构体
type UpfUeInformation struct {
UeidType string `binapi:"string[32],name=ueid_type" json:"ueid_type,omitempty"`
UeidValue string `binapi:"string[32],name=ueid_value" json:"ueid_value,omitempty"`
}
在调用时,通过此结构体初始化
VPP-Agent 调用处理后数据
北向 Restful API 收到数据后,根据之前 upf.ba.go 结构体创建 upf model 部分代码,需要先修改
proto/ligato/vpp/upf/pfcp.proto
message UpfUeInformation
{
bytes UeidType = 1;
bytes UeidValue = 2;
}
然后通过 make generate-proto 命令生成 pfcp.pb.go
VPP-Agent 的 Call 操作
修改 /plugins/vpp/upfplugin/vppcalls/vpp2009/upf_vppcalls.go 添加具体的发送方法,将 pfcp.pb.go 中数据复制给 upf.ba.go 里的结构体中,然后发送给 UPF
func (h *UpfVppHandler) VppGetUeInfo(entry *models.UpfUeInformation) (*vppcalls.UeInformationRep, error) {
......
request := &upf.UpfUeInformation{}
reply := &upf.UpfUeInformationReply{}
Copy(request, entry)
err = h.callsChannel.SendRequest(request).ReceiveReply(reply)
}
然后将该方法 注册到 type UpfPFCPVppAPI 中
修改 /plugins/vpp/upfplugin/vppcalls/upf_vppcalls.go
type UpfPFCPVppAPI interface {
......
VppGetUeInfo(entry *upf.UpfUeInformation) (*UeInformationRep, error)
}
UE 信息查询实现
需求
通过 MP2 接口输入 过滤的type 和具体数据进行 UE 的信息查询
具体方法:https://{ip}:{port}/mp2/v1/meps/{mepId}/ueinformation/query/ue_id?type={ueIdType}&value={ueIdValue}
需要解析 type 和 value 的值
北向 Restful
/plugins/vpp/upfplugin/rest_api.go
func (p *UpfPlugin) registerHandlers(http rest.HTTPHandlers) {
http.RegisterHTTPHandler(mp2 + ueInfo, p.mp2GetUeInfoHandler, "GET")
}
通过 args := req.URL.Query() 获取到具体参数,然后通过
entry := &models.UpfUeInformation{
UeidType: []byte(ueIdType),
UeidValue: []byte(ueIdValue),
}
组装具体请求结构体,进行请求,返回 ueInfo
ueInfo, err := p.upfHandler.VppGetUeInfo(entry)
返回结果
如果查询不到,返回对应查询错误,目前情况只报
"500 Internal server error: sending request failed: VppGetUeInfo error VPPApiError: Unspecified Error (-1)\n"
如果查询到,则返回 200 和 正确结果
# curl -X GET "http://127.0.0.1:9191/mp2/v1/meps/123/ueinformation/query/ue_id?type=ipaddress&value=172.20.231.1"
{
"address": "172.20.231.1",
"supi": "466920100001101"
}