Nosh is not a shell, but a minimal python library to simply and intuitively implement Character Line Interfaces (CLIs) for networking devices.
2024-03-05.12.50.32.mov
git clone https://github.com/upa/nosh
cd nosh
flit install
Nosh treats each text in CLI input as token. Let us consider a
following command line.
set interfaces eth0 address 10.0.0.1/24
set, interfaces, eth0, address, and 10.0.0.1/24, are tokens.
Each token may have one or more leaf tokens and may have action. In
this example, token set may have leaf tokens of firewall,
system, vlans, and so on. Token 10.0.0.1/24 would have no leaves
but have an action that assigns the address to an interface.
You usually type tab, space, or ? to print possible completions
with descriptions. If you do this with the example command line set interfaces, the CLI shows like:
prompt> set interfaces
Completions:
<interface-name> Name to identify an interface
eth0
eth1
eth2
eth3
lo
We call this output of possible completions and their help strings
descriptions. A completion with the form <.*> is called
mark. Mark indicates what this token requires (interface name in
this case), but itself is not for the input completion;
<interface-name> is not inserted to the line buffer even when input
is <inte, unlike input et that shows possible completions eth0,
eth1, eth2, and eth3.
Nosh provides Token classes to implement command lines, for example:
-
TextToken: It has a static text appeared as a token, likeset,interface, andaddress. -
InterfaceToken: It represents interface names, for example,eth0andeth1. It appears as<interface-name>in descriptions by default. -
InterfaceAddressToken: It represents interface addresses likeADDRESS/PREFLEN. It appears as<address>in descriptions by default.
So, the first example set interfaces eth0 address 10.0.0.1/24 is now
represented by a series of tokens: TextToken("set"),
TextToken("interfaces"), InterfaceToken, TextToken("address"),
and InterfaceAddressToken.
Let's implement it as a CLI:
from nosh import CLI, TextToken, InterfaceToken, InterfaceAddressToken
tk_set = TextToken(text="set", desc="Set parameters")
tk_ifs = TextToken(text="interfaces", desc="Set interface parameters")
tk_int = InterfaceToken()
tk_adr = TextToken(text="address", desc="Set IP address")
tk_ifa = InterfaceAddressToken(action=lambda x,y: print(y))
tk_set.append(tk_ifs)
tk_ifs.append(tk_int)
tk_int.append(tk_adr)
tk_adr.append(tk_ifa)
cli = CLI()
cli.append(tk_set)
cli.cli()This script starts your CLI that accepts the command line set interfaces eth0 address 10.0.0.1/24 (eth0 may differ depending on
your environment, and IP addresses other than 10.0.0.1/24 are also
acceptable).
> set interfaces eth0
Completions:
address Set IP address
> set interfaces eth0 address
Completions:
<address> Address/Prefixlen
> set interfaces eth0 address 10.0.0.1/24
['set', 'interfaces', 'eth0', 'address', '10.0.0.1/24']
>
Please see an example CLI cli.py.
We have implemented following token classes:
TextToken: representing a static text.InterfaceToekn: interface names retrieved byifaddr.StringToken: representing a string for user-defined parameters (e.g., route-map, policy-statement, and ACL).IntToken: representing an integer.FloatToken: representing a float.IPv4AddressToken: representing an IPv4 Address.IPv6AddressToken: representing an IPv6 Address.IPAddressToken: representing an IP Address (both v4 and v6).InterfaceAddressToken: representing an IP (both v4 and v6) address with prefix length.IPv4NetworkToken: representing an IPv4 network address.IPv6NetworkToken: representing and IPv6 network address.ChoiceToken: representing choice from static texts.
You may want to implement new completions for specific cases depending
on a configuration backend. For example, <route-map> token would
give a list of already-defined route-maps in a configuration as
possible completions. To do this, implement your own Token class.
InterfaceToken class in nosh/token.py may help you.
Your new Token class should inherit BasicToken class, and overwrites
completion_candidates() and match() functions at
least.
completion_candidates() receives text, which is a input token, and
returns list of possible completions as list[tuple("text", "description")]. Note that text with the mark format <.*> appears
in only descriptions, and is ignored for input completion.
match() function returns True if the argument text exactly
matches this token, otherwise False.
Nosh has no configuration backend. It is a library to implement
command line interfaces. You can implement your own configuration
mechanisms invoked through action of tokens.