diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/developer/plugindoc/add_plugin.rst | 2 | ||||
-rw-r--r-- | docs/interfacing/go/add_plugin_goapi.rst | 83 | ||||
-rw-r--r-- | docs/interfacing/go/index.rst | 176 |
3 files changed, 174 insertions, 87 deletions
diff --git a/docs/developer/plugindoc/add_plugin.rst b/docs/developer/plugindoc/add_plugin.rst index cf771116095..f0632380f34 100644 --- a/docs/developer/plugindoc/add_plugin.rst +++ b/docs/developer/plugindoc/add_plugin.rst @@ -317,7 +317,7 @@ so you will need to use : * ``u32 value = ntohl(mp->value);`` * ``rmp->value = htonl(value);`` -You can now use this API with :ref:`GoLang bindings <add_plugin_goapi>` +You can now use this API with :ref:`GoLang bindings <govpp>` myplugin_periodic.c ------------------- diff --git a/docs/interfacing/go/add_plugin_goapi.rst b/docs/interfacing/go/add_plugin_goapi.rst deleted file mode 100644 index dce35b8f0a2..00000000000 --- a/docs/interfacing/go/add_plugin_goapi.rst +++ /dev/null @@ -1,83 +0,0 @@ -.. _add_plugin_goapi: - -Add a plugin's GO API -===================== - -In order to use your plugin's API with GO, you will need to use -a GO client and GO definitions of the API messages that you defined -in ``myplugin.api`` (go bindings). - -These two things can be found in `govpp <https://github.com/FDio/govpp>`_ - -* The API client lives in `./core` -* The api-generator lives in `./binapigen` -* A sample of its output (the go bindings) for VPP's latest version lives in `./binapi` - -To generate the go bindings for your plugin. Assuming : -* ``/home/vpp`` is a VPP clone with your plugin in it. -* ``/home/controlplane`` is a go controlplane repo - -.. code-block:: console - - $ mkdir /home/controlplane/vpp-go-bindings - $ git clone https://github.com/FDio/govpp> - $ cd govpp - $ BINAPI_DIR=/home/controlplane/vpp-go-bindings VPP_DIR=/home/vpp make gen-binapi-from-code - -This will generate the go-bindings in ``/home/controlplane/vpp-go-bindings`` -For example ``vpp-go-bindings/myplugin/myplugin.ba.go`` will contain : - -.. code-block:: go - - // MypluginEnableDisable defines message 'myplugin_enable_disable'. - type MypluginEnableDisable struct { - EnableDisable bool `binapi:"bool,name=enable_disable" json:"enable_disable,omitempty"` - SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` - } - - -You can then use the generated go bindings in your go code like this : - -.. code-block:: go - - package main - - import ( - "fmt" - "git.fd.io/govpp.git" - "git.fd.io/govpp.git/binapi/interfaces" - "git.fd.io/govpp.git/binapi/vpe" - - "myplugin.io/controlplane/vpp-go-bindings/myplugin/myplugin" - ) - - func main() { - // Connect to VPP - conn, _ := govpp.Connect("/run/vpp/api.sock") - defer conn.Disconnect() - - // Open channel - ch, _ := conn.NewAPIChannel() - defer ch.Close() - - request := &vpe.MypluginEnableDisable{ - EnableDisable: true, - } - reply := &vpe.MypluginEnableDisableReply{} - - err := ch.SendRequest(request).ReceiveReply(reply) - if err != nil { - fmt.Errorf("SendRequest: %w\n", err) - } - } - -As you will need to import (or ``go get "git.fd.io/govpp.git"``) to leverage the API -client in your code, you might want to use the api-generator directly from the -clone ``go build`` fetches for you. You can do this with : - -.. code-block:: console - - $ export GOVPP_DIR=$(go list -f '{{.Dir}}' -m git.fd.io/govpp.git) - $ cd $GOVPP_DIR && go build -o /some/bin/dir ./cmd/binapi-generator - $ # instead of make gen-binapi-from-code you can rewrite the code to target - $ # your version ./binapi-generator diff --git a/docs/interfacing/go/index.rst b/docs/interfacing/go/index.rst index 68c01e086b0..6675853dda8 100644 --- a/docs/interfacing/go/index.rst +++ b/docs/interfacing/go/index.rst @@ -4,7 +4,177 @@ Go api (govpp) ============== -.. toctree:: - :maxdepth: 2 +If you are writing a Control plane in GO that interfaces with VPP, `GoVPP <https://github.com/FDio/govpp>`__ is the library that will allow you to connect to VPP, and program it through its binary API socket. + +Components involved +=================== + +The API client connecting to VPP consists of several elements : + +* First, everything stems from the api definition living in the VPP repository. The message definitions live in ``*.api`` files that you will find instances of in most VPP plugins. +* The repository contains an api generator ``make json-api-files`` that converts those ``.api`` files into ``.json`` files to be consumed by language specific bindings. +* The program ingesting these ``.json`` files is called ``binapi-generator`` and lives inside `GoVPP <https://github.com/FDio/govpp>`__. It contains the logic converting them to ``.ba.go`` files with the appropriate struct definitions matching all the api messages defined in the ``.api`` files. +* `GoVPP <https://github.com/FDio/govpp>`__'s repo also contains the logic for attaching to VPP's binary API socket, and wrappers for sending and receiving messages over it. + +Getting started +=============== + +Generating the API bindings from the VPP source +----------------------------------------------- + +* First create your project directory (watch out for path as it is important for go modules) : + +.. code:: bash + + mkdir -p $HOME/myproject + +* Run the bindings generation at the root of the repo : + +.. code:: bash + + cd <vpp_repo_dir>/vpp + make ARGS="--output-dir=$HOME/myproject/vppbinapi --import-prefix=mygit.com/myproject/vppbinapi" go-api-files + + +.. note:: + The two options are similar but specify two different things. The output-dir option sets the directory where the generated bindings will be stored. The import prefix sets the go package name to be used in the generated bindings, this will be the string to be used in your ``import ( "" )`` in go. Both can or can not match depending on your ``go.mod``. + + +This should prompt you with the name of the directory were the generated go api bindings live. (e.g. ``Go API bindings were generated to myproject/vppbinapi``) + +Generating the API bindings from the VPP package +------------------------------------------------ + +* You should find its corresponding ``api.json`` files present on your system, typically in ``/usr/share/vpp/api/`` + +.. code:: bash + + # First install the binary API generator + # It will be installed to $GOPATH/bin/binapi-generator + # or $HOME/go/bin/binapi-generator + go install git.fd.io/govpp.git/cmd/binapi-generator@latest + + # Run the binapi-generator + $GOPATH/bin/binapi-generator \ + --input-dir=/usr/share/vpp/api/ \ + --output-dir=$HOME/myproject/vppbinapi \ + --import-prefix=mygit.com/myproject/vppbinapi + +This should output the go bindings to ``$HOME/myproject/vppbinapi`` + +Launch VPP +========== + +.. code:: bash + + mkdir -p /tmp/vpp + cat << EOF > /tmp/startup.conf + unix {nodaemon cli-listen /tmp/vpp/api.sock} + plugins { + path /vpp/build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/vpp_plugins + plugin dpdk_plugin.so { disable } + } + EOF + + # If VPP was built from source: + <vpp_repo_dir>/build-root/install-vpp_debug-native/vpp/bin/vpp -c /tmp/startup.conf + + # If VPP was installed from package: + vpp -c /tmp/startup.conf + + +Connecting to VPP +================= + +Once you have your go bindings in ``$HOME/myproject/vppbinapi``, you can start building an agent leveraging them. A typical agent would look like this + +* Back to your project directory, add govpp as a dependency + +.. code:: bash + + cd "$HOME/myproject" + go mod init mygit.com/myproject + go get git.fd.io/govpp.git@latest + +* Create ``main.go`` in ``$HOME/myproject`` like below : + +.. code-block:: go + + package main + + import ( + "os" + "fmt" + + "git.fd.io/govpp.git" + "git.fd.io/govpp.git/api" + + "mygit.com/myproject/vppbinapi/af_packet" + interfaces "mygit.com/myproject/vppbinapi/interface" + "mygit.com/myproject/vppbinapi/interface_types" + ) + + func CreateHostInterface (ch api.Channel, ifName string) (uint32, error) { + response := &af_packet.AfPacketCreateReply{} + request := &af_packet.AfPacketCreate{HostIfName: ifName} + err := ch.SendRequest(request).ReceiveReply(response) + if err != nil { + return 0, err + } else if response.Retval != 0 { + return 0, fmt.Errorf("AfPacketCreate failed: req %+v reply %+v", request, response) + } + return uint32(response.SwIfIndex), nil + } + + func InterfaceAdminUp(ch api.Channel, swIfIndex uint32) error { + request := &interfaces.SwInterfaceSetFlags{ + SwIfIndex: interface_types.InterfaceIndex(swIfIndex), + Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, + } + response := &interfaces.SwInterfaceSetFlagsReply{} + err := ch.SendRequest(request).ReceiveReply(response) + if err != nil { + return err + } + return nil + } + + func main() { + // Connect to VPP + conn, err := govpp.Connect("/tmp/vpp/api.sock") + defer conn.Disconnect() + if err != nil { + fmt.Printf("Could not connect: %s\n", err) + os.Exit(1) + } + + // Open channel + ch, err := conn.NewAPIChannel() + defer ch.Close() + if err != nil { + fmt.Printf("Could not open API channel: %s\n", err) + os.Exit(1) + } + + swIfIndex, err := CreateHostInterface(ch, "eth0") + if err != nil { + fmt.Printf("Could not create host interface: %s\n", err) + os.Exit(1) + } + err = InterfaceAdminUp(ch, swIfIndex) + if err != nil { + fmt.Printf("Could not set interface up: %s\n", err) + os.Exit(1) + } + + fmt.Printf("Created host interface & set it up, id=%d\n", swIfIndex) + } + +* Finally build and launch application. This will connect to VPP on its API socket ``/tmp/vpp/api.sock``, create an AF_PACKET interface on ``eth0`` and set it up + +.. code:: bash + + cd "$HOME/myproject" + go build + ./myproject - add_plugin_goapi |