HOWTO: use truenas_ctl, a remote CLI for TrueNAS (aka truenas_incus_ctl)

TNC, or truenas_incus_ctl, is a tool for remote controlling a TrueNAS server using a familar CLI.

With the discussion on the CLI in the latest T3 video, I thought it was a good time to introduce a little project I’ve been working on.

truenas_incus_ctl, or truenas_ctl is a tool built for easily administering datasets, snapshots and network shares that are hosted on a TrueNAS server.

The latest version of the tool can be obtained from github:

It implements a simple to use command line interface, which is specifically designed to feel familiar to people who are used to using the zfs and zpool, commands.

The tool implements an automatic connection caching daemon to allow efficient login and quick connections to a remote (or local) truenas.

The tool was originally created as part of implementing an incus driver, hence the current name.

It effectively provides a remote CLI interface to a TrueNAS server, that can be used along side the existing WebUI.

Internally, the tool effectively combines and optimizes API calls to efficiently accomplish its goals in a blocking fashion, and is suitable for integrating into scripts.

You may find that the simplest way to accomplish some complex scripting goals would be to add any extra functionality required to the software, and then use the tool from your script!

Login

To login to the remote TrueNAS host first run the command:

sudo truenas_incus_ctl config login

Example:

$ sudo truenas_incus_ctl config login
[sudo] password for user: 
Enter a name for this connection: example
Enter the TrueNAS hostname or IP address: server.example.com
Setting up connection to TrueNAS host: server.example.com
Choose authentication method (1 for API Key, 2 for Username/Password): 2
Testing connection to wss://server.example.com/api/current...
Enter your TrueNAS username: truenas_admin
Enter your TrueNAS password: 
Generating API key...
API key successfully generated
Successfully connected to wss://server.example.com/api/current
Configuration for 'example' (connecting to server.example.com) saved to /root/.truenas_incus_ctl/config.json

This will create a “config” to store the API key which is created as part of logging in. You can further edit the configs, or specify different configs using the --config global flag, see config --help for additional commands

Dataset manipulation

You can then list your datasets with the following command

sudo truenas_incus_ctl dataset ls

Or a specific dataset/pool, and its children

sudo truenas_incus_ctl dataset ls -r pool/dataset 

Like zfs you can specify what columns to output

$ truenas_incus_ctl dataset ls dozer -o name,used
 name  | used  
-------+-------
 dozer | 63.3G 

Help is available for any command, for example, dataset create

$ truenas_incus_ctl dataset create -h
Creates a dataset/zvol.

Usage:
  truenas_incus_ctl dataset create <dataset>... [flags]

Flags:
      --aclmode string                    Controls how an ACL is modified during chmod(2) and how inherited ACEs are modified by the file creation mode (inherit, passthrough, restricted, discard) (default "inherit")
      --acltype string                    Controls whether ACLs are enabled and if so what type of ACL to use (inherit, posix, nfsv4, off) (default "inherit")
      --allow-shrinking                   By default, shrinking a volume to a smaller size is not permitted. This flag disables this check.
      --atime string                      Controls whether the access time for files is updated when they are read (inherit, on, off) (default "inherit")
      --casesensitivity string            (inherit, sensitive, insensitive) (default "inherit")
      --checksum string                   (inherit, on, off, fletcher2, fletcher4, sha256, sha512, skein, edonr, blake3) (default "inherit")
      --comments string                   User defined comments
      --compression string                Controls the compression algorithm used for this dataset
                                          (on, off, gzip, gzip-1, gzip-9, lz4, lzjb, zle, zstd, zstd-1, zstd-2, zstd-3, zstd-4, zstd-5, zstd-6, zstd-7, zstd-8, zstd-9, zstd-10, zstd-11, zstd-12, zstd-13, zstd-14, zstd-15, zstd-16, zstd-17, zstd-18, zstd-19, zstd-fast, zstd-fast-1, zstd-fast-2, zstd-fast-3, zstd-fast-4, zstd-fast-5, zstd-fast-6, zstd-fast-7, zstd-fast-8, zstd-fast-9, zstd-fast-10, zstd-fast-20, zstd-fast-30, zstd-fast-40, zstd-fast-50, zstd-fast-60, zstd-fast-70, zstd-fast-80, zstd-fast-90, zstd-fast-100, zstd-fast-500, zstd-fast-1000) (default "off")
      --copies int                        
  -p, --create-parents                    Creates all the non-existing parent datasets
      --deduplication string              (inherit, on, verify, off) (default "inherit")
      --exec string                       Controls whether processes can be executed from within this file system (inherit, on, off) (default "inherit")
      --force-size                        
  -h, --help                              help for create
      --managedby string                  Manager of this dataset, must not be empty (default "truenas_incus_ctl")
  -o, --option string                     Specify property=value,...
      --quota string                       (default "0")
      --quota-critical int                Percentage (1-100 or 0)
      --quota-warning int                 Percentage (1-100 or 0)
      --readonly string                   (inherit, on, off) (default "inherit")
      --recordsize string                 
      --refquota string                    (default "0")
      --refquota-critical int             Percentage (1-100 or 0)
      --refquota-warning int              Percentage (1-100 or 0)
      --refreservation string              (default "0")
      --reservation string                 (default "0")
      --share-type string                 (inherit, generic, multiprotocol, nfs, smb, apps) (default "inherit")
      --snapdev string                    Controls whether the volume snapshot devices are hidden or visible (hidden, visible) (default "hidden")
      --snapdir string                    Controls whether the .zfs directory is disabled, hidden or visible (disabled, hidden, visible) (default "hidden")
  -s, --sparse                            Creates a sparse volume with no reservation
      --special-small-block-size string    (default "0")
      --sync string                       Controls the behavior of synchronous requests (standard, always, disabled) (default "standard")
  -u, --user-props string                 Sets the specified properties
  -b, --volblocksize string               Volume block size (512, 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K) (default "512")
  -V, --volsize string                    Creates a volume of the given size instead of a filesystem, should be a multiple of the block size. (default "0")

Global Flags:
      --allow-insecure         Allow self-signed or non-trusted SSL certificates
  -K, --api-key string         API key
  -C, --config string          Name of config to look up in config.json, defaults to first entry
  -F, --config-file string     Override config filename (~/.truenas_incus_ctl/config.json)
      --daemon-socket string   Override the default daemon socket path (~/tncdaemon.sock)
      --debug                  Enable debug logs
  -H, --host string            Server hostname or URL

dataset create, and the related update command provides a powerful way to create datasets, and update the properties using the TrueNAS API without having to login to your WebUI to do it.

Once you get used to it… you may find yourself using it instead of the WebUI :wink:

A full suite of dataset commands is available

$ truenas_incus_ctl dataset -h
Edit or list datasets/zvols and their shares on a remote or local machine

Usage:
  truenas_incus_ctl dataset [flags]
  truenas_incus_ctl dataset [command]

Available Commands:
  create      Creates a dataset/zvol.
  delete      Deletes a dataset/zvol.
  list        Prints a table of all datasets/zvols, given a source and an optional set of properties.
  promote     Promote a clone dataset to no longer depend on the origin snapshot.
  rename      Rename a ZFS dataset
  update      Updates an existing dataset/zvol.

Snapshots

The tool has extensive snapshot support.

$ truenas_incus_ctl snapshot -h
Edit or list snapshots on a remote or local machine

Usage:
  truenas_incus_ctl snapshot [flags]
  truenas_incus_ctl snapshot [command]

Aliases:
  snapshot, snap

Available Commands:
  clone       clone snapshot of ZFS dataset
  create      Take a snapshot of dataset, possibly recursive
  delete      Delete a snapshot of dataset, possibly recursive
  list        List all snapshots
  rename      Rename a ZFS snapshot
  rollback    Rollback to a given snapshot

Bulk Commands

Additionally, most commands support multiple parameters. Look for the ... in the usage,

truenas_incus_ctl dataset create <dataset>... [flags]

Example to create two sparse 1GB zvols at once, and any parents:

$ truenas_incus_ctl dataset create -p -s -V 1G dozer/zvols-for-testing/zvol1 dozer/zvols-for-testing/zvol2
$ truenas_incus_ctl dataset ls -r -o name,type,volsize dozer/zvols-for-testing
             name              |    type    | volsize 
-------------------------------+------------+---------
 dozer/zvols-for-testing       | filesystem |         
 dozer/zvols-for-testing/zvol1 | volume     | 1G      
 dozer/zvols-for-testing/zvol2 | volume     | 1G      
$ truenas_incus_ctl dataset rm -r dozer/zvols-for-testing
$ truenas_incus_ctl dataset ls -r -o name,type,volsize dozer/zvols-for-testing
$ 

Output manipulation

Many commands support sophisticated output manipulation, for example, json output, parsable, table, no headers, or specifically choosing which columsn to enable or retrieve

eg, for dataset list

  -a, --all               Output all properties
      --format string     Output table format (csv, json, table, compact) (default "table")
  -j, --json              Equivalent to --format=json
  -c, --no-headers        Equivalent to --format=compact. More easily parsed by scripts
  -o, --output string     Output property list
  -p, --parsable          Show raw values instead of the already parsed values
  -r, --recursive         Retrieves properties for children
  -s, --source string     A comma-separated list of sources to display.
                          Those properties coming from a source other than those in this list are ignored.
                          Each source must be one of the following: local, default, inherited, temporary, received, or none.
                          The default value is all sources. (default "default")
  -u, --user-properties   Include user-properties

Command categories

This is the root help menu, and shows the major command categories, most categories will then have sub-commands, or perhaps even sub-commands for sub-commands

stux@incusdev:~/git/truenas_incus_ctl$ truenas_incus_ctl -h
Usage:
  truenas_incus_ctl [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  config      Manage local configuration settings
  daemon      
  dataset     Edit or list datasets/zvols and their shares on a remote or local machine
  help        Help about any command
  list        Prints a table of datasets/snapshots/shares, given a source and an optional set of properties.
  replication Replicate a dataset from one pool to another, locally or across any network
  service     Control the operation of services on the server (eg. iSCSI)
  share       Create, list, update or delete NFS or iSCSI shares.
  snapshot    Edit or list snapshots on a remote or local machine
  version     Print the version of this program

Flags:
      --allow-insecure         Allow self-signed or non-trusted SSL certificates
  -K, --api-key string         API key
  -C, --config string          Name of config to look up in config.json, defaults to first entry
  -F, --config-file string     Override config filename (~/.truenas_incus_ctl/config.json)
      --daemon-socket string   Override the default daemon socket path (~/tncdaemon.sock)
      --debug                  Enable debug logs
  -h, --help                   help for truenas_incus_ctl
  -H, --host string            Server hostname or URL

Use "truenas_incus_ctl [command] --help" for more information about a command.

Shares

The tool excels at creating and managing shares.

$ truenas_incus_ctl share -h
Create, list, update or delete NFS or iSCSI shares.

Usage:
  truenas_incus_ctl share [command]

Available Commands:
  iscsi       Manage iSCSI connections
  nfs         Create, list, update or delete NFS shares

iSCSI

The tool integrates with open-iscsi on the local system to make creating, activating and using zvol based iscsi devices trivial.

open-iscsi can be installed with apt install open-iscsi

After logging in, you can ensure iSCSI is configured and working with the following command:

sudo truenas_incus_ctl share iscsi setup --test

(iSCSI commands require sudo)

You can then create a zvol if necessary, create a share, and activate it:

$ truenas_incus_ctl dataset create -p -s -V 1G dozer/zvols-for-testing/zvol1
$ truenas_incus_ctl share iscsi create dozer/zvols-for-testing/zvol1
created dozer/zvols-for-testing/zvol1

Now the zvol is created, and shared via iscsi, it can be activated. Activation requires sudo

$ sudo truenas_incus_ctl share iscsi activate dozer/zvols-for-testing/zvol1
activated       /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1-lun-0
$ ls -l /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1-lun-0
lrwxrwxrwx 1 root root 9 Jul 12 04:31 /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1-lun-0 -> ../../sda

The remote zvol is now available on the local system as /dev/sda, and can be formatted with a filesystem if desired

$ sudo mkfs.xfs /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1-lu
n-0
specified blocksize 4096 is less than device physical sector size 16384
switching to logical sector size 512
meta-data=/dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1-lun-0 isize=512    agcount=4, agsize=65536 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=0
data     =                       bsize=4096   blocks=262144, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=16384, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
Discarding blocks...Done.

And that can now be mounted:

stux@incusdev:~/git/truenas_incus_ctl$ sudo mount /dev/sda /mnt
$ mount | grep sda
/dev/sda on /mnt type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)

share iscsi locate can be used to locate an activated volume

$ sudo /home/stux/go/bin/truenas_incus_ctl share iscsi locate dozer/zvols-for-testing/zvol1
located /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1-lun-0

You can then unmount, de-activate and destroy the share if you wish

$ sudo umount /mnt
$ sudo /home/stux/go/bin/truenas_incus_ctl share iscsi deactivate dozer/zvols-for-testing/zvol1
deactivated      iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol1
$ sudo /home/stux/go/bin/truenas_incus_ctl share iscsi delete dozer/zvols-for-testing/zvol1
deleted dozer/zvols-for-testing/zvol1

Actually, you don’t need to deactivate before deleting, as the deactivation is implicit.

BUT, almost all operations can be combined and performed using the locate command.

truenas_incus_ctl share iscsi locate -h
Locate the iscsi targets that map to the given datasets

Usage:
  truenas_incus_ctl share iscsi locate <dataset>... [flags]

Flags:
      --activate               Activate any shares that could not be located
      --create                 Create any shares that could not be activated or located, then activate them
      --deactivate             Deactivate any shares that could be located
      --delete                 Deactivate and delete any shares that could be located
  -h, --help                   help for locate
  -i, --initiator string       iSCSI initiator id or comment
      --parsable               Parsable (ie. minimal) output
  -p, --portal string          iSCSI portal [ip]:[port] or id (default ":")
      --readonly               If a share is to be created, ensure that its extent is read-only. Ignored for snapshots.
  -t, --target-prefix string   label to prefix the created target
      --wait                   Wait until a target is deactivated before returning

If you specify --activate to the locate command, then it will activate the share if necessary to locate it. If you specify the --create command then it will create the share if necessary to activate and then locate it.

So, at its simplest you can create and attach a remove zvol to your system with just 2 commands.

$ truenas_incus_ctl dataset create -p -s -V 1G dozer/zvols-for-testing/zvol3
$ sudo /home/stux/go/bin/truenas_incus_ctl share iscsi locate --create dozer/zvols-for-testing/zvol3
created dozer/zvols-for-testing/zvol3
activated       /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol3-lun-0
$ ls -l /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol3-lun-0
lrwxrwxrwx 1 root root 9 Jul 12 04:47 /dev/disk/by-path/ip-192.168.0.32:3260-iscsi-iqn.2005-10.org.freenas.ctl:dozer:zvols-for-testing:zvol3-lun-0 -> ../../sda

I find myself just spinning up another zvol whenever I need a new disk on a system…

Replication

The tool exposes the full underlying one-shot replication support built into TrueNAS.

$ truenas_incus_ctl replication start -h
Start replicating a dataset from one pool to another, locally or across any network.
A replication specifier can either be "<host>:<dataset>" or just "<dataset>" if local.
Currently, only local to local replication is supported.

Usage:
  truenas_incus_ctl replication start <sources>... <destination> <-n|-N|-R> <name filter> [flags]

Flags:
      --allow-from-scratch                           
      --aux-properties                                (default true)
      --compressed                                    (default true)
      --compression string                           (lz4, pigz, plzip) (default "lz4")
  -d, --direction string                             (push, pull) (default "push")
      --embed                                        
      --encryption                                   
      --encryption-inherit                           
      --encryption-key string                        
      --encryption-key-format string                 (hex, passphrase) (default "passphrase")
      --encryption-key-location string               
  -e, --exclude string                               
      --exclude-mountpoint-property                   (default true)
  -h, --help                                         help for start
      --hold-pending-snapshots                       
      --large-block                                   (default true)
      --lifetime-unit string                         (hour, day, week, month, year) (default "hour")
      --lifetime-value int                           
      --logging-level string                         (debug, info, warning, error) (default "warning")
  -R, --name-regex string                            
  -N, --naming-schema-aux string                     
  -n, --naming-schema-main string                    
      --netcat-active-side string                    (local, remote) (default "local")
      --netcat-active-side-listen-address string     
      --netcat-active-side-port-max int              
      --netcat-active-side-port-min int              
      --netcat-passive-side-connect-address string   
      --only-from-scratch                            
  -o, --options string                               
      --periodic-snapshot-tasks string               
      --properties-exclude string                    
      --properties-override string                   
      --readonly-policy string                       (set, require, ignore) (default "set")
  -r, --recursive                                    
      --replicate                                    
      --restrict-schedule string                     
  -p, --retention-policy string                      (source, custom, none) (default "none")
      --retries int                                   (default 5)
      --speed-limit int                              
      --ssh-credentials int                          
      --sudo

Example

$ tnc repl start -R ".*" dozer/replication-test/src-zvol dozer/replication-test/dst-zvol dozer/repl-test -p none -d push -r
stux@incusdev:~/git/truenas_incus_ctl$ tnc list -r dozer/real-test
                id                
----------------------------------
 dozer/repl-test                  
 dozer/repl-test/dst-zvol         
 dozer/repl-test/dst-zvol@latest  
 dozer/repl-test/dst-zvol@latest2 
 dozer/repl-test/src-zvol         
 dozer/repl-test/src-zvol@latest  
 dozer/repl-test/src-zvol@latest2 

Will replicate the source, and all its snapshtos to the destinationation zvol

This could be quite useful, and these replications show up in the GUI as active tasks.

Command Auto-Complete

The tool supports shell based tab completion, see completion -h for more information

Call to action

There’s a lot in this tool, and I hope the community enjoys it, digs in and gives it a go.

Its fairly easy to add functionality in a modular fashion, so I’m hoping to receive more Pull Requests to add functionality!

Its much easier to use for the things it can do, than doing it through midclt

This has the potential to be a definitive scripting tool for TrueNAS :slight_smile:

Enjoy.

2 Likes

Nice work.
This your project is great example to me how can Incus and Truenas integrate and work nicely together.
Its very sad if iX doesnt see this.

This is addressing the ZFS dataset snapshot, i.e. just the disk use – not the Incus snapshot, that can include a stateful snapshot of an instance’s running state, right?

Now that I had the chance to look and experiment with Incus a bit, I think some of the features of Incus, if tapped into, or just possible on TrueNAS, could make virtualization on TrueNAS comparable to pro-level ESXi features – i.e., things like stateful snapshots, Vmotion-like instance copy/move, easy remote consoling from Linux/Windows hosts, paravirtualized network and disk drivers, VT-d PCI passthrough, USB device pass-through, PowerCLI like scripting, etc.

I’m kinda shocked that people were actually griping about losing that threadbare libvirt virtualization.

Correct.

You can use the incus command line tool to take snapshots of incus instances. And if you set stateful to true, then they can be suspended, moved etc.

When using the TrueNAS driver for Incus, then one snapshot command does end up triggering the other.

Holy crap. Amazing work.

1 Like

Latest version of truenas_incus_ctl, v0.7.3 has now been released :slight_smile: