aboutsummaryrefslogtreecommitdiffstats
path: root/src/tools/vppapigen/VPPAPI.rst
blob: e8144803a8732fae0e14c3c9e1ad6df087b05228 (plain)
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
VPP API Language
================

The VPP binary API is a message passing API. The VPP API language is
used to define a RPC interface between VPP and its control plane. The
API messages supports shared memory transport and Unix domain sockets
(SOCK_STREAM).

The wire format is essentially that of a network formatted (big-endian)
packed C struct.

The VPP API compiler is located in *src/tools/vppapigen* and can
currently compile to JSON or C (used by the VPP binary itself).

Language definition
-------------------

Defining a messages
~~~~~~~~~~~~~~~~~~~

There are 3 types of message exchanges:

-  Request/Reply The client sends a request message and the server
   replies with a single reply message. The convention is that the reply
   message is named as method_name + \_reply.

-  Dump/Detail The client sends a “bulk” request message to the server,
   and the server replies with a set of detail messages. These messages
   may be of different type. A dump/detail call must be enclosed in a
   control ping block (Otherwise the client will not know the end of the
   bulk transmission). The method name must end with method + “\_dump”,
   the reply message should be named method + “\_details”. The exception
   here is for the methods that return multiple message types
   (e.g. sw_interface_dump). The Dump/Detail methods are typically used
   for acquiring bulk information, like the complete FIB table.

-  Events The client can register for getting asynchronous notifications
   from the server. This is useful for getting interface state changes,
   and so on. The method name for requesting notifications is
   conventionally prefixed with “want\_”. E.g. “want_interface_events”.
   Which notification types results from an event registration is
   defined in the service definition.

A message from a client must include the ‘client_index’, an opaque
cookie identifying the sender, and a ‘context’ field to let the client
match request with reply.

An example of a message definition. The client sends the show_version
request, the server replies with the show_version_reply.

The *client_index* and *context* fields are required in all requests.
The *context* is returned by the server and is used by the client to
match up request and reply messages.

.. code-block:: c

   define show_version
   {
     u32 client_index;
     u32 context;
   };
   define show_version_reply
   {
     u32 context;
     i32 retval;
     string program [32];
     string version [32];
     string build_date [32];
     /* The final field can be a variable length argument */
     string build_directory [];
   };

The flags are not used by the clients, but have special meaning for some
of the tracing and debugging of the API. The *autoreply* flag is a
shorthand for a reply message with just a *retval* field.

.. code-block:: c

       define : DEFINE ID '{' block_statements_opt '}' ';'
       define : flist DEFINE ID '{' block_statements_opt '}' ';'
       flist : flag
             | flist flag
       flag : MANUAL_PRINT
            | MANUAL_ENDIAN
            | DONT_TRACE
            | AUTOREPLY

       block_statements_opt : block_statements
       block_statements : block_statement
                        | block_statements block_statement
       block_statement : declaration
                       | option
       declaration : type_specifier ID ';'
                   | type_specifier ID '[' ID '=' assignee ']' ';'
       declaration : type_specifier ID '[' NUM ']' ';'
                   | type_specifier ID '[' ID ']' ';'
       type_specifier : U8
                      | U16
                      | U32
                      | U64
                      | I8
                      | I16
                      | I32
                      | I64
                      | F64
                      | BOOL
                      | STRING
       type_specifier : ID

Options
~~~~~~~

The *option* word is used to specify meta information. The only current
use is to specify a semantic version of the .api file itself.

Example:

.. code-block:: c

   option version = "1.0.0";

.. code-block:: c


       option : OPTION ID '=' assignee ';'
       assignee : NUM
                | TRUE
                | FALSE
                | STRING_LITERAL

Defining new types
~~~~~~~~~~~~~~~~~~

New user defined types are defined just like messages. A typedef has two
forms. It can either define an alias for a different type (or array).

Example:

.. code-block:: c

   typedef u8 ip4_address[4];
   typedef u8 ip6_address[16];

Where the above defines two new types *vl_api_ip4_address_t* and
*vl_api_ip6_address_t*. These are aliases for the underlying u8 array.

In the other form, it is used to specify an abstract data type.

.. code-block:: c

   enum address_family {
     ADDRESS_IP4 = 0,
     ADDRESS_IP6,
   };

   union address_union {
     vl_api_ip4_address_t ip4;
     vl_api_ip6_address_t ip6;
   };

   typedef address {
     vl_api_address_family_t af;
     vl_api_address_union_t un;
   };

Where the new type *vl_api_address_t*

.. code-block:: c

       typedef : TYPEDEF ID '{' block_statements_opt '}' ';'
       typedef : TYPEDEF declaration

Importing Definitions
~~~~~~~~~~~~~~~~~~~~~

You can use definitions from other .api files by importing them. To
import another .api’s definitions, you add an import statement to the
top of your file:

import “vnet/ip/ip_types.api”;

By default you can only use definitions from directly imported .api
files.

The API compiler searches for imported files in a set of directories
specified on the API compiler command line using the –includedir flag.

.. code-block:: c

   import : IMPORT STRING_LITERAL ';'

Comments
~~~~~~~~

The API language uses C style comments.

.. code-block:: c

   /* */
   //

Enumerations
~~~~~~~~~~~~

Enums are similar to enums in C.

Every enum definition must contain a constant that maps to zero as its
first element. This is because:

There must be a zero value, so that we can use 0 as a numeric default
value. The zero value needs to be the first element.

As in C, enums can be used as flags or just as numbers. The on-wire, and
in memory representation size of an enum can be specified. Not all
language bindings will support that. The default size is 4 (u32).

Example

.. code-block:: c

   enum ip_neighbor_flags
   {
     IP_API_NEIGHBOR_FLAG_NONE = 0,
     IP_API_NEIGHBOR_FLAG_STATIC = 0x1,
     IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY = 0x2,
   };

Which generates the vl_api_ip_neighbor_flags_t in the C binding. In
Python that is represented as an IntFlag object
VppEnum.vl_api_ip_neighbor_flags_t.

.. code-block:: c

       enum : ENUM ID '{' enum_statements '}' ';'
       enum : ENUM ID ':' enum_size '{' enum_statements '}' ';'
       enum_size : U8
                 | U16
                 | U32
       enum_statements : enum_statement
                       | enum_statements enum_statement
       enum_statement : ID '=' NUM ','
                      | ID ','

Services
~~~~~~~~

The service statement defines the relationship between messages. For
request/response and dump/details messages it ties the request with the
reply. For events, it specifies which events that can be received for a
given ``want_*`` call.

Example:

.. code-block:: c

   service {
     rpc want_interface_events returns want_interface_events_reply
       events sw_interface_event;
   };

Which states that the request want_interface_events returns a
want_interface_events_reply and if enabled the client will receive
sw_interface_event messages whenever interface states changes.

.. code-block:: c

       service : SERVICE '{' service_statements '}' ';'
       service_statements : service_statement
                       | service_statements service_statement
       service_statement : RPC ID RETURNS NULL ';'
                            | RPC ID RETURNS ID ';'
                            | RPC ID RETURNS STREAM ID ';'
                            | RPC ID RETURNS ID EVENTS event_list ';'
       event_list : events
                  | event_list events
       events : ID
              | ID ','

Types
-----

Scalar Value Types
~~~~~~~~~~~~~~~~~~

========= ======== =============== ===========
.api type size     C type          Python type
========= ======== =============== ===========
i8        1        i8              int
u8        1        u8              int
i16       2        i16             int
u16       2        u16             int
i32       4        i32             int
u32       4        u32             int
i64       8        i64             int
u64       8        u64             int
f64       8        f64             float
bool      1        bool            boolean
string    variable vl_api_string_t str
========= ======== =============== ===========

User Defined Types
~~~~~~~~~~~~~~~~~~

vnet/ip/ip_types.api
^^^^^^^^^^^^^^^^^^^^

+--------------------+--------+-------------+-------------------------+
| .api type          | size   | C type      | Python type             |
+====================+========+=============+=========================+
| vl_api_address_t   | 20     | vl_ap       | `                       |
|                    |        | i_address_t | `<class 'ipaddress.IPv4 |
|                    |        |             | Address'> or <class 'ip |
|                    |        |             | address.IPv6Address'>`` |
+--------------------+--------+-------------+-------------------------+
| vl                 | 4      | vl_api_ip   | ``<class 'ip            |
| _api_ip4_address_t |        | 4_address_t | address.IPv4Address'>`` |
+--------------------+--------+-------------+-------------------------+
| vl                 | 16     | vl_api_ip   | ``<class 'ip            |
| _api_ip6_address_t |        | 6_address_t | address.IPv6Address'>`` |
+--------------------+--------+-------------+-------------------------+
| vl_api_prefix_t    | 21     | vl_a        | `                       |
|                    |        | pi_prefix_t | `<class 'ipaddress.IPv4 |
|                    |        |             | Network'> or <class 'ip |
|                    |        |             | address.IPv6Network'>`` |
+--------------------+--------+-------------+-------------------------+
| v                  | 5      | vl_api_i    | ``<class 'ip            |
| l_api_ip4_prefix_t |        | p4_prefix_t | address.IPv4Network'>`` |
+--------------------+--------+-------------+-------------------------+
| v                  | 17     | vl_api_i    | ``<class 'ip            |
| l_api_ip6_prefix_t |        | p6_prefix_t | address.IPv6Network'>`` |
+--------------------+--------+-------------+-------------------------+
| vl_api_ip4_add     | 5      | vl_api_ip4  | ``<class 'ipad          |
| ress_with_prefix_t |        | _address_wi | dress.IPv4Interface'>`` |
|                    |        | th_prefix_t |                         |
+--------------------+--------+-------------+-------------------------+
| vl_api_ip6_add     | 17     | vl_api_ip6  | ``<class 'ipad          |
| ress_with_prefix_t |        | _address_wi | dress.IPv6Interface'>`` |
|                    |        | th_prefix_t |                         |
+--------------------+--------+-------------+-------------------------+

vnet/ethernet/ethernet_types.api
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

+---------------------+------+---------------------+-------------------+
| .api type           | size | C type              | Python type       |
+=====================+======+=====================+===================+
| ``vl_               | 6    | ``vl_               | ``class 'vpp_pa   |
| api_mac_address_t`` |      | api_mac_address_t`` | pi.MACAddress'>`` |
+---------------------+------+---------------------+-------------------+

vnet/interface_types.api
^^^^^^^^^^^^^^^^^^^^^^^^

======================== ==== ======================== ===========
.api type                size C type                   Python type
======================== ==== ======================== ===========
vl_api_interface_index_t 4    vl_api_interface_index_t int
======================== ==== ======================== ===========

New explicit types
~~~~~~~~~~~~~~~~~~

String versus bytes
^^^^^^^^^^^^^^^^^^^

A byte string with a maximum length of 64:

.. code-block:: c

   u8 name[64];

Before the “string” type was added, text string were defined like this.
The implications of that was the user would have to know if the field
represented a \\0 ended C-string or a fixed length byte string. The wire
format of the ‘string’ type is a u32 length

An IPv4 or IPv6 address was previously defined like:

.. code-block:: c

   u8 is_ip6;
   u8 address[16];

Which made it hard for language bindings to represent the address as
anything but a byte string. The new explicit address types are shown
above.

Language generators
-------------------

The VPP API compiler currently has two output modules. One generating
JSON and one generating C header files that are directly used by the VPP
infrastructure and plugins.

The C/C++, Python, Go Lua, and Java language bindings are generated
based on the JSON files.

Future considerations
~~~~~~~~~~~~~~~~~~~~~

-  Generate C/C++ (vapi) client code directly from vppapigen
-  Embed JSON definitions into the API server, so dynamic languages
   can download them directly without going via the filesystem and JSON
   files.

API Change Process
------------------

Purpose
~~~~~~~

To minimize the disruptions to the consumers of the VPP API, while permitting
the innovation for the VPP itself.

Historically, API changes in VPP master branch were allowed at any point in time
outside of a small window between the API freeze milestone and RC1 milestone.
The API changes on the throttle branches were not permitted at all. This model
proved workable, however all the production use cases ended up on throttle
branches, with a lot of forklift activity when it is the time to upgrade to the
next branch.

This formally structured API change process harmonizes the behavior across all
the VPP branches, and allows more flexibility for the consumer, while permitting
the innovation in the VPP itself.

The Core Promise
~~~~~~~~~~~~~~~~

"If a user is running a VPP version N and does not use any deprecated APIs, they
should be able to simply upgrade the VPP to version N+1 and there should be no
API breakage".

In-Progress, Production and Deprecated APIs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This proposal adds a classification of stability of an API call:

-   "In-Progress": APIs in the process of the development, experimentation, and
    limited testing.

-   "Production": tested as part of the "make test", considered stable for general
    usage.

-   "Deprecated": used as a flag on Production APIs which are slated to be
    deprecated in the future release.

The "In-Progress" APIs or the APIs with the semantic version of 0.x.y are not
subject to any stability checks, thus the developers are free to introduce them,
modify their signatures, and as well remove them completely at will. The users
should not use the in-progress APIs without the interactions with its
maintainers, nor base the production code on those APIs. The goal of
"in-progress" APIs to allow rapid iteration and modifications to ensure the API
signature and function is stabilized. These API calls may be used for testing or
experimentation and prototyping.

When the maintainer is satisfied with the quality of the APIs, and ensures that
they are tested as part of the "Make test" runs, they can transition their
status to "Production".

The "Production" APIs can *NOT* be changed in any way that modifies their
representation on the wire and the signature (thus CRC). The only change that
they may incur is to be marked as "Deprecated". These are the APIs that the
downstream users can use for production purposes. They exist to fulfill a core
promise of this process: The "Deprecated" APIs are the "Production" APIs that
are about to be deleted. To ensure the above core promise is maintained, if the
API call was marked as deprecated at any point between RC1 of release N and RC1
of release N+1, it MUST NOT be deleted until the RC1 milestone of the
release N+2. The deprecated API SHOULD specify a replacement API - which MUST
be a Production API, so as not to decrease the level of stability.


The time interval between a commit that marks an API as deprecated and a commit
that deletes that API MUST be at least equal the time between the two subsequent
releases (currently 4 months).


Doing so allows a for a good heads-up to those who are using the
"one free upgrade" property to proactively catch and test the transition from
the deprecated APIs using the master.


Marking an API as deprecated just 1 day before RC1 branch pull and then deleting
that API one day after does *technically* satisfy "one free upgrade" promise,
but is rather hostile to the users that are proactively tracking it.

Semantic API Versioning
~~~~~~~~~~~~~~~~~~~~~~~

VPP APIs use semantic versioning according to semver.org, with the compatibility
logic being applied at the moment the messages are marked as deprecated.

To discuss: i.e. if message_2 is being introduced which deprecates the
message_1, then that same commit should increase the major version of the API.

The 0.x.x API versions, by virtue of being in-progress, are exempt from this
treatment.

Tooling
~~~~~~~

See https://gerrit.fd.io/r/c/vpp/+/26881:

crcchecker.py is a tool to enforce the policy, with a few other bonus uses:

extras/scripts/crcchecker.py --check-patchset # returns -1 if backwards incompatible extras/scripts/crcchecker.py --dump-manifest extras/scripts/crcchecker.py --git-revision v20.01 <files> extras/scripts/crcchecker.py -- diff <oldfile> <newfile>

Notice that you can use this tool to get the list of API changes since a given past release.

The policy:

.. highlight:: none

.. code-block::

  1. Production APIs should never change.
     The definition of a "production API" is if the major version in
     the API file is > 0 that is not marked as "in-progress".
  2. APIs that are experimental / not released are not checked.
     An API message can be individually marked as in progress,
     by adding the following in the API definition:
        option in_progress;
  3. An API can be deprecated in three-to-six steps (the steps
     with letters can be combined or split, depending on situation):
        Step 1a: A new "in-progress" API new_api_2 is added that
           is deemed to be a replacement.
        Step 1b: The existing API is marked as "replaced_by" this new API:
           option replaced_by="new_api_2";
        Step 2a: The new_api_2 is marked as production by deleting its in-progress status,
           provided that this API does have sufficient test coverage to deem it well tested.
        Step 2b: the existing API is marked as "deprecated":
           option deprecated="optional short message to humans reading it";
        Step 3: the deprecated API is deleted.

There is a time constraint that the minimum interval between the steps 2 and 3
must be at least 4 months. The proposal is to have step 2 around a couple of
weeks before the F0 milestone for a release, as triggered by the release manager
(and in the future by an automated means).

Use Cases
~~~~~~~~~

Adding A New Field To A Production API
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The simplest way to add a new field to a Production API message *foo_message* is
to create a new In-Progress message *foo_message_v2*, and add the field to that
one. Typically it will be an extension - so the API message handlers are
trivially chained. If there are changes/adjustments that are needed, this new
message can be freely altered without bothering the users of the Production API.

When the maintainer is happy with the quality of the implementation, and the
foo_message_v2 is tested in "make test" to the same extent as the foo_message,
they can make two commits: one, removing the in-progress status for
foo_message_v2, and the second one - deprecating foo_message and pointing the
foo_message_v2 as the replacement. Technically after the next throttle pull,
they can delete the foo_message - the deprecation and the replacement will be
already in the corresponding branch.

Rapid Experimentation For A New Feature
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Add a message that is in-progress, and keep iterating with this message. This
message is not subject to the change control process.

An In-progress API Accidentally Marked As "production"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This is expected to mainly apply during the initial period of 20.05->20.09, the
proposal is to have it active for 4 weeks from Jun 17 till July 15th, with the
following process.

If a developer finds that a given API or a set of APIs is not ready for
production due to lack of tests and/or the general API stability, then they:

-   Create a new gerrit change with *just* the marking of the API as
    in_progress, subject being: "api: <feature> api message downgrade" and
    a comment identifying which APIs are being downgraded and why.

-   Add ayourtch@gmail.com or the current Release Manager as a reviewer --
    for help in guiding the process and to ensure that the gerrit change is not
    forgotten.

-   Send an email to vpp-dev mailing list with the subject being the same as the
    one-liner commit message, reference to the gerrit change, and the reasoning.

-   Wait for the timeout period of two weeks for the feedback.

-   If no feedback received, assume the community agreement and commit the
    change to master branch.

This needs to be highlighted that this process is an *exception* - normally the
transition is always in_progress => production => deprecated.

API Change Examples
~~~~~~~~~~~~~~~~~~~

https://gerrit.fd.io/r/q/+is:merged+message:%2522%255Eapi:.*%2524%2522