VyOS Platform Blog

New-style operational mode command definitions are here

Written by Daniil Baturin | May 16, 2018 4:50:29 PM Z

We've had a convertor from the new style configuration command definitions in XML to the old style "templates" for a while in VyOS 1.2.0. As I predicted, a number of issues were only discovered and fixed as we have rewritten more old scripts, but by now they should be fully functional. However, until very recently, there was no equivalent of it for the operational mode commands. Now there is.

The new style

In case you've missed our earlier posts, here's a quick review. The configuration backend currently used by VyOS keeps command definitions in a very cumbesome format with a lot of nested directories where a directory represents a nesting level (e.g. "system options reboot-on-panic" is in "templates/system/options/reboot-on-panic"), a file with command properties must be present in every directory, and command definition files are allowed to have embedded shell scripts.

This makes command definitions very hard to read, and even harder to write. We cannot easily change that format until a new configuration backend is complete, but we can abstract it away, and that's what we did.

The new command definitions are in XML, and a RelaxNG schema is provided, so everyone can see its complete grammar and automatically check if their definitions are correctly written. Automated validation is already integrated in the build process.

Rewriting the command definitions goes along with rewriting the code they call. New style code goes to the vyos-1x package.


The new directory structure

Before we discuss the op mode definitions, let's talk about another important change we've made lately. If you've ever looked inside the VyOS filesystem, you noticed that the directory structure we inherited from Vyatta Core doesn't make much sense. Everything is in /opt/vyatta even though its contents are a) anything but optional b) actually managed by APT. Inside, it gets worse: executables almost arbitrarily distributed between bin/, bin/sudo-users, and sbin and so on.

Trying to restructure it all at once is a dangerous idea since the effect of the changes on the old code that often has hardcoded paths is hard to predict, so we decided to move the files to new locations as we rewrite them. The new directory structure is:

usr/libexec/vyos/
conf_mode/ # Configuration mode scripts
op_mode/ # Operational mode scripts
validators/ # Value validation scripts
completion/ # Completion scripts
helpers/ # Miscelanneous helper programs (nothing there yet)

New executables useful for the end users will go to /usr/bin and /usr/sbin, and new data will go to /usr/share/vyos, as per the filesystem hierarchy standard as well.

We've also introduced environment variables for referencing those directories. I suppose we've learnt the lesson of hardcoded paths too well by now, so let's avoid them in the future. Instead we can reference the directories by:

vyos_libexec_dir=/usr/libexec/vyos
vyos_op_scripts_dir=/usr/libexec/vyos/op_mode
vyos_validators_dir=/usr/libexec/vyos/validators
vyos_completion_dir=/usr/libexec/vyos/completion
vyos_conf_scripts_dir=/usr/libexec/vyos/conf_mode
vyos_sbin_dir=/usr/sbin
vyos_bin_dir=/usr/bin

How to write an operational command definition

For the proof of concept, I've migrated the operational mode commands for traffic dumps and bandwidth usage. Let's look at the CLI for traffic dumps. You can find its definitions in op-mode-definitions/traffic-dump.xml

It is relatively short, but demonstrates the most important concepts found in op mode commands. It also doesn't use any external scripts and instead calls tcpdump directly, which is acceptable because the commands are very short and simple and are run with the same unprocessed arguments unconditionally. If you want to look at a something that uses external scripts, check out the CLI for DNS forwarding.

I took a chance to redesign that CLI. Before that the op mode CLI for making traffic dumps was well hidden under "run monitor interfaces ... traffic ...". Many people weren't even aware that it exists, the fact that most people just run tcpdump by hand notwithstanding.

Now those commands are "run monitor traffic interface $intf <filter $filter | save $file [filter $filter] >".

Let's look at the XML file. Sadly, Posthaven won't let me paste XML into here properly (we should migrate from it — it only got worse over time), so please open the link and follow it with me.

It starts with interfaceDefinition tag, it's mandatory. Then we have a node. Much like in conf mode, there are three types of nodes: normal nodes ("node" tag) that are containers for other nodes (e.g. "show" or "reset"); tag nodes ("tagNode" tag) that take arguments (such as "interface" or "filter" in our case); and leaf nodes ("leafNode" tag) that are terminal nodes (such as "statisics" in "show dns forwarding statistics").

Any kind of op mode node can have a command attached to it with the "command" tag. We just put tcpdump commands in there. Notice the arguments like $4 and $6. This is how the op mode handler passes arguments to commands: you need to reference the number of the word in the command, starting with one. In our case "monitor traffic interface eth0" needs to pass eth0 to tcpdump, eth0 is the word number 4 in that line, so we use $4.

Inside properties, you can specify the help string with the "help" tag, that's what you get in the first level of tab completion. The "completionHelp" tag is more interesting. It defines the completion you get on the second tab press in a command that takes arguments, such as "interface" in our case. That tag may include one or more instances of:
  • "list" tags — those are static lists of options, interface speed in ethernet is a perfect example, "<list>10 100 1000 2500 10000</list>"
  • "script" tags — those are paths to scripts that produce a list of something, that's what we keep in the ${vyos_completion_dir}
  • "path" tags — those are configuration paths, such as "interfaces ethernet". It only makes sense for tag nodes of course, even though technically you can use any non-leaf node there. It's a thin wrapper for /bin/cli-shell-api listNodes $path

And that's pretty much all that is to it. You can fine the RelaxNG schema and its compact syntax equivalent right there in the package.

Join the work

The vyatta-op package has lots of simple commands that are a perfect fit for new contributors, so it makes a good starting point if you want to join VyOS development. Create a task in our Phabricator, write your code, reference the task number in the commits, and make a pull request against vyos-1x. For more complicated commands, it's a good idea to discuss it first.

The "reboot"/"show reboot", "show hardware", and "show system" families are perfect candidates since most of the commands there call just one system command. Let's make the command definitions easily readable and editable!