]> git.proxmox.com Git - mirror_frr.git/blob - doc/developer/grpc.rst
Merge pull request #13430 from opensourcerouting/feature/rip_allow-ecmp_limit
[mirror_frr.git] / doc / developer / grpc.rst
1 .. _grpc-dev:
2
3 ***************
4 Northbound gRPC
5 ***************
6
7 To enable gRPC support one needs to add `--enable-grpc` when running
8 `configure`. Additionally, when launching each daemon one needs to request
9 the gRPC module be loaded and which port to bind to. This can be done by adding
10 `-M grpc:<port>` to the daemon's CLI arguments.
11
12 Currently there is no gRPC "routing" so you will need to bind your gRPC
13 `channel` to the particular daemon's gRPC port to interact with that daemon's
14 gRPC northbound interface.
15
16 The minimum version of gRPC known to work is 1.16.1.
17
18 .. _grpc-languages-bindings:
19
20 Programming Language Bindings
21 =============================
22
23 The gRPC supported programming language bindings can be found here:
24 https://grpc.io/docs/languages/
25
26 After picking a programming language that supports gRPC bindings, the
27 next step is to generate the FRR northbound bindings. To generate the
28 northbound bindings you'll need the programming language binding
29 generator tools and those are language specific.
30
31 C++ Example
32 -----------
33
34 The next sections will use C++ as an example for accessing FRR
35 northbound through gRPC.
36
37 .. _grpc-c++-generate:
38
39 Generating C++ FRR Bindings
40 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
41
42 Generating FRR northbound bindings for C++ example:
43
44 ::
45
46 # Install gRPC (e.g., on Ubuntu 20.04)
47 sudo apt-get install libgrpc++-dev libgrpc-dev
48
49 mkdir /tmp/frr-cpp
50 cd grpc
51
52 protoc --cpp_out=/tmp/frr-cpp \
53 --grpc_out=/tmp/frr-cpp \
54 -I $(pwd) \
55 --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` \
56 frr-northbound.proto
57
58
59 .. _grpc-c++-if-sample:
60
61 Using C++ To Get Version and Interfaces State
62 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63
64 Below is a sample program to print all interfaces discovered.
65
66 ::
67
68 # test.cpp
69 #include <string>
70 #include <sstream>
71 #include <grpc/grpc.h>
72 #include <grpcpp/create_channel.h>
73 #include "frr-northbound.pb.h"
74 #include "frr-northbound.grpc.pb.h"
75
76 int main() {
77 frr::GetRequest request;
78 frr::GetResponse reply;
79 grpc::ClientContext context;
80 grpc::Status status;
81
82 auto channel = grpc::CreateChannel("localhost:50051",
83 grpc::InsecureChannelCredentials());
84 auto stub = frr::Northbound::NewStub(channel);
85
86 request.set_type(frr::GetRequest::ALL);
87 request.set_encoding(frr::JSON);
88 request.set_with_defaults(true);
89 request.add_path("/frr-interface:lib");
90 auto stream = stub->Get(&context, request);
91
92 std::ostringstream ss;
93 while (stream->Read(&reply))
94 ss << reply.data().data() << std::endl;
95
96 status = stream->Finish();
97 assert(status.ok());
98 std::cout << "Interface Info:\n" << ss.str() << std::endl;
99 }
100
101 Below is how to compile and run the program, with the example output:
102
103 ::
104
105 $ g++ -o test test.cpp frr-northbound.grpc.pb.cc frr-northbound.pb.cc -lgrpc++ -lprotobuf
106 $ ./test
107 Interface Info:
108 {
109 "frr-interface:lib": {
110 "interface": [
111 {
112 "name": "lo",
113 "vrf": "default",
114 "state": {
115 "if-index": 1,
116 "mtu": 0,
117 "mtu6": 65536,
118 "speed": 0,
119 "metric": 0,
120 "phy-address": "00:00:00:00:00:00"
121 },
122 "frr-zebra:zebra": {
123 "state": {
124 "up-count": 0,
125 "down-count": 0,
126 "ptm-status": "disabled"
127 }
128 }
129 },
130 {
131 "name": "r1-eth0",
132 "vrf": "default",
133 "state": {
134 "if-index": 2,
135 "mtu": 1500,
136 "mtu6": 1500,
137 "speed": 10000,
138 "metric": 0,
139 "phy-address": "02:37:ac:63:59:b9"
140 },
141 "frr-zebra:zebra": {
142 "state": {
143 "up-count": 0,
144 "down-count": 0,
145 "ptm-status": "disabled"
146 }
147 }
148 }
149 ]
150 },
151 "frr-zebra:zebra": {
152 "mcast-rpf-lookup": "mrib-then-urib",
153 "workqueue-hold-timer": 10,
154 "zapi-packets": 1000,
155 "import-kernel-table": {
156 "distance": 15
157 },
158 "dplane-queue-limit": 200
159 }
160 }
161
162
163
164 .. _grpc-python-example:
165
166 Python Example
167 --------------
168
169 The next sections will use Python as an example for writing scripts to use
170 the northbound.
171
172 .. _grpc-python-generate:
173
174 Generating Python FRR Bindings
175 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
176
177 Generating FRR northbound bindings for Python example:
178
179 ::
180
181 # Install python3 virtual environment capability e.g.,
182 sudo apt-get install python3-venv
183
184 # Create a virtual environment for python grpc and activate
185 python3 -m venv venv-grpc
186 source venv-grpc/bin/activate
187
188 # Install grpc requirements
189 pip install grpcio grpcio-tools
190
191 mkdir /tmp/frr-python
192 cd grpc
193
194 python3 -m grpc_tools.protoc \
195 --python_out=/tmp/frr-python \
196 --grpc_python_out=/tmp/frr-python \
197 -I $(pwd) \
198 frr-northbound.proto
199
200 .. _grpc-python-if-sample:
201
202 Using Python To Get Capabilities and Interfaces State
203 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
204
205 Below is a sample script to print capabilities and all interfaces Python
206 discovered. This demostrates the 2 different RPC results one gets from gRPC,
207 Unary (`GetCapabilities`) and Streaming (`Get`) for the interface state.
208
209 ::
210
211 import grpc
212 import frr_northbound_pb2
213 import frr_northbound_pb2_grpc
214
215 channel = grpc.insecure_channel('localhost:50051')
216 stub = frr_northbound_pb2_grpc.NorthboundStub(channel)
217
218 # Print Capabilities
219 request = frr_northbound_pb2.GetCapabilitiesRequest()
220 response = stub.GetCapabilities(request)
221 print(response)
222
223 # Print Interface State and Config
224 request = frr_northbound_pb2.GetRequest()
225 request.path.append("/frr-interface:lib")
226 request.type=frr_northbound_pb2.GetRequest.ALL
227 request.encoding=frr_northbound_pb2.XML
228
229 for r in stub.Get(request):
230 print(r.data.data)
231
232 The previous script will output something like:
233
234 ::
235
236 frr_version: "7.7-dev-my-manual-build"
237 rollback_support: true
238 supported_modules {
239 name: "frr-filter"
240 organization: "FRRouting"
241 revision: "2019-07-04"
242 }
243 supported_modules {
244 name: "frr-interface"
245 organization: "FRRouting"
246 revision: "2020-02-05"
247 }
248 [...]
249 supported_encodings: JSON
250 supported_encodings: XML
251
252 <lib xmlns="http://frrouting.org/yang/interface">
253 <interface>
254 <name>lo</name>
255 <vrf>default</vrf>
256 <state>
257 <if-index>1</if-index>
258 <mtu>0</mtu>
259 <mtu6>65536</mtu6>
260 <speed>0</speed>
261 <metric>0</metric>
262 <phy-address>00:00:00:00:00:00</phy-address>
263 </state>
264 <zebra xmlns="http://frrouting.org/yang/zebra">
265 <state>
266 <up-count>0</up-count>
267 <down-count>0</down-count>
268 </state>
269 </zebra>
270 </interface>
271 <interface>
272 <name>r1-eth0</name>
273 <vrf>default</vrf>
274 <state>
275 <if-index>2</if-index>
276 <mtu>1500</mtu>
277 <mtu6>1500</mtu6>
278 <speed>10000</speed>
279 <metric>0</metric>
280 <phy-address>f2:62:2e:f3:4c:e4</phy-address>
281 </state>
282 <zebra xmlns="http://frrouting.org/yang/zebra">
283 <state>
284 <up-count>0</up-count>
285 <down-count>0</down-count>
286 </state>
287 </zebra>
288 </interface>
289 </lib>
290
291 .. _grpc-ruby-example:
292
293 Ruby Example
294 ------------
295
296 Next sections will use Ruby as an example for writing scripts to use
297 the northbound.
298
299 .. _grpc-ruby-generate:
300
301 Generating Ruby FRR Bindings
302 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
303
304 Generating FRR northbound bindings for Ruby example:
305
306 ::
307
308 # Install the required gems:
309 # - grpc: the gem that will talk with FRR's gRPC plugin.
310 # - grpc-tools: the gem that provides the code generator.
311 gem install grpc
312 gem install grpc-tools
313
314 # Create your project/scripts directory:
315 mkdir /tmp/frr-ruby
316
317 # Go to FRR's grpc directory:
318 cd grpc
319
320 # Generate the ruby bindings:
321 grpc_tools_ruby_protoc \
322 --ruby_out=/tmp/frr-ruby \
323 --grpc_out=/tmp/frr-ruby \
324 frr-northbound.proto
325
326
327 .. _grpc-ruby-if-sample:
328
329 Using Ruby To Get Interfaces State
330 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
331
332 Here is a sample script to print all interfaces FRR discovered:
333
334 ::
335
336 require 'frr-northbound_services_pb'
337
338 # Create the connection with FRR's gRPC:
339 stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure)
340
341 # Create a new state request to get interface state:
342 request = Frr::GetRequest.new
343 request.type = :STATE
344 request.path.push('/frr-interface:lib')
345
346 # Ask FRR.
347 response = stub.get(request)
348
349 # Print the response.
350 response.each do |result|
351 result.data.data.each_line do |line|
352 puts line
353 end
354 end
355
356
357 .. note::
358
359 The generated files will assume that they are in the search path (e.g.
360 inside gem) so you'll need to either edit it to use ``require_relative`` or
361 tell Ruby where to look for them. For simplicity we'll use ``-I .`` to tell
362 it is in the current directory.
363
364
365 The previous script will output something like this:
366
367 ::
368
369 $ cd /tmp/frr-ruby
370 # Add `-I.` so ruby finds the FRR generated file locally.
371 $ ruby -I. interface.rb
372 {
373 "frr-interface:lib": {
374 "interface": [
375 {
376 "name": "eth0",
377 "vrf": "default",
378 "state": {
379 "if-index": 2,
380 "mtu": 1500,
381 "mtu6": 1500,
382 "speed": 1000,
383 "metric": 0,
384 "phy-address": "11:22:33:44:55:66"
385 },
386 "frr-zebra:zebra": {
387 "state": {
388 "up-count": 0,
389 "down-count": 0
390 }
391 }
392 },
393 {
394 "name": "lo",
395 "vrf": "default",
396 "state": {
397 "if-index": 1,
398 "mtu": 0,
399 "mtu6": 65536,
400 "speed": 0,
401 "metric": 0,
402 "phy-address": "00:00:00:00:00:00"
403 },
404 "frr-zebra:zebra": {
405 "state": {
406 "up-count": 0,
407 "down-count": 0
408 }
409 }
410 }
411 ]
412 }
413 }
414
415
416 .. _grpc-ruby-bfd-profile-sample:
417
418 Using Ruby To Create BFD Profiles
419 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
420
421 In this example you'll learn how to edit configuration using JSON
422 and programmatic (XPath) format.
423
424 ::
425
426 require 'frr-northbound_services_pb'
427
428 # Create the connection with FRR's gRPC:
429 stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure)
430
431 # Create a new candidate configuration change.
432 new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new)
433
434 # Use JSON to configure.
435 request = Frr::LoadToCandidateRequest.new
436 request.candidate_id = new_candidate.candidate_id
437 request.type = :MERGE
438 request.config = Frr::DataTree.new
439 request.config.encoding = :JSON
440 request.config.data = <<-EOJ
441 {
442 "frr-bfdd:bfdd": {
443 "bfd": {
444 "profile": [
445 {
446 "name": "test-prof",
447 "detection-multiplier": 4,
448 "required-receive-interval": 800000
449 }
450 ]
451 }
452 }
453 }
454 EOJ
455
456 # Load configuration to candidate.
457 stub.load_to_candidate(request)
458
459 # Commit candidate.
460 stub.commit(
461 Frr::CommitRequest.new(
462 candidate_id: new_candidate.candidate_id,
463 phase: :ALL,
464 comment: 'create test-prof'
465 )
466 )
467
468 #
469 # Now lets delete the previous profile and create a new one.
470 #
471
472 # Create a new candidate configuration change.
473 new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new)
474
475 # Edit the configuration candidate.
476 request = Frr::EditCandidateRequest.new
477 request.candidate_id = new_candidate.candidate_id
478
479 # Delete previously created profile.
480 request.delete.push(
481 Frr::PathValue.new(
482 path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof']",
483 )
484 )
485
486 # Add new profile with two configurations.
487 request.update.push(
488 Frr::PathValue.new(
489 path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/detection-multiplier",
490 value: 5.to_s
491 )
492 )
493 request.update.push(
494 Frr::PathValue.new(
495 path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/desired-transmission-interval",
496 value: 900_000.to_s
497 )
498 )
499
500 # Modify the candidate.
501 stub.edit_candidate(request)
502
503 # Commit the candidate configuration.
504 stub.commit(
505 Frr::CommitRequest.new(
506 candidate_id: new_candidate.candidate_id,
507 phase: :ALL,
508 comment: 'replace test-prof with test-prof-2'
509 )
510 )
511
512
513 And here is the new FRR configuration:
514
515 ::
516
517 $ sudo vtysh -c 'show running-config'
518 ...
519 bfd
520 profile test-prof-2
521 detect-multiplier 5
522 transmit-interval 900
523 !
524 !