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