docs: Improve new plugin doc & add govpp API doc
Type: docs Change-Id: I5f20ac0232c5cdc3cf64015185b0d0fc5c4a3100 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
This commit is contained in:

committed by
Andrew Yourtchenko

parent
f681e9f736
commit
c4781a32df
1
docs/gettingstarted/developers/VPPAPI.md
Symbolic link
1
docs/gettingstarted/developers/VPPAPI.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../src/tools/vppapigen/VPPAPI.md
|
@ -7,7 +7,7 @@ Adding a plugin
|
|||||||
|
|
||||||
Overview
|
Overview
|
||||||
________
|
________
|
||||||
|
|
||||||
This section shows how a VPP developer can create a new plugin, and
|
This section shows how a VPP developer can create a new plugin, and
|
||||||
add it to VPP. We assume that we are starting from the VPP <top-of-workspace>.
|
add it to VPP. We assume that we are starting from the VPP <top-of-workspace>.
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ Create your new plugin
|
|||||||
Change directory to **./src/plugins**, and run the plugin generator:
|
Change directory to **./src/plugins**, and run the plugin generator:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ cd ./src/plugins
|
$ cd ./src/plugins
|
||||||
$ ../../extras/emacs/make-plugin.sh
|
$ ../../extras/emacs/make-plugin.sh
|
||||||
<snip>
|
<snip>
|
||||||
@ -36,7 +36,7 @@ Change directory to **./src/plugins**, and run the plugin generator:
|
|||||||
Plugin name: myplugin
|
Plugin name: myplugin
|
||||||
Dispatch type [dual or qs]: dual
|
Dispatch type [dual or qs]: dual
|
||||||
(Shell command succeeded with no output)
|
(Shell command succeeded with no output)
|
||||||
|
|
||||||
OK...
|
OK...
|
||||||
|
|
||||||
The plugin generator script asks two questions: the name of the
|
The plugin generator script asks two questions: the name of the
|
||||||
@ -65,9 +65,8 @@ Here are the generated files. We'll go through them in a moment.
|
|||||||
|
|
||||||
$ cd ./myplugin
|
$ cd ./myplugin
|
||||||
$ ls
|
$ ls
|
||||||
CMakeLists.txt myplugin.c myplugin_periodic.c setup.pg
|
CMakeLists.txt myplugin.api myplugin.c myplugin.h
|
||||||
myplugin_all_api_h.h myplugin.h myplugin_test.c
|
myplugin_periodic.c myplugin_test.c node.c setup.pg
|
||||||
myplugin.api myplugin_msg_enum.h node.c
|
|
||||||
|
|
||||||
Due to recent build system improvements, you **don't** need to touch
|
Due to recent build system improvements, you **don't** need to touch
|
||||||
any other files to integrate your new plugin into the vpp build. Simply
|
any other files to integrate your new plugin into the vpp build. Simply
|
||||||
@ -92,7 +91,7 @@ As a quick sanity check, run vpp and make sure that
|
|||||||
"myplugin_plugin.so" and "myplugin_test_plugin.so" are loaded:
|
"myplugin_plugin.so" and "myplugin_test_plugin.so" are loaded:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ cd <top-of-workspace>
|
$ cd <top-of-workspace>
|
||||||
$ make run
|
$ make run
|
||||||
<snip>
|
<snip>
|
||||||
@ -122,25 +121,21 @@ the copyright notice:
|
|||||||
|
|
||||||
The rest of the build recipe is pretty simple:
|
The rest of the build recipe is pretty simple:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: CMake
|
||||||
|
|
||||||
add_vpp_plugin (myplugin
|
add_vpp_plugin (myplugin
|
||||||
SOURCES
|
SOURCES
|
||||||
myplugin.c
|
myplugin.c
|
||||||
node.c
|
node.c
|
||||||
myplugin_periodic.c
|
myplugin_periodic.c
|
||||||
myplugin.h
|
myplugin.h
|
||||||
|
|
||||||
MULTIARCH_SOURCES
|
MULTIARCH_SOURCES
|
||||||
node.c
|
node.c
|
||||||
|
|
||||||
API_FILES
|
API_FILES
|
||||||
myplugin.api
|
myplugin.api
|
||||||
|
|
||||||
INSTALL_HEADERS
|
|
||||||
myplugin_all_api_h.h
|
|
||||||
myplugin_msg_enum.h
|
|
||||||
|
|
||||||
API_TEST_SOURCES
|
API_TEST_SOURCES
|
||||||
myplugin_test.c
|
myplugin_test.c
|
||||||
)
|
)
|
||||||
@ -178,13 +173,13 @@ binary API message dispatcher, and to add its messages to vpp's global
|
|||||||
Vpp itself uses dlsym(...) to track down the vlib_plugin_registration_t
|
Vpp itself uses dlsym(...) to track down the vlib_plugin_registration_t
|
||||||
generated by the VLIB_PLUGIN_REGISTER macro:
|
generated by the VLIB_PLUGIN_REGISTER macro:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: C
|
||||||
|
|
||||||
VLIB_PLUGIN_REGISTER () =
|
VLIB_PLUGIN_REGISTER () =
|
||||||
{
|
{
|
||||||
.version = VPP_BUILD_VER,
|
.version = VPP_BUILD_VER,
|
||||||
.description = "myplugin plugin description goes here",
|
.description = "myplugin plugin description goes here",
|
||||||
};
|
};
|
||||||
|
|
||||||
Vpp only loads .so files from the plugin directory which contain an
|
Vpp only loads .so files from the plugin directory which contain an
|
||||||
instance of this data structure.
|
instance of this data structure.
|
||||||
@ -193,7 +188,7 @@ You can enable or disable specific vpp plugins from the command
|
|||||||
line. By default, plugins are loaded. To change that behavior, set
|
line. By default, plugins are loaded. To change that behavior, set
|
||||||
default_disabled in the macro VLIB_PLUGIN_REGISTER:
|
default_disabled in the macro VLIB_PLUGIN_REGISTER:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: C
|
||||||
|
|
||||||
VLIB_PLUGIN_REGISTER () =
|
VLIB_PLUGIN_REGISTER () =
|
||||||
{
|
{
|
||||||
@ -205,14 +200,14 @@ default_disabled in the macro VLIB_PLUGIN_REGISTER:
|
|||||||
The boilerplate generator places the graph node dispatch function
|
The boilerplate generator places the graph node dispatch function
|
||||||
onto the "device-input" feature arc. This may or may not be useful.
|
onto the "device-input" feature arc. This may or may not be useful.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: C
|
||||||
|
|
||||||
VNET_FEATURE_INIT (myplugin, static) =
|
VNET_FEATURE_INIT (myplugin, static) =
|
||||||
{
|
{
|
||||||
.arc_name = "device-input",
|
.arc_name = "device-input",
|
||||||
.node_name = "myplugin",
|
.node_name = "myplugin",
|
||||||
.runs_before = VNET_FEATURES ("ethernet-input"),
|
.runs_before = VNET_FEATURES ("ethernet-input"),
|
||||||
};
|
};
|
||||||
|
|
||||||
As given by the plugin generator, myplugin.c contains the binary API
|
As given by the plugin generator, myplugin.c contains the binary API
|
||||||
message handler for a generic "please enable my feature on such and
|
message handler for a generic "please enable my feature on such and
|
||||||
@ -243,20 +238,53 @@ node.c
|
|||||||
This is the generated graph node dispatch function. You'll need to
|
This is the generated graph node dispatch function. You'll need to
|
||||||
rewrite it to solve the problem at hand. It will save considerable
|
rewrite it to solve the problem at hand. It will save considerable
|
||||||
time and aggravation to retain the **structure** of the node dispatch
|
time and aggravation to retain the **structure** of the node dispatch
|
||||||
function.
|
function.
|
||||||
|
|
||||||
Even for an expert, it's a waste of time to reinvent the *loop
|
Even for an expert, it's a waste of time to reinvent the *loop
|
||||||
structure*, enqueue patterns, and so forth. Simply tear out and
|
structure*, enqueue patterns, and so forth. Simply tear out and
|
||||||
replace the specimen 1x, 2x, 4x packet processing code with code
|
replace the specimen 1x, 2x, 4x packet processing code with code
|
||||||
relevant to the problem you're trying to solve.
|
relevant to the problem you're trying to solve.
|
||||||
|
|
||||||
|
myplugin.api
|
||||||
|
------------
|
||||||
|
|
||||||
|
This contains the API message definition. Here we only have defined
|
||||||
|
a single one named ``myplugin_enable_disable`` and an implicit
|
||||||
|
``myplugin_enable_disable_reply`` containing only a return value due
|
||||||
|
to the ``autoreply`` keyword.
|
||||||
|
|
||||||
|
The syntax reference for ``.api`` files can be found at VPP API Language
|
||||||
|
|
||||||
|
Addressing the binary API with this message will run the handler defined
|
||||||
|
in ``myplugin.c`` as ``vl_api_myplugin_enable_disable_t_handler``.
|
||||||
|
It will receive a message pointer ``*mp`` which is the struct defined
|
||||||
|
in ``myplugin.api`` and should return another message pointer ``*rmp``,
|
||||||
|
of the reply type. That's what ``REPLY_MACRO`` does.
|
||||||
|
|
||||||
|
To be noted, all API messages are in net-endian and vpp is host-endian,
|
||||||
|
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>`
|
||||||
|
|
||||||
|
myplugin_periodic.c
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
This defines a VPP process, a routine that will run indefinitely and
|
||||||
|
be woken up intermittently, here to process plugin events.
|
||||||
|
|
||||||
|
To be noted, vlib_processes aren't thread-safe, and data structures
|
||||||
|
should be locked when shared between workers.
|
||||||
|
|
||||||
Plugin "Friends with Benefits"
|
Plugin "Friends with Benefits"
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
In vpp VLIB_INIT_FUNCTION functions, It's reasonably common to see a
|
In vpp VLIB_INIT_FUNCTION functions, It's reasonably common to see a
|
||||||
specific init function invoke other init functions:
|
specific init function invoke other init functions:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: C
|
||||||
|
|
||||||
if ((error = vlib_call_init_function (vm, some_other_init_function))
|
if ((error = vlib_call_init_function (vm, some_other_init_function))
|
||||||
return error;
|
return error;
|
||||||
@ -264,7 +292,7 @@ specific init function invoke other init functions:
|
|||||||
In the case where one plugin needs to call a init function in another
|
In the case where one plugin needs to call a init function in another
|
||||||
plugin, use the vlib_call_plugin_init_function macro:
|
plugin, use the vlib_call_plugin_init_function macro:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: C
|
||||||
|
|
||||||
if ((error = vlib_call_plugin_init_function (vm, "otherpluginname", some_init_function))
|
if ((error = vlib_call_plugin_init_function (vm, "otherpluginname", some_init_function))
|
||||||
return error;
|
return error;
|
||||||
@ -274,7 +302,7 @@ This allows sequencing between plugin init functions.
|
|||||||
If you wish to obtain a pointer to a symbol in another plugin, use the
|
If you wish to obtain a pointer to a symbol in another plugin, use the
|
||||||
vlib_plugin_get_symbol(...) API:
|
vlib_plugin_get_symbol(...) API:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: C
|
||||||
|
|
||||||
void *p = vlib_get_plugin_symbol ("plugin_name", "symbol");
|
void *p = vlib_get_plugin_symbol ("plugin_name", "symbol");
|
||||||
|
|
||||||
|
83
docs/gettingstarted/developers/add_plugin_goapi.rst
Normal file
83
docs/gettingstarted/developers/add_plugin_goapi.rst
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
.. _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
|
@ -22,6 +22,7 @@ The Developers section covers the following areas:
|
|||||||
running_vpp
|
running_vpp
|
||||||
gdb_examples
|
gdb_examples
|
||||||
add_plugin
|
add_plugin
|
||||||
|
add_plugin_goapi
|
||||||
gitreview
|
gitreview
|
||||||
softwarearchitecture
|
softwarearchitecture
|
||||||
infrastructure
|
infrastructure
|
||||||
@ -42,3 +43,4 @@ The Developers section covers the following areas:
|
|||||||
quic_plugin
|
quic_plugin
|
||||||
cross_compile_macos.rst
|
cross_compile_macos.rst
|
||||||
cnat
|
cnat
|
||||||
|
VPPAPI.md
|
||||||
|
Reference in New Issue
Block a user