diff options
Diffstat (limited to 'docs/contributing/coding_guidelines.rst')
-rw-r--r-- | docs/contributing/coding_guidelines.rst | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/docs/contributing/coding_guidelines.rst b/docs/contributing/coding_guidelines.rst new file mode 100644 index 00000000..ff863d61 --- /dev/null +++ b/docs/contributing/coding_guidelines.rst @@ -0,0 +1,144 @@ +.. _coding_guidelines: + +Python Coding Guidelines +======================== + +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. + +Configuration script structure and behaviour +-------------------------------------------- + +.. code-block:: python + + import sys + + from vyos.config import Config + from vyos.util 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 ``vyos.config.Config`` object +methods 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 occurence 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 an internal representation of the config and checks +if it's valid, otherwise it must raise ``VyOSError`` 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 auxillary 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. + +Coding guidelines +----------------- + +Language +******** + +Python 3 **shall** be used. How long can we keep Python 2 alive anyway? + +No considerations for Python 2 compatibility **should** be taken. + +Formatting +********** + +Tabs **shall not** be used. Every indentation level should be 4 spaces. + +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. + +Code policy +----------- + +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 + +.. _process: https://blog.vyos.io/vyos-development-digest-10 +.. _vyos-1x: https://github.com/vyos/vyos-1x/blob/current/schema/ +.. _VyConf: https://github.com/vyos/vyconf/blob/master/data/schemata + |