JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Oracle ZFS Storage Appliance Administration Guide
search filter icon
search icon

Document Information


1.  Introduction




Expansion Storage


Key Features

Data Services


Browser User Interface (BUI)

Command Line Interface (CLI)

User Interface

Browser User Interface (BUI)

Command Line Interface (CLI)


Supported Browsers

Tier 1

Tier 2

Unsupported Browsers

Main Window





Session Annotation

Title Bar

Side Panels and Menu Titles

Main Window Side Panels and Menu Titles

Add Projects

Move Shares

Object Name

Non-Standard BUI Control Primer


Editing Share Properties

Viewing List Item Controls

Modal Dialogs


General Usage


Basic Usage


Dashboard Thresholds


Identity Mapping

Miscellaneous Icons


CLI Introduction

Logging Into the CLI


CLI Contexts

Root Context

Child Contexts

Dynamic Child Contexts

Returning to a Previous Context

Navigating to a Parent Context

Contexts and Tab-Completion

Executing Context-Specific Commands

Uncommitted Contexts


CLI Properties

Getting Properties

Getting a Single Property Value

Tab Completion

Setting Properties

Committing a Set Property Value

Setting a Property Value with an Implied Commit

Setting a Property to a List of Values

Setting a Property to a Value Containing Special Characters

Immutable Properties


Batching Commands


The Script Environment

Interacting with the System

The Run Function

The Get Function

The List Function

The Children Function

The Choices Function

Generating Output

Dealing with Errors

Automating Access

2.  Status

3.  Configuration

4.  Services


6.  Integration



Batching Commands

The simplest scripting mechanism is to batch appliance shell commands. For example, to automatically take a snapshot called "newsnap" in the project "myproj" and the filesystem "myfs", put the following commands in a file:

select myproj
select myfs
snapshots snapshot newsnap

Then ssh onto the appliance, redirecting standard input to be the file:

% ssh root@dory < myfile.txt

In many shells, you can abbreviate this by using a "here file", where input up to a token is sent to standard input. Following is the above example in terms of a here file:

% '''ssh root@dory << EOF
select myproj
select myfs
snapshots snapshot newsnap

This mechanism is sufficient for the simplest kind of automation, and may be sufficient if wrapped in programmatic logic in a higher-level shell scripting language on a client, but it generally leaves much to be desired.


While batching commands is sufficient for the simplest of operations, it can be tedious to wrap in programmatic logic. For example, if you want to get information on the space usage for every share, you must have many different invocations of the CLI, wrapped in a higher level language on the client that parsed the output of specific commands. This results in slow, brittle automation infrastructure. To allow for faster and most robust automation, the appliance has a rich scripting environment based on ECMAScript 3. An ECMAScript tutorial is beyond the scope of this document, but it is a dynamically typed language with a C-like syntax that allows for:

The Script Environment

In the CLI, enter the script environment using the script command:

dory:> script
("." to run)> 

As the script environment prompt, you can input your script, finally entering "." alone on a line to execute it:

dory:> script
("." to run)> for (i = 10; i > 0; i--)
("." to run)>    printf("%d... ", i);
("." to run)> printf("Blastoff!\n");
("." to run)> .
10... 9... 8... 7... 6... 5... 4... 3... 2... 1... Blastoff!

If your script is a single line, you can simply provide it as an argument to the script command, making for an easy way to explore scripting:

dory:> script print("It is now " + new Date())
It is now Tue Oct 14 2009 05:33:01 GMT+0000 (UTC)

Interacting with the System

Of course, scripts are of little utility unless they can interact with the system at large. There are several built-in functions that allow your scripts to interact with the system:

Gets the value of the specified property. Note that this function returns the value in native form, e.g. dates are returned as Date objects.
Returns an array of tokens corresponding to the dynamic children of the current context.
Runs the specified command in the shell, returning any output as a string. Note that if the output contains multiple lines, the returned string will contain embedded newlines.
Returns an array of the property names for the current node.
Takes two string arguments, setting the specified property to the specified value.
The Run Function

The simplest way for scripts to interact with the larger system is to use the "run" function: it takes a command to run, and returns the output of that command as a string. For example:

dory:> configuration version script dump(run('get boot_time'))
'                     boot_time = 2009-10-12 07:02:17\n'

The built-in dump function dumps the argument out, without expanding any embedded newlines. ECMAScript's string handling facilities can be used to take apart output. For example, splitting the above based on whitespace:

dory:> configuration version script dump(run('get boot_time').split(/\s+/))
[&#39;', 'boot_time', '=', '2009-10-12', '07:02:17', &#39;']
The Get Function

The run function is sufficiently powerful that it may be tempting to rely exclusively on parsing output to get information about the system -- but this has the decided disadvantage that it leaves scripts parsing human-readable output that may or may not change in the future. To more robustly gather information about the system, use the built-in "get" function. In the case of the boot_time property, this will return not the string but rather the ECMAScript Date object, allowing the property value to be manipulated programmatically. For example, you might want to use the boot_time property in conjunction with the current time to determine the time since boot:

       run('configuration version');
       now = new Date();
       uptime = (now.valueOf() - get('boot_time').valueOf()) / 1000; 
       printf('up %d day%s, %d hour%s, %d minute%s, %d second%s\n',
           d = uptime / 86400, d < 1 || d >= 2 ? 's' : '',
           h = (uptime / 3600) % 24, h < 1 || h >= 2 ? 's': '',
           m = (uptime / 60) % 60, m < 1 || m >= 2 ? 's': '',
           s = uptime % 60, s < 1 || s >= 2 ? 's': '');

Assuming the above is saved as a "uptime.aksh", you could run it this way:

% ssh root@dory < uptime.aksh
Pseudo-terminal will not be allocated because stdin is not a terminal.
up 2 days, 10 hours, 47 minutes, 48 seconds

The message about pseudo-terminal allocation is due to the ssh client; the issue that this message refers to can be dealt with by specifying the "-T" option to ssh.

The List Function

In a context with dynamic children, it can be very useful to iterate over those children programmatically. This can be done by using the list function, which returns an array of dynamic children. For example, following is a script that iterates over every share in every project, printing out the amount of space consumed and space available:

       projects = list();

       for (i = 0; i < projects.length; i++) {
               run('select ' + projects[i]);
               shares = list();

               for (j = 0; j < shares.length; j++) {
                       run('select ' + shares[j]);
                       printf("%s/%s %1.64g %1.64g\n", projects[i], shares[j],
                           get('space_data'), get('space_available'));
                       run('cd ..');

               run('cd ..');

Here's the output of running the script, assuming it were saved to a file named "space.aksh":

% ssh root@koi < space.aksh
admin/accounts 18432 266617007104
admin/exports 18432 266617007104
admin/primary 18432 266617007104
admin/traffic 18432 266617007104
admin/workflow 18432 266617007104
aleventhal/hw_eng 18432 266617007104
bcantrill/analytx 1073964032 266617007104
bgregg/dashbd 18432 266617007104
bgregg/filesys01 26112 107374156288
bpijewski/access_ctrl 18432 266617007104

If one would rather a "pretty printed" (though more difficult to handle programmatically) variant of this, one could directly parse the output of the get command:

       projects = list();

       printf('%-40s %-10s %-10s\n', 'SHARE', 'USED', 'AVAILABLE');

       for (i = 0; i < projects.length; i++) {
               run('select ' + projects[i]);
               shares = list();

               for (j = 0; j < shares.length; j++) {
                       run('select ' + shares[j]);

                       share = projects[i] + '/' + shares[j];
                       used = run('get space_data').split(/\s+/)[3];
                       avail = run('get space_available').split(/\s+/)[3];

                       printf('%-40s %-10s %-10s\n', share, used, avail);
                       run('cd ..');

               run('cd ..');

And here's some of the output of running this new script, assuming it were named "prettyspace.aksh":

% ssh root@koi < prettyspace.aksh
SHARE                                    USED       AVAILABLE 
admin/accounts                           18K        248G      
admin/exports                            18K        248G      
admin/primary                            18K        248G      
admin/traffic                            18K        248G      
admin/workflow                           18K        248G      
aleventhal/hw_eng                        18K        248G      
bcantrill/analytx                        1.00G      248G      
bgregg/dashbd                            18K        248G       
bgregg/filesys01                         25.5K      100G      
bpijewski/access_ctrl                    18K        248G      
The Children Function

Even in a context with static children, it can be useful to iterate over those children programmatically. This can be done by using the children function, which returns an array of static children. For example, here's a script that iterates over every service, printing out the status of the service:

configuration services
       var svcs = children();
       for (var i = 0; i < svcs.length; ++i) {
                try {
                        printf("%-10s %s\n", svcs[i], get('<status>'));
                } catch (err) { }

Here's the output of running the script, assuming it were saved to a file named "svcinfo.aksh":

% ssh root@koi < space.aksh
cifs       disabled
dns        online
ftp        disabled
http       disabled
identity   online
idmap      online
ipmp       online
iscsi      online
ldap       disabled
ndmp       online
nfs        online
nis        online
ntp        online
scrk       online
sftp       disabled
smtp       online
snmp       disabled
ssh        online
tags       online
vscan      disabled
The Choices Function

The choices function returns an array of the valid property values for any property for which the set of values is known and enumerable. For example, the following script retrieves the list of all pools on the shares node using the choices function and then iterates all pools to list projects and shares along with the available space.

fmt = '%-40s %-15s %-15s\n';
printf(fmt, 'SHARE', 'USED', 'AVAILABLE');
run('cd /');
pools = choices('pool');
for (p = 0; p < pools.length; p++) {
        set('pool', pools[p]);
        projects = list();
        for (i = 0; i < projects.length; i++) {
                run('select ' + projects[i]);
                shares = list();
                for (j = 0; j < shares.length; j++) {
                        run('select ' + shares[j]);
                        share = pools[p] + ':' + projects[i] + '/' + shares[j];
                        printf(fmt, share, get('space_data'),
                        run('cd ..');
                run('cd ..');

Here is the output of running the script:

SHARE                                    USED            AVAILABLE     
pond:projectA/fs1                        31744           566196178944  
pond:projectA/fs2                        31744           566196178944  
pond:projectB/lun1                       21474836480     587670999040  
puddle:deptA/share1                      238475          467539219283
puddle:deptB/share1                      129564          467539219283
puddle:deptB/share2                      19283747        467539219283

Generating Output

Reporting state on the system requires generating output. Scripts have several built-in functions made available to them to generate output:

Dumps the specified argument to the terminal, without expanding embedded newlines. Objects will be displayed in a JSON-like format. Useful for debugging.
Prints the specified object as a string, followed by a newline. If the object does not have a toString method, it will be printed opaquely.
Like C's printf(3C), prints the specified arguments according to the specified formatting string.

Dealing with Errors

When an error is generated, an exception is thrown. The exception is generally an object that contains the following members:

Exceptions can be caught and handled, or they may be thrown out of the script environment. If a script environment has an uncaught exception, the CLI will display the details. For example:

dory:> script run('not a cmd')
error: uncaught error exception (code EAKSH_BADCMD) in script: invalid command
       "not a cmd" (encountered while attempting to run command "not a cmd")

You could see more details about the exception by catching it and dumping it out:

dory:> script try { run('not a cmd') } catch (err) { dump(err); }
   toString: <function>,
   code: 10004,
   message: 'invalid command "not a cmd" (encountered while attempting to
                      run command "not a cmd")'

This also allows you to have rich error handling, for example:

#!/usr/bin/ksh -p

ssh -T root@dory <<EOF
       try {
               run('shares select default select $1');
       } catch (err) {
               if (err.code == EAKSH_ENTITY_BADSELECT) {
                       printf('error: "$1" is not a share in the ' +
                           'default project\n');

               throw (err);

       printf('"default/$1": compression is %s\n', get('compression'));

If this script is named "share.ksh" and run with an invalid share name, a rich error message will be generated:

% ksh ./share.ksh bogus
error: "bogus" is not a share in the default project

Automating Access

Whether using batched commands or scripting (or some combination), automated infrastructure requires automated access to the appliance. This should be done by creating users, giving them necessary authorizations, and uploading SSH keys.