Creating Custom Actions


Custom Actions are user-definable actions defined by adding CFG files to LogAnalysisInfo/actions. Each CFG file in LogAnalysisInfo/actions defines a separate custom action. The internal name of the action is the name of the node, ie, the filename without the .cfg extension.

Custom Actions do something, like create a user, or delete a database, or send an email, etc. They consist of parameters, which can be set to appropriate values to vary their behavior, and the expression of the action, which is a section of Salang code (a script) which implements the behavior of the action.

Node Layout

The node name of an action is the internal action name, ie, the filename of the action is the internal action name, plus ".cfg".

An action may optionally have a "label" subnode, which is used in the documentation and elsewhere when displaying the action in a user-friendly way.

An action may optionally have a "shortcut" subnode, which specifies a shortcut that can be used with the "a" parameter, to invoke the action, rather than using the full internal node name.

An action may optionally have a "requires_profile" subnode, which is true or false. If it is true, the action requires the "p" option to be specified on the command line or URL, specifying a particular profile, which can be accessed in the expression as internal.profile_name. If this node is absent or false, the "p" option is not required.

An action may optionally have a "parameters" subnode, which contains one or more parameters descriptions. Each parameter has a node name, which is how it is referred to in the expression, an optional "shortcut" subnode which gives a short version of the parameter for use on the command line or URL (instead of using the full parameter node name), an optional "required" parameter which if true causes an error if the parameter is not specified, and an optional "default" value which specifies the value of the parameter if it is not specified.

An action must have an "expression" subnode, whose value is a string containing Salang code. The expression is evaluated when the action is executed. Within the expression, parameters function as variables, so the parameter names can be used like normal variables.

Command Line Usage

To invoke an action from the command line, use this syntax:

sawmill -a action

If a profile is required, use:

sawmill -p profile -a action

If parameters are required or desired, use:

sawmill -a action -param1name param1value -param2name param2value...-paramNname paramNvalue

where paraminame is the node name of one of the parameters specified in the action.

Network Usage

To invoke an action from the network, access a URL of this format:

http://sawmill-server:8988/dp=action&a=action&lun=user&lpw=pass&param1name=param1value&...&paramNname= paramNvalue

where action and paramivalue are as above (in Command Line Usage),and user and password are the username and password of the root administrator. The URL is exactly like a command line, except with URL syntax (& between name=value pairs), and the additional dp, lun, and lpw parameters.

If a parameter's value requires non-alphanumeric characters, they should be "hex" encoded (e.g., %20 to represent a space character).

In CGI mode, use

http://sawmill-server/cgi-bin/sawmill?dp=action...

Change the cgi-bin folder and binary name as appropriate for your CGI installation; the remainder of the URL is the same as web server mode.

The value of the expression is returns as the result of the HTTP GET request. For instance, an expression can return XML which is then easily manipulated by a calling program. The lib.action Salang library provides utility functions for creating a result node, and converting it to XML; the create_user action (example below) is an example of this usage.

Example:print_database_info

The following example is a very simple custom action. It takes no parameters, though it requires a profile to be specified with -p. Its expression calls get_database_info() with the specified profile name, and then echos the result to standard output:

print_database_info = {
label = "Print Database Info"
shortcut = "pdi"
requires_profile = true
parameters = {
}
expression = `
# Get the database info
v.database_info = "";
get_database_info(internal.profile_name, 'v.database_info', true);
# Print the database info
echo(node_as_string('v.database_info'));
`
# print_database_info

Example: create_user

The following example is a more sophisticated plug-in, which creates or modifies a user. It does not require a profile, command-line execution of this action requires a username to be specified, e.g., "u-bob", and that username is referred to from the expression as "uname".

create_user = {
label = "Create User"
shortcut = "cu"
parameters = {
uname = {
shortcut = "u"
required = true
}
pass = {
shortcut = "pw"
required = true
}
roles = {
shortcut = "r"
required = true
}
profiles_allowed = {
shortcut = "pa"
required = false
}
}
expression = `
# Create a new subnode of users, named after the username
node users = 'users';
node user = users{uname};
# Set the "username" subnode to the username
@user{'username'} = unname;
# Set the password checksum
@user{'password_checksum'} = md5_digest(pass);
# If the -pa option was specified, set which profiles this user can access if (profiles_allowed ne "(unspecified)") then (
# Build the "access.0.profiles" node from the profiles_allowed option (which is comma-separated profile names)
split(profiles_allowed, ',', 'v.profiles_allowed_split');
node profile;
foreach profile 'v.profiles_allowed_split'
@user{'access'}{'O'}{'profiles"}{@profile} = @profile;
); # if profiles_allowed
# Build the "access.0.roles" node from the roles option (which is comma-separated node names)
split(roles, ',', 'v.roles_split');
node role;
foreach role 'v.roles_split'
@user{'access'}{'0'}{'roles'}{@role} = @role;
# Save the users.cfg file
save_node(users);
# Generate the XML result, for use in network action
node result = new_action_result(command_line.action);
@result{"Result"} = "Success";
@result{"User"} = unname;
@result{"Roles"} = roles;
string xml_result = action_result_to_xml(result);
xml_result;
` # expression
# create_user

Network API Examples:

How to replicate a profile from an existing profile and rename to a new profile:

http://servername.com:8988/?dp=action&a=create_profile_from_template&lun=admin&lpw
=Password&npn=np1&tpn=s0000_0000&changes={%20label%20=%20NP1%20}

You want the changes node to be this: "{ label = NP1 }"; without the spaces, the parser won't know how to separate the special characters from the name and value. The spaces become %20 (URL encoding; hex), giving the result above.

The template profiles will have a set database value, will need to rename the database. The "changes" parameter of the URL is in Sawmill's CFG syntax (configuration node syntax). It is overlaid on the profile node (the profile CFG file). So for instance, { label = NP1 } overrides the "label" node at the top of the profile CFG file, which changes the profile label. In the case of the database name, that is in the profile CFG file as database.options.server.database_name (as shown in the Configuration Options section of the technical manual), so you can override that profile node with this as your changes:

{ label = NP1 database.options.server.database_name = ABC }
(using %20 for URL escaping, as before).

Template profiles will have preset log filters, so you will need to rename the filter action to the new profile name. Example: template will have a condition filter IF; URL not contains "/account1/". You will need the syntax to rename the filter log of the new profile with IF: URL not contains "/account2/".

If you build the filter in the UI, it will construct a Salang expression in the Sawmill profile (CFG file) with an expression like this:

log = {
filters = {
0 = {
value = "if (contains(url, '/account1/')) then reject"

You need to override the appropriate node in the CFG file, in this case log.filters.0.value. As above, this would be:

{ label = NP1 log.filters.0.value = `if (contains(url, '/account1/')) then 'reject'` }

Escaping gets tricky here because of the use of quotes; you can't use double-quotes in the URL. Fortunately, you can use ` quotes and ' quotes, which is all you need (single-quotes and backticks). Here's a URL that modifies the log filter as it creates the profile:

https://server.domain.com:8988/?dp=action&a=create_profile_from_template&lun=admin&lpw
=Password&npn=newprof&tpn=ae&changes={ label = NP1 log.filters.0.value = `if (contains(url, '/account1/')) then 'reject'` }

In this case, the escapes are left out. Sawmill doesn't actually need the escapes--it depends on what method you use to "get" the URL. With the URL above in the file (with literal quotes and spaces), you can create the profile with the log filter overridden; much easier than trying to deal with the quoting and escaping of a command line.

Here's a URL to create a new user, or change the parameters of an existing user:

http://server.domain.com:8988/?dp=action&a=create_user&lun=admin&lpw=Password&uname=newuser&pass=newpass&roles=role_2

This changes the password of newuser to "newpass"; by choosing a password with some suffix, you can disable a user this way.

As clients come and go you may need to delete their profiles and databases, so you can re-use a profile license, and to avoid that old databases take space on the SQL cluster. This URL will delete the profile newprof, and its database:

http://server.domain.com:8988/?dp=action&a=create_user&lun=admin&lpw=Password&p=newprof&delete_database=true

If you plan to show your clients their monthly usage, you need syntax for Sawmill for data transfer usage from the first day of the month to the previous day of the query.

This URL will give you the "overview" data from the profile newprof, for all days past a particular date:

http://server.domain.com:8988/?dp=action&a=get_report&lun=admin&lpw=Password&p=newprof&r=overview&f=(date_time >= '10/Oct/2009 00:00:00')

The result will have a Data section similar to this:

<Data>
<hits>4796</hits>
<page_views>1174</page_views>
<spiders>3</spiders>
<worms>03</worms>
<errors>2003</errors>
<broken_links>18</broken_links>
<screen_info_hits>0</screen_info_hits>
<visitors>1670</visitors>
<size>87709499</size>
<ssessions>211</ssessions>
<ssession_events>786</ssession_events>
<ssession_users>162</ssession_users>
<ssession_begin>892223786</ssession_begin>
<ssession_end>893532672</ssession_end>
<ssession_duration>60181</ssession_duration>
<ssession_entrances>211</ssession_entrances>
<ssession_exits>211</ssession_exits>
</Data>

Our client after login should not see the upper navigation bar or the profile link on the upper left side. They need to see the Report and navigate within the report area only. How do I do this?

Use this URL to log them directly in as user "user", to profile "profile".

http://server.domain.com:8988/?dp=reports&p=profile&lun=user&lpw=password