1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
.. _vapi_doc:
VPP API module
==============
Overview
--------
VPP API module allows communicating with VPP over shared memory
interface. The API consists of 3 parts:
- common code - low-level API
- generated code - high-level API
- code generator - to generate your own high-level API e.g. for custom
plugins
Common code
~~~~~~~~~~~
C common code
^^^^^^^^^^^^^
C common code represents the basic, low-level API, providing functions
to connect/disconnect, perform message discovery and send/receive
messages. The C variant is in vapi.h.
.. _c-common-code-1:
C++ common code
^^^^^^^^^^^^^^^
C++ is provided by vapi.hpp and contains high-level API templates, which
are specialized by generated code.
Generated code
~~~~~~~~~~~~~~
Each API file present in the source tree is automatically translated to
JSON file, which the code generator parses and generates either C
(``vapi_c_gen.py``) or C++ (``vapi_cpp_gen.py``) code.
This can then be included in the client application and provides
convenient way to interact with VPP. This includes:
- automatic byte-swapping
- automatic request-response matching based on context
- automatic casts to appropriate types (type-safety) when calling
callbacks
- automatic sending of control-pings for dump messages
The API supports two modes of operation:
- blocking
- non-blocking
In blocking mode, whenever an operation is initiated, the code waits
until it can finish. This means that when sending a message, the call
blocks until the message can be written to shared memory. Similarly,
receiving a message blocks until a message becomes available. On higher
level, this also means that when doing a request
(e.g. ``show_version``), the call blocks until a response comes back
(e.g. ``show_version_reply``).
In non-blocking mode, these are decoupled, the API returns VAPI_EAGAIN
whenever an operation cannot be performed and after sending a request,
it’s up to the client to wait for and process a response.
Code generator
~~~~~~~~~~~~~~
Python code generator comes in two flavors - C and C++ and generates
high-level API headers. All the code is stored in the headers.
Usage
-----
Low-level API
~~~~~~~~~~~~~
Refer to inline API documentation in doxygen format in ``vapi.h`` header
for description of functions. It’s recommended to use the safer,
high-level API provided by specialized headers (e.g. ``vpe.api.vapi.h``
or ``vpe.api.vapi.hpp``).
C high-level API
^^^^^^^^^^^^^^^^
Callbacks
'''''''''
The C high-level API is strictly callback-based for maximum efficiency.
Whenever an operation is initiated a callback with a callback context is
part of that operation. The callback is then invoked when the response
(or multiple responses) arrive which are tied to the request. Also,
callbacks are invoked whenever an event arrives, if such callback is
registered. All the pointers to responses/events point to shared memory
and are immediately freed after callback finishes so the client needs to
extract/copy any data in which it is interested in.
Blocking mode
^^^^^^^^^^^^^
In simple blocking mode, the whole operation (being a simple request or
a dump) is finished and it’s callback is called (potentially multiple
times for dumps) during function call.
Example pseudo-code for a simple request in this mode:
\` vapi_show_version(message, callback, callback_context)
1. generate unique internal context and assign it to
message.header.context
2. byteswap the message to network byte order
3. send message to vpp (message is now consumed and vpp will free it)
4. create internal “outstanding request context” which stores the
callback, callback context and the internal context value
5. call dispatch, which in this mode receives and processes responses
until the internal “outstanding requests” queue is empty. In blocking
mode, this queue always contains at most one item. \`
**Note**: it’s possible for different - unrelated callbacks to be called
before the response callbacks is called in cases where e.g. events are
stored in shared memory queue.
Non-blocking mode
^^^^^^^^^^^^^^^^^
In non-blocking mode, all the requests are only byte-swapped and the
context information along with callbacks is stored locally (so in the
above example, only steps 1-4 are executed and step 5 is skipped).
Calling dispatch is up to the client application. This allows to
alternate between sending/receiving messages or have a dedicated thread
which calls dispatch.
.. _c-high-level-api-1:
C++ high level API
~~~~~~~~~~~~~~~~~~
.. _callbacks-1:
Callbacks
^^^^^^^^^
In C++ API, the response is automatically tied to the corresponding
``Request``, ``Dump`` or ``Event_registration`` object. Optionally a
callback might be specified, which then gets called when the response is
received.
**Note**: responses take up shared memory space and should be freed
either manually (in case of result sets) or automatically (by destroying
the object owning them) when no longer needed. Once a Request or Dump
object was executed, it cannot be re-sent, since the request itself
(stores in shared memory) is consumed by vpp and inaccessible (set to
nullptr) anymore.
.. _usage-1:
Usage
^^^^^
Requests & dumps
^^^^^^^^^^^^^^^^
0. Create on object of ``Connection`` type and call ``connect()`` to
connect to vpp.
1. Create an object of ``Request`` or ``Dump`` type using it’s typedef
(e.g. ``Show_version``)
2. Use ``get_request()`` to obtain and manipulate the underlying request
if required.
3. Issue ``execute()`` to send the request.
4. Use either ``wait_for_response()`` or ``dispatch()`` to wait for the
response.
5. Use ``get_response_state()`` to get the state and ``get_response()``
to read the response.
Events
^^^^^^
0. Create a ``Connection`` and execute the appropriate ``Request`` to
subscribe to events (e.g. ``Want_stats``)
1. Create an ``Event_registration`` with a template argument being the
type of event you are interested in.
2. Call ``dispatch()`` or ``wait_for_response()`` to wait for the event.
A callback will be called when an event occurs (if passed to
``Event_registration()`` constructor). Alternatively, read the result
set.
**Note**: events stored in the result set take up space in shared memory
and should be freed regularly (e.g. in the callback, once the event is
processed).
|