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

Document Information

Preface

1.  Introduction

Overview

Introduction

Platforms

Expansion Storage

Protocols

Key Features

Data Services

Availability

Browser User Interface (BUI)

Command Line Interface (CLI)

User Interface

Browser User Interface (BUI)

Command Line Interface (CLI)

Browsers

Supported Browsers

Tier 1

Tier 2

Unsupported Browsers

Main Window

Overview

Masthead

Navigation

Alerts

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

Permissions

Editing Share Properties

Viewing List Item Controls

Modal Dialogs

Icons

General Usage

Status

Basic Usage

Networking

Dashboard Thresholds

Analytics

Identity Mapping

Miscellaneous Icons

CLI

CLI Introduction

Logging Into the CLI

Contexts

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

Properties

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

Scripting

Batching Commands

Scripting

The Script Environment

Interacting with the System

The Run Function

The Get Function

The List Function

The Children Function

Generating Output

Dealing with Errors

Automating Access

Workflows

Introduction

Workflow execution context

Workflow parameters

Constrained parameters

Optional parameters

Error Handling

Input validation

Execution auditing

Execution reporting

Versioning

Appliance versioning

Workflow versioning

Workflows as alert actions

Alert action execution context

Auditing alert actions

Using Scheduled Workflows

Using the CLI

Coding the Schedule

Example: device type selection

BUI

CLI

Downloading workflows

Viewing workflows

Executing workflows

2.  Status

3.  Configuration

4.  Services

5.  Shares

6.  Analytics

7.  Application Integration

Glossary

Index

Workflows

Introduction

A workflow is a script that is uploaded to and managed by the appliance by itself. Workflows can be parameterized and executed in a first-class fashion from either the browser interface or the command line interface. Workflows may also be optionally executed as alert actions or at a designated time. As such, workflows allow for the appliance to be extended in ways that capture specific policies and procedures, and can be used (for example) to formally encode best practices for a particular organization or application.

A workflow is embodied in a valid ECMAscript file, containing a single global variable, workflow. This is an Object that must contain at least three members:

Required member
Type
Description
name
String
Name of the workflow
description
String
Description of workflow
execute
Function
Function that executes the workflow

Here is the canonically trivial workflow:

var workflow = {
       name: 'Hello world',
       description: 'Bids a greeting to the world',
       execute: function () { return ('hello world!') }
};

Uploading this workflow will result in a new workflow named "Hello world"; executing the workflow will result in the output "hello world!"

Workflow execution context

Workflows execute asynchronously in the appliance shell, running (by default) as the user executing the workflow. As such, workflows have at their disposal the appliance scripting facility, and may interact with the appliance just as any other instance of the appliance shell. That is, workflows may execute commands, parse output, modify state, and so on. Here is a more complicated example that uses the run function to return the current CPU utilization:

var workflow = {
       name: 'CPU utilization',
       description: 'Displays the current CPU utilization',
       execute: function () {
               run('analytics datasets select name=cpu.utilization');
               cpu = run('csv 1').split('\n')[1].split(',');
               return ('At ' + cpu[0] + ', utilization is ' + cpu[1] + '%');
       }
};

Workflow parameters

Workflows that do not operate on input have limited scope; many workflows need to be parameterized to be useful. This is done by adding a parameters member to the global workflow object. The parameters member is in turn an object that is expected to have a member for each parameter. Each parameters member must have the following members:

Required Member
Type
Description
label
String
Label to adorn input of workflow parameter
type
String
Type of workflow parameter

The type member must be set to one of these types:

Type name
Description
Boolean
A boolean value
ChooseOne
One of a number of specified values
EmailAddress
An e-mail address
File
A file to be transferred to the appliance
Host
A valid host, as either a name or dotted decimal
HostName
A valid hostname
HostPort
A valid, available port
Integer
An integer
NetAddress
A network address
NodeName
A name of a network node
NonNegativeInteger
An integer that is greater than or equal to zero
Number
Any number -- including floating point
Password
A password
Permissions
POSIX permissions
Port
A port number
Size
A size
String
A string
StringList
A list of strings

Based on the specified types, an appropriate input form will be generated upon execution of the workflow. For example, here is a workflow that has two parameters, the name of a business unit (to be used as a project) and the name of a share (to be used as the share name):

var workflow = {
       name: 'New share',
       description: 'Creates a new share in a business unit',
       parameters: {
               name: {
                       label: 'Name of new share',
                       type: 'String'
               },
               unit: {
                       label: 'Business unit',
                       type: 'String'
               }
       },
       execute: function (params) {
               run('shares select ' + params.unit);
               run('filesystem ' + params.name);
               run('commit');
               return ('Created new share "' + params.name + '"');
       }
};

If you upload this workflow and execute it, you will be prompted with a dialog box to fill in the name of the share and the business unit. When the share has been created, a message will be generated indicating as much.

Constrained parameters

For some parameters, one does not wish to allow an arbitrary string, but wishes to rather limit input to one of a small number of alternatives. These parameters should be specified to be of type ChooseOne, and the object containing the parameter must have two additional members:

Required Member
Type
Description
options
Array
Specifies the valid options
optionlabels
Array
Specifies the labels associated with the options specified in options

Using the ChooseOne parameter type, we can enhance the previous example to limit the business unit to be one of a small number of predefined values:

var workflow = {
    name: 'Create share',
    description: 'Creates a new share in a business unit',
    parameters: {
        name: {
            label: 'Name of new share',
            type: 'String'
        },
        unit: {
            label: 'Business unit',
            type: 'ChooseOne',
            options: [ 'development', 'finance', 'qa', 'sales' ],
            optionlabels: [ 'Development', 'Finance',
                'Quality Assurance', 'Sales/Administrative' ],
        }
    },
    execute: function (params) {
        run('shares select ' + params.unit);
        run('filesystem ' + params.name);
        run('commit');
        return ('Created new share "' + params.name + '"');
    }
};

When this workflow is executed, the unit parameter will not be entered by hand -- it will be selected from the specified list of possible options.

Optional parameters

Some parameters may be considered optional in that the UI should not mandate that these parameters are set to any value to allow execution of the workflow. Such a parameter is denoted via the optional field of the parameters member:

Optional Member
Type
Description
optional
Boolean
If set to true, denotes that the parameter need not be set; the UI may allow the workflow to be executed without a value being specified for the parameter.

If a parameter is optional and is unset, its member in the parameters object passed to the execute function will be set to undefined.

Error Handling

If, in the course of executing a workflow, an error is encountered, an exception will be thrown. If the exception is not caught by the workflow itself (or if the workflow throws an exception that is not otherwise caught), the workflow will fail, and the information regarding the exception will be displayed to the user. To properly handle errors, exceptions should be caught and processed. For example, in the previous example, an attempt to create a share in a non-existent project results in an uncaught exception. This example could be modified to catch the offending error, and create the project in the case that it doesn't exist:

var workflow = {
    name: 'Create share',
    description: 'Creates a new share in a business unit',
    parameters: {
        name: {
            label: 'Name of new share',
            type: 'String'
        },
        unit: {
            label: 'Business unit',
            type: 'ChooseOne',
            options: [ 'development', 'finance', 'qa', 'sales' ],
            optionlabels: [ 'Development', 'Finance',
                'Quality Assurance', 'Sales/Administrative' ],
        }
    },
    execute: function (params) {
        try {
            run('shares select ' + params.unit);
        } catch (err) {
            if (err.code != EAKSH_ENTITY_BADSELECT)
                throw (err);

            /*
             * We haven't yet created a project that corresponds to
             * this business unit; create it now.
             */
            run('shares project ' + params.unit);
            run('commit');
            run('shares select ' + params.unit);
        }

        run('filesystem ' + params.name);
        run('commit');
        return ('Created new share "' + params.name + '"');
    }
};

Input validation

Workflows may optionally validate their input by adding a validate member that takes as a parameter an object that contains the workflow parameters as members. The validate function should return an object where each member is named with the parameter that failed validation, and each member's value is the validation failure message to be displayed to the user. To extend our example to give a crisp error if the user attempts to create an extant share:

var workflow = {
    name: 'Create share',
    description: 'Creates a new share in a business unit',
    parameters: {
        name: {
            label: 'Name of new share',
            type: 'String'
        },
        unit: {
            label: 'Business unit',
            type: 'ChooseOne',
            options: [ 'development', 'finance', 'qa', 'sales' ],
            optionlabels: [ 'Development', 'Finance',
                'Quality Assurance', 'Sales/Administrative' ],
        }
    },
    validate: function (params) {
        try {
            run('shares select ' + params.unit);
            run('select ' + params.name);
        } catch (err) {
            if (err.code == EAKSH_ENTITY_BADSELECT)
                return;
        }

        return ({ name: 'share already exists' });
    },
    execute: function (params) {
        try {
            run('shares select ' + params.unit);
        } catch (err) {
            if (err.code != EAKSH_ENTITY_BADSELECT)
                throw (err);

            /*
             * We haven't yet created a project that corresponds to
             * this business unit; create it now.
             */
            run('shares project ' + params.unit);
            set('mountpoint', '/export/' + params.unit);
            run('commit');
            run('shares select ' + params.unit);
        }

        run('filesystem ' + params.name);
        run('commit');
        return ('Created new share "' + params.name + '"');
    }
};

Execution auditing

Workflows may emit audit records by calling the audit function. The audit function's only argument is a string that is to be placed into the audit log.

Execution reporting

For complicated workflows that may require some time to execute, it can be useful to provide clear progress to the user executing the workflow. To allow the execution of a workflow to be reported in this way, the execute member should return an array of steps. Each array element must contain the following members:

Required Member
Type
Description
step
String
String that denotes the name of the execution step
execute
Function
Function that executes the step of the workflow

As with the execute function on the workflow as a whole, the execute member of each step takes as its argument an object that contains the parameters to the workflow. As an example, here is a workflow that creates a new project, share, and audit record over three steps:

var steps = [ {
    step: 'Checking for associated project',
    execute: function (params) {
        try {
            run('shares select ' + params.unit);
        } catch (err) {
            if (err.code != EAKSH_ENTITY_BADSELECT)
                throw (err);

            /*
             * We haven't yet created a project that corresponds to
             * this business unit; create it now.
             */
            run('shares project ' + params.unit);
            set('mountpoint', '/export/' + params.unit);
            run('commit');
            run('shares select ' + params.unit);
        }
    }
}, {
    step: 'Creating share',
    execute: function (params) {
        run('filesystem ' + params.name);
        run('commit');
    }
}, {
    step: 'Creating audit record',
    execute: function (params) {
        audit('created "' + params.name + '" in "' + params.unit);
    }
} ];

var workflow = {
    name: 'Create share',
    description: 'Creates a new share in a business unit',
    parameters: {
        name: {
            label: 'Name of new share',
            type: 'String'
        },
        unit: {
            label: 'Business unit',
            type: 'ChooseOne',
            options: [ 'development', 'finance', 'qa', 'sales' ],
            optionlabels: [ 'Development', 'Finance',
                'Quality Assurance', 'Sales/Administrative' ],
        }
    },
    validate: function (params) {
        try {
            run('shares select ' + params.unit);
            run('select ' + params.name);
        } catch (err) {
            if (err.code == EAKSH_ENTITY_BADSELECT)
                return;
        }

        return ({ name: 'share already exists' });
    },
    execute: function (params) { return (steps); }
};

Versioning

There are two aspects of versioning with respect to workflows: the first is the expression of the version of the appliance software that the workflow depends on, and the second is the expression of the version of the workflow itself. Versioning is expressed through two optional members to the workflow:

Optional Member
Type
Description
required
String
The minimum version of the appliance software required to run this workflow, including the minimum year, month, day, build and branch.
version
String
Version of this workflow, in dotted decimal (major.minor.micro) form.
Appliance versioning

To express a minimally required version of the appliance software, add the optional required field to your workflow. The appliance is versioned in terms of the year, month and day on which the software was built, followed by a build number and then a branch number, expressed as "year.month.day.build-branch". For example "2009.04.10,12-0" would be the twelfth build of the software originally build on April 10th, 2009. To get the version of the current appliance kit software, run the "configuration version get version" CLI command, or look at the "Version" field in the system view in the BUI. Here's an example of using the required field:

var workflow = {
    name: 'Configure FC',
    description: 'Configures fibre channel target groups',
        required: '2009.12.25,1-0',
        ...

If a workflow requires a version of software that is newer than the version loaded on the appliance, the attempt to upload the workflow will fail with a message explaining the mismatch.

Workflow versioning

In addition to specifying the required version of the appliance software, workflows themselves may be versioned with the version field. This string denotes the major, minor and micro numbers of the workflow version, and allows multiple versions of the same workflow to exist on the machine. When uploading a workflow, any compatible, older versions of the same workflow are deleted. A workflow is deemed to be compatible if it has the same major number, and a workflow is considered to be older if it has a lower version number. Therefore, uploading a workflow with a version of "2.1" will remove the same workflow with version "2.0" (or version "2.0.1") but not "1.2" or "0.1".

Workflows as alert actions

Workflows may be optionally executed as alert actions. To allow a workflow to be eligible as an alert action, its alert action must be set to true.

Alert action execution context

When executed as alert actions, workflows assume the identity of the user that created them. For this reason, any workflow that is to be eligible as an alert action must set setid to true. Alert actions have a single object parameter that has the following members:

Required Member
Type
Description
class
String
The class of the alert.
code
String
The code of the alert.
items
Object
An object describing the alert.
timestamp
Date
Time of alert.

The items member of the parameters object has the following members:

Required Member
Type
Description
url
String
The URL of the web page describing the alert
action
String
The action that should be taken by the user in response to the alert.
impact
String
The impact of the event that precipitated the alert.
description
String
A human-readable string describing the alert.
severity
String
The severity of the event that precipitated the alert.
Auditing alert actions

Workflows executing as alert actions may use the audit function to generate audit log entries. It is recommended that any relevant debugging information be generated to the audit log via the audit function. For example, here is a workflow that executes failover if in the clustered state -- but audits any failure to reboot:

var workflow = {
       name: 'Failover',
       description: 'Fail the node over to its clustered peer',
       alert: true,
       setid: true,
       execute: function (params) {
               /*
                * To failover, we first confirm that clustering is configured
                * and that we are in the clustered state.  We then reboot,
                * which will force our peer to takeover.  Note that we're
                * being very conservative by only rebooting if in the
                * AKCS_CLUSTERED state:  there are other states in which it
                * may well be valid to failback (e.g., we are in AKCS_OWNER,
                * and our peer is AKCS_STRIPPED), but those states may also
                * indicate aberrent operation, and we therefore refuse to
                * failback.  (Even in an active/passive clustered config, a
                * FAILBACK should always be performed to transition the
                * cluster peers from OWNER/STRIPPED to CLUSTERED/CLUSTERED.)
                */
               var uuid = params.uuid;
               var clustered = 'AKCS_CLUSTERED';

               audit('attempting failover in response to alert ' + uuid);

               try {
                       run('configuration cluster');
               } catch (err) {
                       audit('could not get clustered state; aborting');
                       return;
               }

               if ((state = get('state')) != clustered) {
                       audit('state is ' + state + '; aborting');
                       return;
               }

               if ((state = get('peer_state')) != clustered) {
                       audit('peer state is ' + state + '; aborting');
                       return;
               }

               run('cd /');
               run('confirm maintenance system reboot');
       }
};

Using Scheduled Workflows

Workflows can be started via a timer event by setting up a schedule for them. The property scheduled has to be added to the Workflow Object and needs to be set to true. Schedules can either be created via the CLI once a workflow is loaded into the appliance or an array type property named schedule can be added to the Object Workflow.

Using the CLI

Once a workflow has been loaded into the appliance a schedule can be defined for it via the CLI interface as follows:

dory:> maintenance workflows
dory:maintenance workflows> "select workflow-002'''
dory:maintenance workflow-002> schedules
dory:maintenance workflow-002 schedules>create
dory:maintenance workflow-002 schedule (uncommitted)> set frequency=day
                    frequency = day (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> set hour=10
                         hour = 10 (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> set minute=05
                       minute = 05 (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> commit
dory:maintenance workflow-002 schedules> list
NAME                 FREQUENCY            DAY                  HH:MM
schedule-001         day                  -                    10:05
dory:maintenance workflow-002 schedules> create
dory:maintenance workflow-002 schedule (uncommitted)> set frequency=week
                    frequency = week (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> set day=Monday
                           day = Monday (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> set hour=13
                          hour = 13 (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> set minute=15
                       minute = 15 (uncommitted)
dory:maintenance workflow-002 schedule (uncommitted)> commit
dory:maintenance workflow-002 schedules> list
NAME                 FREQUENCY            DAY                  HH:MM
schedule-001         day                             -                      10:05
schedule-002         week                     Monday               13:15
dory:maintenance workflow-002 schedules>

Each schedule entry consists of the following properties:

Property
Type
Description
NAME
String
Name of the schedule, system generated
frequency
String
minute,halfhour,hour,day,week,month
day
String
Specifies specific day and can be set to: Monday, Tuesday,Wednesday,Thursday,Friday,Saturday or Sunday. Can be set when frequency is set to week or month
hour
String
00-23, Specifies the hour part of the schedule and can be specified when the frequency is set to a day,week or month.
minute
String
00-59, Specifies the minute part of the schedule.
Coding the Schedule

Schedules can also be specified in the workflow code as a property in the Object workflow. The property syntax used here differs from the CLI schedule creation. Here three properties are used,

Property
Type
Description
offset
Number
Determines the starting point in the defined period
period
Number
Defines the frequency of the Schedule
unit
String
Specifies if either seconds or month are used as unit in the offset and period definition

The following code example illustrates the use of the properties. Note that inline arithmetic helps to make the offset and period declarations more readable.

// Example of using Schedule definitions within a workflow 
var MyTextObject = {     
    MyVersion:    '1.0', 
    MyName:        'Example 9',
    MyDescription:     'Example of use of Timer',
    Origin:        'Oracle'
 }; 
var MySchedules = [
    // half hr interval
    { offset: 0, period: 1800, units: "seconds" },
    // offset 2 days, 4hr, 30min , week interval
    {offset: 2*24*60*60+4*60*60+30*60, period: 604800,units: "seconds" } 
];
var workflow = { 
    name:        MyTextObject.MyName, 
    description:    MyTextObject.MyDescription, 
    version:    MyTextObject.MyVersion,
    alert:        false,
    setid:        true, 
    schedules:     MySchedules,
    scheduled:    true,
    origin:        MyTextObject.Origin, 
    execute:    function () {             
                audit('workflow started for timer; ');
                 } 
            }
 }; 

The property units in the Object MySchedules specifies the type of units used for the properties offset and period. They can be set to either seconds or month. The property period specifies the frequency of the event and the offset specifies the units within the period. In the above example the period in the second schedule is set for a week, starting at the second day , at 4:30. Multiple schedules can be defined in the property schedules. Now for some strange reason day zero is Thursday. When the above workflow is loaded into the appliance the schedule and be listed from the CLI interface and will show:

dory:> maintenance workflows
dory:maintenance workflows>list
WORKFLOW     NAME                 OWNER      SETID ORIGIN
workflow-002      Example 9            root             true     Oracle
dory:maintenance workflows> select workflow-002
dory:maintenance workflow-002> schedules
dory:maintenance workflow-002 schedules> list
NAME                 FREQUENCY            DAY                  HH:MM
schedule-000         halfhour             -                    --:00
schedule-001         week                 Saturday             04:30
dory:maintenance workflow-002 schedules>

Example: device type selection

Here is an example workflow that creates a worksheet based on a specified drive type:

var steps = [ {
    step: 'Checking for existing worksheet',
    execute: function (params) {
        /*
         * In this step, we're going to see if the worksheet that
         * we're going to create already exists.  If the worksheet
         * already exists, we blow it away if the user has indicated
         * that they desire this behavior.  Note that we store our
         * derived worksheet name with the parameters, even though
         * it is not a parameter per se; this is explicitly allowed,
         * and it allows us to build state in one step that is
         * processed in another without requiring additional global
         * variables.
         */
        params.worksheet = 'Drilling down on ' + params.type + ' disks';

        try {
            run('analytics worksheets select name="' +
                params.worksheet + '"');

            if (params.overwrite) {
                run('confirm destroy');
                return;
            }
                
            throw ('Worksheet called "' + params.worksheet +
                '" already exists!');
        } catch (err) {
            if (err.code != EAKSH_ENTITY_BADSELECT)
                throw (err);
        }
    }
 }, {
    step: 'Finding disks of specified type',
    execute: function (params) {
        /*
         * In this step, we will iterate over all chassis, and for
         * each chassis iterates over all disks in the chassis,
         * looking for disks that match the specified type.
         */
        var chassis, name, disks;
        var i, j;

        run('cd /');
        run('maintenance hardware');

        chassis = list();
        params.disks = [];

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

            name = get('name');
            run('select disk');
            disks = list();

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

                if (get('use') == params.type) {
                    params.disks.push(name + '/' +
                        get('label'));
                }

                run('cd ..');
            }

            run('cd ../..');
        }

        if (params.disks.length === 0)
            throw ('No ' + params.type + ' disks found');
        run('cd /');
    }
 }, {
    step: 'Creating worksheet',
    execute: function (params) {
        /*
         * In this step, we're ready to actually create the worksheet
         * itself:  we have the disks of the specified type and
         * we know that we can create the worksheet.  Note that we
         * create several datasets:  first, I/O bytes broken down
         * by disk, with each disk of the specified type highlighted
         * as a drilldown.  Then, we create a separate dataset for
         * each disk of the specified type.  Finally, note that we
         * aren't saving the datasets -- we'll let the user do that
         * from the created worksheet if they so desire.  (It would
         * be straightforward to add a boolean parameter to this
         * workflow that allows that last behavior to be optionally
         * changed.)
         */
        var disks = [], i;

        run('analytics worksheets');
        run('create "' + params.worksheet + '"');
        run('select name="' + params.worksheet + '"');
        run('dataset');
        run('set name=io.bytes[disk]');

        for (i = 0; i < params.disks.length; i++)
            disks.push('"' + params.disks[i] + '"');

        run('set drilldowns=' + disks.join(','));
        run('commit');

        for (i = 0; i < params.disks.length; i++) {
            run('dataset');
            run('set name="io.bytes[disk=' +
                params.disks[i] + ']"');
            run('commit');
        }
    }
} ];

var workflow = {
    name: 'Disk drilldown',
    description: 'Creates a worksheet that drills down on system, ' +
        'cache, or log devices',
    parameters: {
        type: {
            label: 'Create a new worksheet drilling down on',
            type: 'ChooseOne',
            options: [ 'cache', 'log', 'system' ],
            optionlabels: [ 'Cache', 'Log', 'System' ]
        },
        overwrite: {
            label: 'Overwrite the worksheet if it exists',
            type: 'Boolean'
        }
    },
    execute: function (params) { return (steps); }
};

BUI

Workflows are uploaded to the appliance by clicking on the plus icon, and they are executed by clicking on the row specifying the workflow.

CLI

Workflows are manipulated in the maintenance workflows section of the CLI.

Downloading workflows

Workflows are downloaded to the appliance via the download command, which is similar to the mechanism used for software updates:

dory:maintenance workflows> download
dory:maintenance workflows download (uncommitted)> get
                          url = (unset)
                         user = (unset)
                     password = (unset)

You must set the "url" property to be a valid URL for the workflow. This may be either local to your network or over the internet. The URL can be either HTTP (beginning with "http://") or FTP (beginning with "ftp://"). If user authentication is required, it may be a part of the URL (e.g. "ftp://myusername:mypasswd@myserver/export/foo"), or you may leave the username and password out of the URL and instead set the user and password properties.

dory:maintenance workflows download (uncommitted)> set url=
   ftp://foo/example1.akwf
                          url = ftp://foo/example1.akwf
dory:maintenance workflows download (uncommitted)> set user=bmc
                         user = bmc
dory:maintenance workflows download (uncommitted)> set password
Enter password: 
                     password = ********
dory:maintenance workflows download (uncommitted)> commit
Transferred 138 of 138 (100%) ... done

Viewing workflows

To list workflows, use the list command from the maintenance workflows context:

dory:maintenance workflows> list
WORKFLOW     NAME                 OWNER      SETID ORIGIN              
workflow-000 Hello world          root       false <local>             

To select a workflow, use the select command:

dory:maintenance workflows> select workflow-000 
dory:maintenance workflow-000>

To get a workflow's properties, use the get command from within the context of the selected workflow:

dory:maintenance workflow-000> get
                         name = Hello world
                  description = Bids a greeting to the world
                        owner = root
                       origin = <local>
                        setid = false
                         alert = false
               scheduled = false

Executing workflows

To execute a workflow, use the execute command from within the context of the selected workflow. If the workflow takes no parameters, it will simply execute:

dory:maintenance workflow-000> execute 
hello world!

If the workflow takes parameters, the context will become a captive context in which parameters must be specified:

dory:maintenance workflow-000> execute 
dory:maintenance workflow-000 execute (uncommitted)> get
                         type = (unset)
                    overwrite = (unset)

Any attempt to commit the execution of the workflow without first setting the requisite parameters will result in an explicit failure:

dory:maintenance workflow-000 execute (uncommitted)> commit
error: cannot execute workflow without setting property "type"

To execute the workflow, set the specified parameters, and then use the commit command:

dory:maintenance workflow-000 execute (uncommitted)> set type=system 
                         type = system
dory:maintenance workflow-000 execute (uncommitted)> set overwrite=true
                    overwrite = true
dory:maintenance workflow-000 execute (uncommitted)> commit

If the workflow has specified steps, those steps will be displayed via the CLI, e.g.:

dory:maintenance workflow-000 execute (uncommitted)> commit
Checking for existing worksheet ... done
Finding disks of specified type ... done
Creating worksheet ... done