Extending the Command-line Interface (using viperfile.py)

Why and How

The viper CLI can easily be extended to include custom subcommands using the viper.project module.

To do this, you have to create a file named viperfile.py in the root of your workspace. This file will contain the definition(s) of one or multiple projects. A project works like a namespace for all the custom subcommands under it.

Example: Defining a Project

This is how a project can be defined in viperfile.py:

from viper.project import Project, arg

foo = Project("foo")

The viper.project.arg() function helps defining the command-line arguments a.k.a options or switches that the subcommand expects.

Let’s define a subcommand @foo:group1 that expects optional arguments --login_name and --identity_file with some default values and returns the text representation of a viper.Hosts object.

Example: Defining a subcommand for host group

from viper import Host, Hosts, meta

@foo.hostgroup(
    args=[
        arg("-l", "--login_name", default="root"),
        arg("-i", "--identity_file", default="/root/.ssh/id_rsa.pub"),
    ]
)
def group1(args):
    return Hosts.from_items(
        Host(
            ip="192.168.0.11",
            hostname="host11"
            login_name="root",
            identity_file=args.identity_file,
            meta=meta(provider="aws"),
        ),
        Host(
            ip="192.168.0.12",
            hostname="host12",
            login_name="root",
            identity_file=args.identity_file,
            meta=meta(provider="aws"),
        )
    )

Now running viper -h in that workspace will show us @foo:group1  [Hosts], and running viper @foo:group1 --help will list the arguments it’s expecting and their default values.

The subcommand can now be executed as below:

# Use the default values
viper @foo:group1

# Specify the login name and identity file
viper @foo:group1 -l user1 -i ~user1/.ssh/id_rsa.pub

Note

All the custom subcommands are prefixed with @ to separate them from the core viper subcommands. And the string following @ acts like a namespace that separates the subcommands belonging from different projects in the same viperfile.