summaryrefslogtreecommitdiffstats
path: root/doc/developer/mgmtd-dev.rst
blob: 2404ffe2a73b257bb7a29c661f8e20d6210f161a (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
..
.. SPDX-License-Identifier: GPL-2.0-or-later
..
.. June 19 2023, Christian Hopps <chopps@labn.net>
..
.. Copyright (c) 2023, LabN Consulting, L.L.C.
..

.. _mgmtd_dev:

MGMTD Development
=================

Overview
--------

``mgmtd`` (Management Daemon) is a new centralized management daemon for FRR.

Previously, ``vtysh`` was the only centralized management service provided.
Internally ``vtysh`` connects to each daemon and sends CLI commands (both
configuration and operational state queries) over a socket connection. This
service only supports CLI which is no longer sufficient.

An important next step was made with the addition of YANG support. A YANG
infrastructure was added through a new development called *northbound*. This
*northbound* interface added the capability of daemons to be configured and
queried using YANG models. However, this interface was per daemon and not
centralized, which is not sufficient.

``mgmtd`` harnesses this new *northbound* interface to provide a centralized
interface for all daemons. It utilizes the daemons YANG models to interact with
each daemon. ``mgmtd`` currently provides the CLI interface for each daemon that
has been converted to it, but in the future RESTCONF and NETCONF servers can
easily be added as *front-ends* to mgmtd to support those protocols as well.

Conversion Status
^^^^^^^^^^^^^^^^^

Fully Converted To MGMTD
""""""""""""""""""""""""

- lib/distribute
- lib/filter
- lib/if_rmap
- lib/routemap
- lib/affinitymap
- lib/if
- lib/vrf
- ripd
- ripngd
- staticd
- zebra (* - partial)

Converted To Northbound
"""""""""""""""""""""""
- bfdd
- pathd
- pbrd
- pimd

Converted To Northbound With Issues
"""""""""""""""""""""""""""""""""""
- eigrp
- isisd

Unconverted
"""""""""""
- babel
- bgpd
- ldpd
- lib/event
- lib/keychain
- lib/log_vty
- lib/nexthop_group
- lib/zlog_5424_cli
- nhrpd
- ospfd
- ospf6d
- pceplib
- qdb
- sharpd
- vrrpd

Converting A Daemon to MGMTD
----------------------------

A daemon must first be transitioned to the new :ref:`northbound` interface if that
has not already been done (see :ref:`nb-retrofit` for how to do this). Once this
is done a few simple steps are all that is required move the daemon over to
``mgmtd`` control.

Overview of Changes
^^^^^^^^^^^^^^^^^^^

Adding support for a *northbound* converted daemon involves very little work. It
requires enabling *frontend* (CLI and YANG) and *backend* (YANG) support.
``mgmtd`` was designed to keep this as simple as possible.

Front-End Interface:

#. Add YANG module file to ``mgmtd/subdir.am`` (e.g., ``yang/frr-staticd.yang.c``).

#. Add CLI handler file[s] to ``mgmtd/subdir.am``. The `subdir.am` variable to
   use is indicated in the next 2 steps.

   #. [if needed] Exclude (:code:`#ifndef`) non-configuration CLI handlers from
      CLI source file (e.g., inside :file:`staticd/static_vty.c`) and add the
      file to :code:`nodist_mgmtd_libmgmt_be_nb_la_SOURCES` in
      :file:`mgmtd/subdir.am`.

   #. [otherwise] Remove CLI handler file from _SOURCES variable in the daemon
      :file:`subdir.am` file (e.g in :file:`staticd/subdir.am`) and add to
      :code:`mgmtd_libmgmtd_a_SOURCES` in :file:`mgmtd/subdir.am`.

#. In order to have mgmtd try and load existing per-daemon config files, add
   the daemon to the :code:`mgmt_daemons` array in :file:`lib/vty.c`. With the
   official release of the mgmtd code FRR is no longer supporting per daemon log
   files but it will take a while before all of the topotest is converted.

#. In the daemon's :code:`struct frr_daemon_info` (i.e., inside it's
   :code:`FRR_DAEMON_INFO()`) set the `.flags` bit `FRR_NO_SPLIT_CONFIG`. This
   will keep the daemon from trying to read it's per-daemon config file as mgmtd
   will now be doing this.

#. Add the daemon's YANG module description[s] into the array
   :code:`mgmt_yang_modules` defined in :file:`mgmtd/mgmt_main.c` (see
   :ref:`mgmtd-config-write`). Make sure that all YANG modules that the daemon
   uses are present in the mgmtd list. To find this list look in the daemon's
   equivalent yang module array variable.

#. Initialize the CLI handlers inside :code:`mgmt_vty_init` in :file:`mgmtd/mgmt_vty.c`.

#. Direct ``vtysh`` to send CLI commands to ``mgmtd`` by modifying
   ``vtysh/vtysh.h``. At the top of this file each daemon has a bit
   ``#define``'d (e.g., ``#define VTYSH_STATICD 0x08000``) below this there are
   groupings, replace all the uses of the daemons bit with ``VTYSH_MGMTD``
   instead so that the CLI commands get properly routed to ``mgmtd`` rather than
   the daemon now.

 #. Remove initialization (and installation) of library CLI routines. These will
    correspond with the VTYSH removals from the last step i.e.,:

    - change access_list_init() to access_list_init_new(false) and remove from
      VTYSH_ACL_CONFIG (leave in VTYSH_ACL_SHOW).
    - remove if_cmd_init_default() => remove from VTYSH_INTERFACE_SUBSET
    - remove if_cmd_init() => remove from VTYSH_INTERFACE_SUBSET
    - change route_map_init() to route_map_init_new(false) and remove from
      VTYSH_ROUTE_MAP_CONFIG (leave in VTYSH_ROUTE_MAP_SHOW).
    - remove vrf_cmd_init(NULL)  => remove from VTYSH_INTERFACE_SUBSET
    ...

Back-End Interface:

#. In the daemon's main file initialize the BE client library. You add a global
   `struct mgmt_be_client *mgmt_be_client` near the daemons `event_loop *master`
   variable. Then where the daemon used to initialize it's CLI/VTY code replace
   that with the client initialization by calling `mgmt_be_client_create`.
   Likewise in the daemon's sigint cleanup code, operational walks should be
   canceled with a call to `nb_oper_cancel_all_walks`, and then the BE client
   should be destroyed with a call to `mgmt_be_client_destroy` and to be safe
   NULL out the global `mgmt_be_client` variable.

#. In ``mgmtd/mgmt_be_adapter.c`` add xpath prefix mappings to a one or both
   mapping arrays (``be_client_config_xpaths`` and ``be_client_oper_xpaths``) to
   direct ``mgmtd`` to send config and oper-state requests to your daemon. NOTE:
   make sure to include library supported xpaths prefixes as well (e.g.,
   "/frr-interface:lib"). A good way to figure these paths out are to look in
   each of the YANG modules that the daemon uses and include each of their paths
   in the array.

Add YANG and CLI into MGMTD
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

As an example here is the addition made to ``mgmtd/subdir.am`` for adding
``staticd`` support.

.. code-block:: make

    if STATICD
    nodist_mgmtd_mgmtd_SOURCES += \
        yang/frr-staticd.yang.c \
        yang/frr-bfdd.yang.c \
        # end
    nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c
    endif

An here is the addition to the modules array in ``mgmtd/mgmt_main.c``:

.. code-block:: c

    #ifdef HAVE_STATICD
    extern const struct frr_yang_module_info frr_staticd_info;
    #endif

    static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
           &frr_filter_info,
           ...
    #ifdef HAVE_STATICD
           &frr_staticd_info,
    #endif
    }


CLI Config and Show Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The daemon's CLI handlers for configuration (which having been converted to the
:ref:`northbound` now simply generate YANG changes) will be linked directly into
``mgmtd``.

If the operational and debug CLI commands are kept in files separate from the
daemon's configuration CLI commands then no extra work is required. Otherwise some
CPP #ifndef's will be required.

``mgmtd`` supports both config and operational state. However, many
daemons have not had their operational state CLI commands converted over to the
new YANG based methods. If that is the case and if both types of CLI handlers
are present in a single file (e.g. a ``xxx_vty.c`` or ``xxx_cli.c`` file) then
:code:`#ifndef` will need to be used to exclude the non-config CLI handlers from
``mgmtd``. The same goes for unconverted *debug* CLI handlers. For example:

.. code-block:: c

  DEFPY(daemon_one_config, daemon_one_config_cmd,
        "daemon one [optional-arg]"
        ...
  {
        ...
  }

  #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
  DEFPY(daemon_show_oper, daemon_show_oper_cmd,
        "show daemon oper [all]"
        ...
  {
        ...
  }
  #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */

  void daemon_vty_init(void)
  {
	install_element(CONFIG_NODE, &daemon_one_config_cmd);
        ...

  #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
          install_element(ENABLE_NODE, &daemon_show_oper_cmd);
  #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */

  }

.. _mgmtd-config-write:

CLI Config Write Handlers (:code:`cli_show`)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To support writing out the CLI configuration file the northbound API defines a
2 callbacks (:code:`cli_show` and :code:`cli_show_end`). Pointers to these
callbacks used to live side-by-side in a daemons :code:`struct frr_yang_module_info`,
with the daemons back-end configuration and operational state callbacks
(normally in a file named `<daemon>_nb.c`).

However, these 2 functionalities need to be split up now. The *frontend* config
writing callbacks (:code:`cli_show`) should now be linked into ``mgmtd`` while
the *backend* config and oper-state callbacks (e.g., :code:`create`,
:code:`modify`, etc) should continue to be linked into the daemon.

So you will need to define 2 :code:`struct frr_yang_module_info` arrays.

#. The existing array remains in the same place in the daemon, but with all the
   :code:`cli_show` handlers removed.

#. The removed :code:`cli_show` handlers should be added to a new
   :code:`struct frr_yang_module_info` array. This second array should be
   included in the same file that includes that actual function pointed to by
   the the :code:`cli_show` callbacks (i.e., the file is compiled into
   ``mgmtd``).

   This new :code:`struct frr_yang_module_info` array is the one to be included
   in mgmtd in `mgmt_yang_modules` inside ``mgmtd/mgmt_main.c``.

Back-End Client Connection
^^^^^^^^^^^^^^^^^^^^^^^^^^

In order for your daemon to communicate with mgmtd you need to initialize the
backend client library. You normally do this where you used to initialize your
CLI/VTY code.

.. code-block:: c

    ...
    struct event_loop *master;

    static struct mgmt_be_client *mgmt_be_client;
    ...

    int main(int argc, char **argv)
    {
        ...
        rip_init();
	rip_if_init();
	mgmt_be_client = mgmt_be_client_create("ripd", NULL, 0, master);

Likewise the client should be cleaned up in the daemon cleanup routine.

.. code-block:: c

    /* SIGINT handler. */
    static void sigint(void)
    {
            zlog_notice("Terminating on signal");
            ...
            nb_oper_cancel_all_walks();
            mgmt_be_client_destroy(mgmt_be_client);
            mgmt_be_client = NULL;


Back-End XPATH mappings
^^^^^^^^^^^^^^^^^^^^^^^

In order for ``mgmtd`` to direct configuration to your daemon you need to add
some XPATH mappings to ``mgmtd/mgmt_be_adapter.c``. These XPATHs determine which
configuration changes get sent over the *back-end* interface to your daemon.
There are 2 arrays to update the first for config support and the second for
operational state.

Below are the strings added for staticd config support:

.. code-block:: c

    #if HAVE_STATICD
    static const char *const staticd_xpaths[] = {
            "/frr-vrf:lib",
            "/frr-interface:lib",
            "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd",
            NULL,
    };
    #endif

    static const char *const *be_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
    #ifdef HAVE_STATICD
            [MGMTD_BE_CLIENT_ID_STATICD] = staticd_xpaths,
    #endif
    };

Below are the strings added for zebra operational state support (note zebra is
not conditionalized b/c it should always be present):

.. code-block:: c

    static const char *const zebra_oper_xpaths[] = {
            "/frr-interface:lib/interface",
            "/frr-vrf:lib/vrf/frr-zebra:zebra",
            "/frr-zebra:zebra",
            NULL,
    };

    static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
            [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths,
    };

MGMTD Internals
---------------

This section will describe the internal functioning of ``mgmtd``, for now a
couple diagrams are included to aide in source code perusal.


The client side of a CLI configuration change

.. figure:: ../figures/cli-change-client.svg
   :align: center


The server (mgmtd) side of a CLI configuration change

.. figure:: ../figures/cli-change-mgmtd.svg
   :align: center


The client and server sides of oper-state query

.. figure:: ../figures/cli-oper-state.svg
   :align: center