Vpp私有API协议入门

  • SDN/VNF
  • 3,315 clicked

1. VPP 接口文件

vpp同控制平面交互的定义接口位于目录vpp/src/vnetvpp/src/plugins中的api文件;例如src/vnet/ip/ip_types.api文件中定义实例如下:

/* Hey Emacs use -*- mode: C -*- */
/*
 * Copyright (c) 2018 Cisco and/or its affiliates.
 * 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.
 */

option version = "3.0.0";
manual_print typedef u8 ip4_address[4];
manual_print typedef u8 ip6_address[16];

enum address_family : u8 {
  ADDRESS_IP4 = 0,
  ADDRESS_IP6,
};

/**
 * @brief The location at which to apply a feature
 */
enum ip_feature_location: u8 {
  IP_API_FEATURE_INPUT = 0,
  IP_API_FEATURE_OUTPUT,
  IP_API_FEATURE_LOCAL,
  IP_API_FEATURE_PUNT,
  IP_API_FEATURE_DROP,
};

/* ECN code points - RFC 3168
   https://tools.ietf.org/html/rfc3168
*/
enum ip_ecn : u8 {
  IP_API_ECN_NONE = 0,
  IP_API_ECN_ECT0 = 1,
  IP_API_ECN_ECT1 = 2,
  IP_API_ECN_CE = 3,
};

/* DSCP code points - RFC 2474
   https://tools.ietf.org/html/rfc2474
   Values other than these RFC defined values are accepted.
*/
enum ip_dscp : u8 {
  IP_API_DSCP_CS0 =  0,
  IP_API_DSCP_CS1 = 8,
  IP_API_DSCP_AF11 = 10,
  IP_API_DSCP_AF12 = 12,
  IP_API_DSCP_AF13 = 14,
  IP_API_DSCP_CS2 =  16,
  IP_API_DSCP_AF21 = 18,
  IP_API_DSCP_AF22 = 20,
  IP_API_DSCP_AF23 = 22,
  IP_API_DSCP_CS3 =  24,
  IP_API_DSCP_AF31 = 26,
  IP_API_DSCP_AF32 = 28,
  IP_API_DSCP_AF33 = 30,
  IP_API_DSCP_CS4 =  32,
  IP_API_DSCP_AF41 = 34,
  IP_API_DSCP_AF42 = 36,
  IP_API_DSCP_AF43 = 38,
  IP_API_DSCP_CS5 =  40,
  IP_API_DSCP_EF =   46,
  IP_API_DSCP_CS6 =  48,
  IP_API_DSCP_CS7 =  50,
};

enum ip_proto : u8 {
  IP_API_PROTO_HOPOPT = 0,
  IP_API_PROTO_ICMP = 1,
  IP_API_PROTO_IGMP = 2,
  IP_API_PROTO_TCP = 6,
  IP_API_PROTO_UDP = 17,
  IP_API_PROTO_GRE = 47,
  IP_API_PROTO_ESP = 50,
  IP_API_PROTO_AH = 51,
  IP_API_PROTO_ICMP6 = 58,
  IP_API_PROTO_EIGRP = 88,
  IP_API_PROTO_OSPF = 89,
  IP_API_PROTO_SCTP = 132,
  IP_API_PROTO_RESERVED = 255,
};

union address_union {
  vl_api_ip4_address_t ip4;
  vl_api_ip6_address_t ip6;
};

manual_print typedef address {
  vl_api_address_family_t af;
  vl_api_address_union_t un;
};

manual_print typedef prefix {
  vl_api_address_t address;
  u8 len;
};

typedef ip4_address_and_mask
{
  vl_api_ip4_address_t addr;
  vl_api_ip4_address_t mask;
};

typedef ip6_address_and_mask
{
  vl_api_ip6_address_t addr;
  vl_api_ip6_address_t mask;
};

typedef mprefix {
  vl_api_address_family_t af;
  u16 grp_address_length;
  vl_api_address_union_t grp_address;
  vl_api_address_union_t src_address;
};

manual_print typedef ip6_prefix {
  vl_api_ip6_address_t address;
  u8 len;
};

manual_print typedef ip4_prefix {
  vl_api_ip4_address_t address;
  u8 len;
};

/** \brief
 *
 * The vl_api_[ip4|ip6]_address_with_prefix_t types are used as a type to denote
 * both an IP address and a prefix. I.e. in CIDR notation
 * '192.168.10.1/24' the address is 192.168.10.1 and the network
 * prefix is 192.168.10.0/24.
 *
 * If only an address is needed use: vl_api_address_t types and if
 * only a network prefix is needed (i.e. no hosts bits), then use the
 * vl_api_prefix_t types.
 *
 **/

manual_print typedef vl_api_prefix_t address_with_prefix;
manual_print typedef vl_api_ip4_prefix_t ip4_address_with_prefix;
manual_print typedef vl_api_ip6_prefix_t ip6_address_with_prefix;

/** \brief A context for matching prefixes against.  (Think ip prefix list.)
    The meaning (exact match / want subnets) of an unset matcher is left to the implementer.
    @param le - le mut be <= to prefix.len. Default: 255 (not set).
    @param ge - ge must be greater than le and <= max_size of prefix. Default: 255 (not set).

*/
typedef prefix_matcher {
  u8 le; /* [default=255] */
  u8 ge; /* [default=255] */
};

2. VPP 接口帮助文档

生成其他类型的接口文件,需要先生成json,然后根据json生成其他类型文件

  • vpp/src/tools/vppapigen/VPPAPI.md

3. VPP 生成不同语言接口文件

3.1. json

cd /root/go/src/github.com/FDio/vpp/src/tools/vppapigen

#首先生成.json文件
mkdir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/api
python3 generate_json.py --srcdir /root/go/src/github.com/FDio/vpp/src --output /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/api --debug-target

3.2. C

/root/go/src/github.com/FDio/vpp/src/tools/vppapigen目录中有很多python文件用于转换.api为其他格式接口;这里总体入口是vppapigen.py文件,其他依赖文件主要作为依赖插件加载并执行解析;

cd /root/go/src/github.com/FDio/vpp/src/tools/vppapigen
mkdir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/c

#这里的output_module模块默认是c代码,即采用vppapigen_c.py文件执行;这里是编译ip_types.api为c文件
#--input 待转换的.api接口文件
#--outputdir 生成文件输出路径
#--includedir用于定位寻找依赖项文件的路径
python3 vppapigen.py --includedir /root/go/src/github.com/FDio/vpp/src --input /root/go/src/github.com/FDio/vpp/src/vnet/ip/ip_types.api --outputdir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/c --debug

#python3 /root/go/src/github.com/FDio/vpp/src/vpp-api/vapi/vapi_c_gen.py

3.3. Go

cd /root/go/src/github.com/FDio/vpp/src/tools/vppapigen
mkdir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/go

# 方法1.根据json文件生成其他不通语言api文件,此处生成golang文件
python3 generate_go.py -input-dir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/api -output-dir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/go

# 方法2. vpp-agne 中的govpp 项目中,也有自动生成文件;/root/go/src/github.com/ligato/govpp/cmd/binapi-generator/binapi-generator
mkdir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/go
./binapi-generator -input-dir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/api -output-dir /root/go/src/github.com/FDio/vpp/src/tools/vppapigen/go
  • generate_go.py文件生成golang代码
#!/usr/bin/env python3

import argparse
import os
import pathlib
import subprocess
import tarfile

#import requests
import sys

#
# GoVPP API generator generates Go bindings compatible with the local VPP
#

parser = argparse.ArgumentParser()
parser.add_argument("-govpp-commit", help="GoVPP commit or branch (defaults to v0.3.5-45-g671f16c)",
                    default="671f16c",  # fixed GoVPP version
                    type=str)
parser.add_argument("-output-dir", help="output target directory for generated bindings", type=str)
parser.add_argument("-api-files", help="api files to generate (without commas)", nargs="+", type=str)
parser.add_argument("-import-prefix", help="prefix imports in the generated go code", type=str)
parser.add_argument("-no-source-path-info", help="disable source path info in generated files", nargs='?', const=True,
                    default=False)
parser.add_argument('-input-dir', help="api json files dir to generate (without commas)", type=str)
args = parser.parse_args()

# Check input arguments
def validate_args(vpp_dir, o, f, c, i):
    if o is not None:
        if not os.path.exists(o) or os.path.isfile(o):
            print(o + " is not a valid output path")
            sys.exit(1)
    else:
        o = vpp_dir
    if f is None:
        f = []
    if c is None:
        c = "671f16c"
    if i is None:
        i = ""

    return str(o), f, c, i

# Returns version of the installed Go
def get_go_version(go_root):
    p = subprocess.Popen(["./go", "version"],
                         cwd=go_root + "/bin",
                         stdout=subprocess.PIPE,
                         universal_newlines=True, )
    output, _ = p.communicate()
    output_fmt = output.replace("go version go", "", 1)

    return output_fmt.rstrip("\n")

# Returns version of the installed binary API generator
def get_binapi_gen_version(go_path):
    p = subprocess.Popen(["./binapi-generator", "-version"],
                         cwd=go_path + "/bin",
                         stdout=subprocess.PIPE,
                         universal_newlines=True, )
    output, _ = p.communicate()
    output_fmt = output.replace("govpp", "", 1)

    return output_fmt.rstrip("\n")

# Verifies local Go installation and installs the latest
# one if missing
def install_golang(go_root):
    go_bin = go_root + "/bin/go"

    return 
    # if os.path.exists(go_bin) and os.path.isfile(go_bin):
    #     print('Go ' + get_go_version(go_root) + ' is already installed')
    #     return

    # print("Go binary not found, installing the latest version...")
    # go_folders = ['src', 'pkg', 'bin']

    # for f in go_folders:
    #     if not os.path.exists(os.path.join(go_root, f)):
    #         os.makedirs(os.path.join(go_root, f))

    # filename = requests.get('https://golang.org/VERSION?m=text').text + ".linux-amd64.tar.gz"
    # url = "https://dl.google.com/go/" + filename
    # r = requests.get(url)
    # with open("/tmp/" + filename, 'wb') as f:
    #     f.write(r.content)

    # go_tf = tarfile.open("/tmp/" + filename)
    # # Strip /go dir from the go_root path as it will
    # # be created while extracting the tar file
    # go_root_head, _ = os.path.split(go_root)
    # go_tf.extractall(path=go_root_head)
    # go_tf.close()
    # os.remove("/tmp/" + filename)

    # print('Go ' + get_go_version(go_root) + ' was installed')

# Installs latest binary API generator
def install_binapi_gen(c, go_root, go_path):
    os.environ['GO111MODULE'] = "on"
    if os.path.exists(go_root + "/bin/go") & os.path.isfile(go_root + "/bin/go"):
        p = subprocess.Popen(["./go", "get", "git.fd.io/govpp.git/cmd/binapi-generator@" + c],
                             cwd=go_root + "/bin",
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             universal_newlines=True, )
        _, error = p.communicate()
        if p.returncode != 0:
            print("binapi generator installation failed: %d %s" % (p.returncode, error))
            sys.exit(1)
    bg_ver = get_binapi_gen_version(go_path)
    print('Installed binary API generator ' + bg_ver)

# Creates generated bindings using GoVPP binapigen to the target folder
def generate_api(output_dir, json_dir, api_list, import_prefix, no_source, go_path):
    output_binapi = output_dir + "binapi" if output_dir[-1] == "/" else output_dir + "/binapi"
    if not os.path.exists(json_dir):
        print("Missing JSON api definitions")
        sys.exit(1)

    print("Generating API")
    cmd = ["./binapi-generator", "--output-dir=" + output_binapi, "--input-dir=" + json_dir]
    if len(api_list):
        print("Following API files were requested by 'GO_API_FILES': " + str(api_list))
        print("Note that dependency requirements may generate additional API files")
        cmd.append(api_list)
    if not import_prefix == "":
        cmd.append("-import-prefix=" + import_prefix)
    if no_source:
        cmd.append("-no-source-path-info")
    p = subprocess.Popen(cmd, cwd=go_path + "/bin",
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         universal_newlines=True, )

    out = p.communicate()[1]
    if p.returncode != 0:
        print("go api generate failed: %d %s" % (p.returncode, out))
        sys.exit(1)

    # Print nice output of the binapi generator
    for msg in out.split():
        if "=" in msg:
            print()
        print(msg, end=" ")

    print("\n")
    print("Go API bindings were generated to " + output_binapi)

def main():
    # project root directory
    root = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
    vpp_dir: str = root.parent.parent.parent

    o, f, c, i = validate_args(vpp_dir, args.output_dir, args.api_files, args.govpp_commit,
                               args.import_prefix)

    # go specific environment variables
    if "GOROOT" in os.environ:
        go_root = os.environ['GOROOT']
    else:
        go_root = os.environ['HOME'] + "/.go"
    if "GOPATH" in os.environ:
        go_path = os.environ['GOPATH']
    else:
        go_path = os.environ['HOME'] + "/go"

    install_golang(go_root)
    install_binapi_gen(c, go_root, go_path)

    json_dir = args.input_dir
    generate_api(o, json_dir, f, i, args.no_source_path_info, go_path)

if __name__ == "__main__":
    main()

发表评论

邮箱地址不会被公开。 必填项已用*标注