summaryrefslogtreecommitdiff
path: root/docs/contributing/development.rst
blob: 66344ead211b722d302bcb3cda8ba20ed403221b (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
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
.. _development:

###########
Development
###########

All VyOS source code is hosted on GitHub under the VyOS organization which can
be found here: https://github.com/vyos

Our code is split into several modules. VyOS is composed of multiple individual
packages, some of them are forks of upstream packages and are periodically
synced with upstream, so keeping the whole source under a single repository
would be very inconvenient and slow. There is now an ongoing effort to
consolidate all VyOS-specific framework/config packages into vyos-1x package,
but the basic structure is going to stay the same, just with fewer and fewer
packages while the base code is rewritten from Perl/BASH into Python using and
XML based interface definition for the CLI.

The repository that contains all the ISO build scripts is:
https://github.com/vyos/vyos-build

The README.md file will guide you to use the this top level repository.

Submit a Patch
==============

Patches are always more then welcome. To have a clean and easy to maintain
repository we have some guidelines when working with Git. A clean repository
eases the automatic generation of a changelog file.

A good approach for writing commit messages is actually to have a look at the
file(s) history by invoking ``git log path/to/file.txt``.

.. _prepare_commit:

Prepare patch/commit
--------------------

In a big system, such as VyOS, that is comprised of multiple components, it's
impossible to keep track of all the changes and bugs/feature requests in one's
head. We use a bugtracker known as Phabricator_ for it ("issue tracker" would
be a better term, but this one stuck).

The information is used in three ways:

* Keep track of the progress (what we've already done in this branch and what
  we still need to do).

* Prepare release notes for upcoming releases

* Help future maintainers of VyOS (it could be you!) to find out why certain
  things have been changed in the codebase or why certain features have been
  added

To make this approach work, every change must be associated with a task number
(prefixed with **T**) and a component. If there is no bug report/feature request
for the changes you are going to make, you have to create a Phabricator_ task
first. Once there is an entry in Phabricator_, you should reference its id in
your commit message, as shown below:

* ``ddclient: T1030: auto create runtime directories``
* ``Jenkins: add current Git commit ID to build description``

If there is no Phabricator_ reference in the commits of your pull request, we
have to ask you to amend the commit message. Otherwise we will have to reject
it.

Writing good commit messages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The format should be and is inspired by: https://git-scm.com/book/ch5-2.html

* A single, short, summary of the commit (recommended 50 characters or less,
  not exceeding 80 characters) containing a prefix of the changed component
  and the corresponding Phabricator_ reference e.g. ``snmp: T1111:`` or
  ``ethernet: T2222:`` - multiple components could be concatenated as in
  ``snmp: ethernet: T3333``

* In some contexts, the first line is treated as the subject of an email and
  the rest of the text as the body. The blank line separating the summary from
  the body is critical (unless you omit the body entirely); tools like rebase
  can get confused if you run the two together.

* Followed by a message which describes all the details like:

  * What/why/how something has been changed, makes everyone's life easier when
    working with `git bisect`

  * All text of the commit message should be wrapped at 72 characters if
    possible which makes reading commit logs easier with ``git log`` on a
    standard terminal (which happens to be 80x25)

  * If applicable a reference to a previous commit should be made linking
    those commits nicely when browsing the history: ``After commit abcd12ef
    ("snmp: this is a headline") a Python import statement is missing,
    throwing the following exception: ABCDEF``

* Always use the ``-x`` option to the ``git cherry-pick`` command when back or
  forward porting an individual commit. This automatically appends the line:
  ``(cherry picked from commit <ID>)`` to the original authors commit message
  making it easier when bisecting problems.

* Every change set must be consistent (self containing)! Do not fix multiple
  bugs in a single commit. If you already worked on multiple fixes in the same
  file use `git add --patch` to only add the parts related to the one issue
  into your upcoming commit.

Limits:

* We only accept bugfixes in packages other than https://github.com/vyos/vyos-1x
  as no new functionality should use the old style templates (``node.def`` and
  Perl/BASH code. Use the new style XML/Python interface instead.

Please submit your patches using the well-known GitHub pull-request against our
repositories found in the VyOS GitHub organisation at https://github.com/vyos


Determinine source package
--------------------------

Suppose you want to make a change in the webproxy script but yet you do not know
which of the many VyOS packages ship this file. You can determine the VyOS
package name in question by using Debian's ``dpkg -S`` command of your running
VyOS installation.

.. code-block:: none

  vyos@vyos:~ dpkg -S /opt/vyatta/sbin/vyatta-update-webproxy.pl
  vyatta-webproxy: /opt/vyatta/sbin/vyatta-update-webproxy.pl

This means the file in question (``/opt/vyatta/sbin/vyatta-update-webproxy.pl``)
is located in the ``vyatta-webproxy`` package which can be found here:
https://github.com/vyos/vyatta-webproxy


Fork Repository and submit Patch
--------------------------------

Forking the repository and submitting a GitHub pull-request is the preferred
way of submitting your changes to VyOS. You can fork any VyOS repository to your
very own GitHub account by just appending ``/fork`` to any repository's URL on
GitHub. To e.g. fork the ``vyos-1x`` repository, open the following URL in your
favourite browser: https://github.com/vyos/vyos-1x/fork

You then can proceed with cloning your fork or add a new remote to your local
repository:

* Clone: ``git clone https://github.com/<user>/vyos-1x.git``

* Fork: ``git remote add myfork https://github.com/<user>/vyos-1x.git``

In order to record you as the author of the fix please identify yourself to Git
by setting up your name and email. This can be done local for this one and only
repository ``git config`` or globally using ``git config --global``.

.. code-block:: none

  git config --global user.name "J. Random Hacker"
  git config --global user.email "jrhacker@example.net"

Make your changes and save them. Do the following for all changes files to
record them in your created Git commit:

* Add file to Git index using ``git add myfile``, or for a whole directory:
  ``git add somedir/*``

* Commit the changes by calling ``git commit``. Please use a meaningful commit
  headline (read above) and don't forget to reference the Phabricator_ ID.

* Submit the patch ``git push`` and create the GitHub pull-request.


Attach patch to Phabricator task
--------------------------------

Follow the above steps on how to "Fork repository to submit a Patch". Instead
of uploading "pushing" your changes to GitHub you can export the patches/
commits and send it to maintainers@vyos.net or attach it directly to the bug
(preferred over email)

* Export last commit to patch file: ``git format-patch`` or export the last two
  commits into its appropriate patch files: ``git format-patch -2``


Coding Guidelines
=================

Like any other project we have some small guidelines about our source code, too.
The rules we have are not there to punish you - the rules are in place to help
us all. By having a consistent coding style it becomes very easy for new
and also longtime contributors to navigate through the sources and all the
implied logic of any one source file..

Python 3 **shall** be used. How long can we keep Python 2 alive anyway? No
considerations for Python 2 compatibility **should** be taken at any time.


Formatting
----------

* Python: Tabs **shall not** be used. Every indentation level should be 4 spaces
* XML: Tabs **shall not** be used. Every indentation level should be 2 spaces

.. note:: There are extensions to e.g. VIM (xmllint) which will help you to get
   your indention levels correct. Add to following to your .vimrc file:
   ``au FileType xml setlocal equalprg=xmllint\ --format\ --recover\ -\
   2>/dev/null`` now you can call the linter using ``gg=G`` in command mode.


Text generation
^^^^^^^^^^^^^^^

Template processor **should** be used for generating config files. Built-in
string formatting **may** be used for simple line-oriented formats where every
line is self-contained, such as iptables rules. Template processor **must** be
used for structured, multi-line formats such as those used by ISC DHCPd.

The default template processor for VyOS code is Jinja2_.


Summary
-------

When modifying the source code, remember these rules of the legacy elimination
campaign:

* No new features in Perl
* No old style command definitions
* No code incompatible with Python3


Python
======

The switch to the Python programming language for new code is not merely a
change of the language, but a chance to rethink and improve the programming
approach.

Let's face it: VyOS is full of spaghetti code where logic for reading the VyOS
config, generating daemon configs, and restarting processes is all mixed up.

Python (or any other language, for that matter) does not provide automatic
protection from bad design, so we need to also devise design guidelines and
follow them to keep the system extensible and maintainable.

But we are here to assist you and want to guide you through how you can become
a good VyOS contributor. The rules we have are not there to punish you - the
rules are in place to help us all. What does it mean? By having a consistent
coding style it becomes very easy for new contributors and also longtime
contributors to navigate through the sources and all the implied logic of
the spaghetti code.

Please use the following template as good starting point when developing new
modules or even rewrite a whole bunch of code in the new style XML/Pyhon
interface.


Configuration Script Structure and Behaviour
--------------------------------------------

Your configuration script or operation mode script which is also written in
Python3 should have a line break on 80 characters. This seems to be a bit odd
nowadays but as some people also work remotely or program using vi(m) this is
a fair good standard which I hope we can rely on.

In addition this also helps when browsing the GitHub codebase on a mobile
device if you happen to be a crazy scientist.

.. code-block:: python

  #!/usr/bin/env python3
  #
  # Copyright (C) 2019 VyOS maintainers and contributors
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License version 2 or later as
  # published by the Free Software Foundation.
  #
  # This program is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program.  If not, see <http://www.gnu.org/licenses/>.

  import sys

  from vyos.config import Config
  from vyos import ConfigError

  def get_config():
      vc = Config()
      # Convert the VyOS config to an abstract internal representation
      config = ...
      return config

  def verify(config):
      # Verify that configuration is valid
      if invalid:
          raise ConfigError("Descriptive message")
      return True

  def generate(config):
      # Generate daemon configs
      pass

  def apply(config):
      # Apply the generated configs to the live system
      pass

  try:
      config = get_config()
      verify(config)
  except ConfigError as e:
      print(e)
      sys.exit(1)

The ``get_config()`` function must convert the VyOS config to an abstract,
internal representation. No other function is allowed to call the ``vyos.config.
Config`` object method directly. The rationale for it is that when config reads
are mixed with other logic, it's very hard to change the config syntax since
you need to weed out every occurrence of the old syntax. If syntax-specific
code is confined to a single function, the rest of the code can be left
untouched as long as the internal representation remains compatible.

Another advantage is testability of the code. Mocking the entire config
subsystem is hard, while constructing an internal representation by hand is
way simpler.

The ``verify()`` function takes your internal representation of the config and
checks if it's valid, otherwise it must raise ``ConfigError`` with an error
message that describes the problem and possibly suggests how to fix it. It must
not make any changes to the system. The rationale for it is again testability
and, in the future when the config backend is ready and every script is
rewritten in this fashion, ability to execute commit dry run ("commit test"
like in JunOS) and abort commit before making any changes to the system if an
error is found in any component.

The ``generate()`` function generates config files for system components.

The ``apply()`` function applies the generated configuration to the live
system. It should use non-disruptive reload whenever possible. It may execute
disruptive operations such as daemon process restart if a particular component
does not support non-disruptive reload, or when the expected service degradation
is minimal (for example, in case of auxiliary services such as LLDPd). In case
of high impact services such as VPN daemon and routing protocols, when non-
disruptive reload is supported for some but not all types of configuration
changes, scripts authors should make effort to determine if a configuration
change can be done in a non-disruptive way and only resort to disruptive restart
if it cannot be avoided.

Unless absolutely necessary, configuration scripts should not modify the active
configuration of system components directly. Whenever at all possible, scripts
should generate a configuration file or files that can be applied with a single
command such as reloading a service through systemd init. Inserting statements
one by one is particularly discouraged, for example, when configuring netfilter
rules, saving them to a file and loading it with iptables-restore should always
be preferred to executing iptables directly.

The ``apply()`` and ``generate()`` functions may ``raise ConfigError`` if, for
example, the daemon failed to start with the updated config. It shouldn't be a
substitute for proper config checking in the ``verify()`` function. All
reasonable effort should be made to verify that generated configuration is
valid and will be accepted by the daemon, including, when necessary, cross-
checks with other VyOS configuration subtrees.

Exceptions, including ``VyOSError`` (which is raised by ``vyos.config.Config``
on improper config operations, such as trying to use ``list_nodes()`` on a
non-tag node) should not be silenced or caught and re-raised as config error.
Sure this will not look pretty on user's screen, but it will make way better
bug reports, and help users (and most VyOS users are IT professionals) do their
own debugging as well.

For easy orientation we suggest you take a look on the ``ntp.py`` or
``interfaces-bonding.py`` (for tag nodes) implementation. Both files can be
found in the vyos-1x_ repository.


XML (used for CLI definitions)
==============================

The bash (or better vbash) completion in VyOS is defined in *templates*.
Templates are text files (called ``node.def``) stored in a directory tree. The
directory names define the command names, and template files define the command
behaviour. Before VyOS 1.2 (crux) this files were created by hand. After a
complex redesign process_ the new style template are automatically generated
from a XML input file.

XML interface definitions for VyOS come with a RelaxNG schema and are located
in the vyos-1x_ module. This schema is a slightly modified schema from VyConf_
alias VyOS 2.0 So VyOS 1.2.x interface definitions will be reusable in Nextgen
VyOS Versions with very minimal changes.

The great thing about schemas is not only that people can know the complete
grammar for certain, but also that it can be automatically verified. The
`scripts/build-command-templates` script that converts the XML definitions to
old style templates also verifies them against the schema, so a bad definition
will cause the package build to fail. I do agree that the format is verbose, but
there is no other format now that would allow this. Besides, a specialized XML
editor can alleviate the issue with verbosity.

Example:

.. code-block:: xml

  <?xml version="1.0"?>
  <!-- Cron configuration -->
  <interfaceDefinition>
    <node name="system">
      <children>
        <node name="task-scheduler">
          <properties>
            <help>Task scheduler settings</help>
          </properties>
          <children>
            <tagNode name="task" owner="${vyos_conf_scripts_dir}/task_scheduler.py">
              <properties>
                <help>Scheduled task</help>
                <valueHelp>
                  <format>&lt;string&gt;</format>
                  <description>Task name</description>
                </valueHelp>
                <priority>999</priority>
              </properties>
              <children>
                <leafNode name="crontab-spec">
                  <properties>
                    <help>UNIX crontab time specification string</help>
                  </properties>
                </leafNode>
                <leafNode name="interval">
                  <properties>
                    <help>Execution interval</help>
                    <valueHelp>
                      <format>&lt;minutes&gt;</format>
                      <description>Execution interval in minutes</description>
                    </valueHelp>
                    <valueHelp>
                      <format>&lt;minutes&gt;m</format>
                      <description>Execution interval in minutes</description>
                    </valueHelp>
                    <valueHelp>
                      <format>&lt;hours&gt;h</format>
                      <description>Execution interval in hours</description>
                    </valueHelp>
                    <valueHelp>
                      <format>&lt;days&gt;d</format>
                      <description>Execution interval in days</description>
                    </valueHelp>
                    <constraint>
                      <regex>[1-9]([0-9]*)([mhd]{0,1})</regex>
                    </constraint>
                  </properties>
                </leafNode>
                <node name="executable">
                  <properties>
                    <help>Executable path and arguments</help>
                  </properties>
                  <children>
                    <leafNode name="path">
                      <properties>
                        <help>Path to executable</help>
                      </properties>
                    </leafNode>
                    <leafNode name="arguments">
                      <properties>
                        <help>Arguments passed to the executable</help>
                      </properties>
                    </leafNode>
                  </children>
                </node>
              </children>
            </tagNode>
          </children>
        </node>
      </children>
    </node>
  </interfaceDefinition>

Command definitions are purely declarative, and cannot contain any logic. All
logic for generating config files for target applications, restarting services
and so on is implemented in configuration scripts instead.

GNU Preprocessor
----------------

XML interface definition files use the `xml.in` file extension which was
implemented in :vytask:`T1843`. XML interface definitions tend to have a lot of
duplicated code in areas such as:

* VIF (incl. VIF-S/VIF-C)
* Address
* Description
* Enabled/Disabled

Instead of supplying all those XML nodes multiple times there are now include
files with predefined features. Brief overview:

* `IPv4, IPv6 and DHCP(v6)`_ address assignment
* `IPv4, IPv6`_ address assignment
* `VLAN (VIF)`_ definition
* `MAC address`_ assignment

All interface definition XML input files (.in suffix) will be sent to the GCC
preprocess and the output is stored in the `build/interface-definitions`
folder. The previously mentioned `scripts/build-command-templates` script
operates on the `build/interface-definitions` folder to generate all required
CLI nodes.

.. code-block:: none

  $ make interface_definitions
  install -d -m 0755 build/interface-definitions
  install -d -m 0755 build/op-mode-definitions
  Generating build/interface-definitions/intel_qat.xml from interface-definitions/intel_qat.xml.in
  Generating build/interface-definitions/interfaces-bonding.xml from interface-definitions/interfaces-bonding.xml.in
  Generating build/interface-definitions/cron.xml from interface-definitions/cron.xml.in
  Generating build/interface-definitions/pppoe-server.xml from interface-definitions/pppoe-server.xml.in
  Generating build/interface-definitions/mdns-repeater.xml from interface-definitions/mdns-repeater.xml.in
  Generating build/interface-definitions/tftp-server.xml from interface-definitions/tftp-server.xml.in
  [...]


Guidelines
----------

Use of numbers
^^^^^^^^^^^^^^^

Use of numbers in command names **should** be avoided unless a number is a
part of a protocol name or similar. Thus, ``protocols ospfv3`` is perfectly
fine, but something like ``server-1`` is questionable at best.

Help String
^^^^^^^^^^^

To ensure uniform look and feel, and improve readability, we should follow a
set of guidelines consistently.

Capitalization and punctuation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The first word of every help string **must** be capitalized. There **must not**
be a period at the end of help strings.

Rationale: this seems to be the unwritten standard in network device CLIs, and
a good aesthetic compromise.

Examples:

* Good: "Frobnication algorithm"
* Bad: "frobnication algorithm"
* Bad: "Frobnication algorithm."
* Horrible: "frobnication algorithm."

Use of abbreviations and acronyms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Abbreviations and acronyms **must** be capitalized.

Examples:

* Good: "TCP connection timeout"
* Bad: "tcp connection timeout"
* Horrible: "Tcp connection timeout"

Acronyms also **must** be capitalized to visually distinguish them from normal
words:

Examples:

* Good: RADIUS (as in remote authentication for dial-in user services)
* Bad: radius (unless it's about the distance between a center of a circle and
  any of its points)

Some abbreviations are traditionally written in mixed case. Generally, if it
contains words "over" or "version", the letter **should** be lowercase. If
there's an accepted spelling (especially if defined by an RFC or another
standard), it **must** be followed.

Examples:

* Good: PPPoE, IPsec
* Bad: PPPOE, IPSEC
* Bad: pppoe, ipsec

Use of verbs
~~~~~~~~~~~~

Verbs **should** be avoided. If a verb can be omitted, omit it.

Examples:

* Good: "TCP connection timeout"
* Bad: "Set TCP connection timeout"

If a verb is essential, keep it. For example, in the help text of ``set system
ipv6 disable-forwarding``, "Disable IPv6 forwarding on all interfaces" is a
perfectly justified wording.

Prefer infinitives
~~~~~~~~~~~~~~~~~~

Verbs, when they are necessary, **should** be in their infinitive form.

Examples:

* Good: "Disable IPv6 forwarding"
* Bad: "Disables IPv6 forwarding"

Migrating old CLI
-----------------

.. list-table::
   :widths: 25 25 50
   :header-rows: 1

   * - Old concept/syntax
     - New syntax
     - Notes
   * - mynode/node.def
     - <node name="mynode"> </node>
     - Leaf nodes (nodes with values) use <leafNode> tag instead
   * - mynode/node.tag , tag:
     - <tagNode name="mynode> </node>
     -
   * - help: My node
     - <properties> <help>My node</help>
     -
   * - val_help: <format>; some string
     - <properties> <valueHelp> <format> format </format> <description> some
       string </description>
     - Do not add angle brackets around the format, they will be inserted
       automatically
   * - syntax:expression: pattern
     - <properties> <constraint> <regex> ...
     - <constraintErrorMessage> will be displayed on failure
   * - syntax:expression: $VAR(@) in "foo", "bar", "baz"
     - None
     - Use regex
   * - syntax:expression: exec ...
     - <properties> <constraint> <validator> <name ="foo" argument="bar">
     - "${vyos_libexecdir}/validators/foo bar $VAR(@)" will be executed,
       <constraintErrorMessage> will be displayed on failure
   * - syntax:expression: (arithmetic expression)
     - None
     - External arithmetic validator may be added if there's demand, complex
       validation is better left to commit-time scripts
   * - priority: 999
     - <properties> <priority>999</priority>
     - Please leave a comment explaining why the priority was chosen (e.g. "after
       interfaces are configured")
   * - multi:
     - <properties> <multi/>
     - Only applicable to leaf nodes
   * - allowed: echo foo bar
     - <properties> <completionHelp> <list> foo bar </list>
     -
   * - allowed: cli-shell-api listNodes vpn ipsec esp-group
     - <properties> <completionHelp> <path> vpn ipsec esp-group </path> ...
     -
   * - allowed: /path/to/script
     - <properties> <completionHelp> <script> /path/to/script </script> ...
     -
   * - default:
     - None
     - Move default values to scripts
   * - commit:expression:
     - None
     - All commit time checks should be in the verify() function of the script
   * - begin:/create:/delete:
     - None
     - All logic should be in the scripts

Debugging
=========

There are two flags available to aid in debugging configuration scripts.
Since configuration loading issues will manifest during boot, the flags are
passed as kernel boot parameters.

Kernel boot parameters
----------------------

``vyos-debug``
^^^^^^^^^^^^^^

Adding the parameter ``vyos-debug`` to the linux boot line will produce
timing results for the execution of scripts during commit. If one is seeing
an unexpected delay during manual or boot commit, this may be useful in
identifying bottlenecks. The internal flag is ``VYOS_DEBUG``, and is found in
vyatta-cfg_. Output is directed to ``/var/log/vyatta/cfg-stdout.log``.

``vyos-config-debug``
^^^^^^^^^^^^^^^^^^^^^

During development, coding errors can lead to a commit failure on boot,
possibly resulting in a failed initialization of the CLI. In this
circumstance, the kernel boot parameter ``vyos-config-debug`` will ensure
access to the system as user ``vyos``, and will log a Python stack trace to
``/tmp/boot-config-trace``.


Debugging features
------------------

A number of flags can be set up to change the behaviour of VyOS at runtime.
These flags can be toggled using either environment variables or creating
files.

For each feature, a file called ``vyos.feature.debug`` can be created to toggle
the feature on. If a parameter is required it can be placed inside the file as
its first line.

The file can be placed in ``/tmp`` for one time debugging (as the file will be
removed on reboot) or placed in '/config' to stay permanently.

For example, ``/tmp/vyos.ifconfig.debug`` can be created to enable interface
debugging.

It is also possible to set up the debugging using environment variables.
In that case, the name will be (in uppercase) VYOS_FEATURE_DEBUG.

for example running, ``export VYOS_IFCONFIG_DEBUG=""`` on your vash, will have
the same effect as ``touch /tmp/vyos.ifconfig.debug``.

``ifconfig``
^^^^^^^^^^^^

Once set, all commands used, and their responses received from the OS, will be
presented on the screen for inspection.

``command``
^^^^^^^^^^^

Once set, all commands used, and their responses received from the OS, will be 
presented on the screen for inspection.

``developer``
^^^^^^^^^^^^^

Should a command fail, instead of printing a message to the user explaining how
to report issues, the python interpreter will start a PBD post-mortem session
to allow the developer to debug the issue.

As the debugger will wait from input from the developer, it has the capacity to
prevent a router to boot and therefore should only be permanently set up on 
production if you are ready to see the OS fail to boot.

``log``
^^^^^^^

In some rare cases, it may be useful to see what the OS is doing, including
during boot. This option sends all commands used by VyOS to a file. 
The default file is ``/tmp/full-log`` but it can be changed.


Priorities
==========

VyOS CLI is all about priorities. Every CLI node has a corresponding ``node.def``
file and possibly an attached script that is executed when the node is present.
Nodes can have a priority, and on system bootup - or any other ``commit`` to the
config all scripts are executed from lowest to higest priority. This is good as
this gives a deterministic behavior.

To debug issues in priorities or to see what's going on in the background you can
use the ``/opt/vyatta/sbin/priority.pl`` script which lists to you the execution
order of the scripts.

Continuous Integration
======================

VyOS makes use of Jenkins_ as our Continuous Integration (CI) service. Our CI
server is publicly accessible here: https://ci.vyos.net. You can get a brief
overview of all required components shipped in a VyOS ISO.

To build our modules we utilize a CI/CD Pipeline script. Each and every VyOS
component comes with it's own ``Jenkinsfile`` which is (more or less) a copy.
The Pipeline utilizes the Docker container from the :ref:`build_iso` section -
but instead of building it from source on every run, we rather always fetch a
fresh copy (if needed) from Dockerhub_.

Each module is build on demand if a new commit on the branch in question is
found. After a successful run the resulting Debian Package(s) will be deployed
to our Debian repository which is used during build time. It is located here:
http://dev.packages.vyos.net/repositories/.

.. _process: https://blog.vyos.io/vyos-development-digest-10
.. _VyConf: https://github.com/vyos/vyconf/tree/master/data/schemata
.. _vyos-1x: https://github.com/vyos/vyos-1x/tree/current/schema
.. _Jinja2: https://jinja.palletsprojects.com/
.. _Jenkins: https://jenkins.io/
.. _Dockerhub: https://hub.docker.com/u/vyos/
.. _`IPv4, IPv6 and DHCP(v6)`: https://github.com/vyos/vyos-1x/tree/current/interface-definitions/include/address-ipv4-ipv6-dhcp.xml.i
.. _`IPv4, IPv6`: https://github.com/vyos/vyos-1x/tree/current/interface-definitions/include/address-ipv4-ipv6.xml.i
.. _`VLAN (VIF)`: https://github.com/vyos/vyos-1x/tree/current/interface-definitions/include/vif.xml.i
.. _`MAC address`: https://github.com/vyos/vyos-1x/tree/current/interface-definitions/include/interface-mac.xml.i
.. _vyatta-cfg: https://github.com/vyos/vyatta-cfg

.. include:: ../common-references.rst