YAMS

YAMS is the main management daemon. It registers itself to Sysrepo, to be notified on configuration changes, state requests, and RPCs.

  • on configuration changes, YAMS configures the system (kernel and daemons).

  • on state request, YAMS reads the state of the system by querying the kernel and daemons, and replies back with formatted data.

  • on RPC request, YAMS executes the requested procedure (query specific system data, flush objects, ping, traceroute, import/export data…) and replies with formatted data.

Both config and state are described in the YANG model, respectively in /vrouter:config and /vrouter:state:

  • config contains the configuration data to apply

  • state contains the current operational state of the system

All YANG nodes available in config should also be available in state. This eases the comparison between the configuration (the desired state), and the effective system state. state can contain more nodes than config, to provide read-only information like statistics or an operational status.

A YANG module may contain specific RPCs (remote procedure calls) that are used to perform a synchronous action, without changing the configuration.

YAMS coding rules

YAMS is implemented in Python3, and makes heavy use of the asyncio library.

It is advised to validate coding rules with the following tools:

  • flake8: enforces style consistency across Python projects

  • isort: sorts Python import definitions alphabetically

  • pylint: checks for errors in Python code, and enforces a coding standard

  • pyang: validates YANG models

YANG models conventions

The configuration tree follows some conventions:

  • services that can run in different network instances (also called VRF) should be added inside the /vrouter:config/vrouter:vrf list.

  • interfaces are added in /vrouter:config/vrouter:vrf/vrouter-interface:interface.

  • services related to system configuration should be added in /vrouter:config/vrouter-system:system.

  • services related to routing configuration should be added in /vrouter:config/vrouter-routing:routing or in /vrouter:config/vrouter:vrf/vrouter-routing:routing.

As stated above, config and state are respectively in /vrouter:config and /vrouter:state. The state tree follows the same conventions than the config tree.

Here is an overview of the config and state trees:

config
  +-- vrf[name]
  |  +-- routing
  |  +-- <other per-vrf services>
  |  |   ...
  |  +-- interface
  |  |  +-- physical[name]
  |  |  +-- <other interface types>
  |  |      ...
  |
  +-- system
  +-- routing
  +-- <other services>
      ...
state
  +-- vrf[name]
  |  +-- routing
  |  +-- <other per-vrf services>
  |  |   ...
  |  +-- interface
  |  |  +-- physical[name]
  |  |  +-- <other interface types>
  |  |      ...
  |
  +-- system
  +-- routing
  +-- <other services>
      ...

Source organization and packaging

In YAMS, the services are grouped in different modules which corresponds to features that are packaged separately.

The services that are part of the base package are located in:

  • $YAMS/yams/service for the python code

  • $YAMS/yang for the yang models

The other modules (cgnat, ha, routing, …) are located in:

  • $YAMS/modules/<modname>/yams_<modname> for the python code

  • $YAMS/modules/<modname>/yang for the yang models

Interaction with system services

Some services (like DHCP, SSH, network interface configuration, …) are already configured through the standard Linux distribution configuration. This can conflict with the configuration done by YAMS.

For these services, some basic rules should be followed:

  • YAMS must not interact with a system service if it is not present in its configuration tree. Therefore, the system configuration remains and can be used.

  • YAMS should reuse system tools to configure and launch a service when available. For instance, many services can be started using systemd.

  • After a service or an object has been configured by YAMS once, if it is removed from configuration, it is disabled. The initial system state is not recovered.

  • The state tree shows the state of the system for what can be configured by YAMS, even if this was configured by another mean. For instance, if an external daemon adds an address to an interface, it is seen in the state of this interface.

NETCONF API stability

The API defined by the YANG model is a programmatic interface that can be used by NETCONF clients. It is therefore important that this API remains stable, to avoid breaking tools using it.

Adding YANG nodes is usually not an issue for API stability. However, nodes should not be deleted without a specific procedure:

  • A node to be deleted is first deprecated, using the status deprecated YANG statement. Some extensions, defined in $YAMS/yang/vrouter-extensions.yang can be added to provide details: status-deprecated-revision, status-obsoleted-release, status-description, status-replacement. Its default value, if any, has to be removed.

    A deprecated node must still work as before:

    • For a config node, it must still control the configuration. If a replacement node was added, the new one has priority over the old one.

    • For a state node, it must contain a valid value.

    The CLI displays a warning when using deprecated nodes.

  • After some time, the node is marked as obsolete, using the status obsolete YANG statement. The related code (configuration or state) is removed.

  • Later, the node is removed from the YANG model.

Debug YAMS

Logs

The usual way to add logs in YAMS is:

import logging
LOG = logging.getLogger(__name__)
LOG.error('This is an error message')
LOG.debug('This is a debug message')

The default YAMS log level is set in /etc/yams.conf. YAMS has to be restarted if the configuration file is modified.

Run in foreground

To ease debugging, yams can be run in foreground:

# systemctl stop yams
# yams -f

Use Python debugger

It is possible to use the python debugger (pdb):

# systemctl stop yams
# yams -f --pdb