Here are some leading practices and guidelines for using your favorite scripting engines to help automate and customize NoTouch
Contents
- 1 NoTouch Center Scripting Interface
- 2 Scripting Engines
- 3 Relevant settings
- 4 Activating a script
- 5 Scripting functionality
- 6 Script programming guidelines
- 7 Sample scripts
NoTouch Center Scripting Interface
NoTouch Center has a decent scripting interface that allows to add custom functionality.
Types of script extension are:
- Parameter scripts providing dynamic "parameter default values"
- Task-based scripts such as startup and shutdown of NoTouch Center, or autoassign
- Protocol-based scripts that hook into the communication between NoTouch endpoints and NoTouch Center
- Scripted web "actions"
- Periodic script actions, such as running custom functionality hourly, daily, weekly ...
Some of the potential use cases are:
- Automatically set configuration values based on hardware. E.g. disable some features on older, weaker hardware.
- Selectively distribute firmware updates, not based on simple group membership or per-client setting, but instead on hardware parameters
- Set values based on pre-existing text lists (i.e. the script would evaluate a text file)
- Set values based on settings in a different database, e.g. an asset management system. In this case, the script would read from the other database and return desired values to NoTouch Center
- Automatically report device and device parameters into a different database, e.g. an asset management system. Whenever a client announces itself, a script could send important values into a third-party asset tracking tool.
- Extend the NoTouch Center GUI by programming our own script-based "servlets"
- Create URL-based actions that can be triggered manually or in an automated manner
Scripting Engines
By default, NoTouch Center comes with the engines for JavaScript and Lua. The standard Java scripting interface JSR-223 [1][2] is used, thus you can easily extend NoTouch Center to support other languages such as Python, Ruby, Clojure and many others that provide JSR-223 bindings [3].
Default scripting engines
- JavaScript/ECMAScript (.js). as a standard part of the Java runtime environment.
- Lua (.lua). Lua was primarily included because the Lua interpreter is incredibly tiny and Lua has gained popularity recently for instance in Adobe's Lightroom.
Earlier versions of NoTouch Center (up to April 2020) had Jython 2.5 installed - Jython is a full Java version of the Python interpreter. As Python 2.5 is deprecated and the latest Jython version 2.7 is larger it makes more sense to not ship it by default. Interested users can simply download the Jython-standalone jar file from https://www.jython.org/download and place it into the /opt/center/lib folder in the Virtual Appliance.
Extending / Installing your own engines
Extending NoTouch Center works by simply placing the language's .jar file into the lib/ sub-folder of your NoTouch Center installation (the full path is /opt/center/lib on the Virtual Appliance)
Check scripting engines
The "Resources" -> "About" dialog presents a list of available scripting engines, for example:
Slight variations are of course possible depending on actual NoTouch Center version and Java environment (as noted above, Python is no longer shipped by default). Now check this second screenshot that was taken after heavy customizing:
As you can see, the system administrator installed the Ruby, Scala, Clojure and Groovy languages.
Relevant settings
In the NoTouch Center Settings there are a few "master switches" that control the Scripting interface:
- Allow scripting MServer. Default is off. This a master switch that enables or disables all scripting of the client-to-Center communication protocols. If you don't use that functionality, you can reduce server load by not even look for scripts, thus we suggest so keep that off unless you really use it.
- Allow scripted default values. Default is off. This master switch refers to the "scripted default values". Do not turn it on unless you really use it. If turned on, NoTouch Center will look for active scripts each time a parameter value is accessed, and this happens quite frequently.
- Pass status values into default-values-scripts. Default is on, albeit it has no effect unless you activate "Allow scripted default values". Since scripted default values place additional load on the server, you can decide if you need the status values passed into the script. If not, you can at least save some server work. The more complex decisions a script can make will most likely require status values, however.
Activating a script
A script is active if it
- has a special filename to denote its functionality, such as a parameter name or trigger event name like autoassign,
- has a file name extension refering an installed scripting engine, e.g. .py for python, .lua for Lua, .js for JavaScript/ECMAScript
- it is placed in the scripts subdirectory of the NoTouch Center installation - see File exchange if running on a Stratodesk Virtual Appliance
and
- the relevant master switches in the NoTouch Center settings are "on".
Thus, startup.py would be valid. On the other hand, startup.rb would only work if you install JRuby. Also, test.py would not work, because there is nothing in NoTouch Center that starts a script named "test". Keep in mind, the file names denote the trigger events when the script should be started.
Note: You should not supply scripts in more than one language. NoTouch Center will just pick any script, execute it, and ignore the other(s). So, if you supply announce.py and announce.js, just one of them will be executed and this decision is arbitrary and may change from run to run. So do not activate a script in more than one language.
Scripting functionality
Parameter default values
You know the concept: Parameter values are either set or inherited from group settings and higher-level groups. If there is nothing set, a hard-coded default value will be used. And this is exactly the point where you can add custom functionality: The NoTouch Center Scripting Interface allows to selectively overwrite the system's hardcoded default values, by "selectively" we mean that every group or client can get a different, specific default value.
The power of the concept lies in the fact that it integrates well with the normal functionality and the GUI: a system administrator can still see what is set/inherited, and potentially override a scripted default value as much they would do with a "normal" default value.
Each parameter is represented by a code in the database. These codes form the script names. NoTouch Center expects the desired default value to be in the "result" variable of type "string". To find out which codes are available, look into the database and select the CODE column of the table CONFIGITEM, or look into the HTML source code of the configuration pages of the NoTouch Center Web GUI. We do not list the values here because values are constantly added with new client OS versions.
Before you start scripting, keep in mind that in NoTouch Center you can configure clients, connections, groups, the group template ("Group Settings") and connections of the Group Settings group template. Your script will be called for various objects and it may react differently. For example, a group does not have status values - you can of course write a script that assigns a value only on a specific hardware, or specific network configuration, but the script must not fail or crash if not status values are present because a group or connection is calling the script.
These variables are passed into the script:
- logger. As usual and already explained, the logger object.
- code. The parameter name. This matches the file name of the script. In other words, you know that parameter value already, if it was different than what you expect, the script would not have been called. It makes sense to evaluate the value though to prevent copy/paste errors on the script developers side.
- parameterType. A string that is either GENERIC or CONNECTION, denoting the type of parameter.
- callerType. A string denoting the type of the calling object: "CLIENT", "DEFAULTCLIENT", "CONNECTION", "DEFAULTCLIENT_CONNECTION"
- callerCoId. COID of the object operating on (Integer)
- callerName. Name of the object operating on (String)
- callerObject. Actual object
- clientCoId. COID of the client operating on (Integer). Now when would the caller be different from the client? Simple answer: When you are dealing with a connection parameter! A connection is part of a client, but not the caller.
- clientName. Name of the client operating on (String)
- clientObject. Actual client object
- groupCoId. COID of the group operating on (Integer)
- groupName. Name of the group operating on (String)
- groupObject. Actual group object
- haveStatusValues. Boolean indicating if we have status values
And only if the scriptedDefaultsWithStatusValues property is set and the caller is an actual client or connection of an actual client:
- statusValues. Status values of the last announce (Map/Dictionary)
- lastStatusEvent. The actual last StatusEvent event object
In general it is a safe strategy to just query haveStatusValues - if yes, this call is about a specific client or its connections, if not, then we are dealing with a group and thus you should return safer default values.
Client OS Image
As you know, the setting which OS image a client should use is also just a regular parameter - its database code is "IMAGE". Scripting the IMAGE parameter works like with any other parameter, place a file named IMAGE.py or IMAGE.js or whatever extension you prefer to use in the scripts directory. There are two more variables that will be passed to an IMAGE script and we encourage you to use them:
- images. List/array of strings containing all available images. An example would be
["2.39.323-EEs-150520","2.38.120-EEv-140320"]
- imageMap. Dictionary with the image names being the keys and the actual image file names being the values. An example would be
{"2.39.323-EEs-150520":"lnx-2.39.323-EEs-150520.lfi","2.38.120-EEv-140320":"lnx-2.38.120-EEv-140320.lfi"}
We suggest checking if your "result", which is supposed to be an image name (NOT file name) of an image that is installed. Otherwise, please return the string "default" to make sure you don't advertise a firmware image that does not exist in your installation.
A sample script in Python - IMAGE.py - could look like this - whenever a machine reports a DMI-BIOS "SYSTEM_PRODUCT" value that starts with "HP t620", a specific image is chosen, with all safeguards:
result = "default"
if haveStatusValues:
if statusValues['SYSTEM_PRODUCT'].startswith("HP t620"):
result='2.39.288d-TC-512-k305-150402'
if not result in images:
result="default"
Note it does not matter that the script returns "default" for all 'other' devices that are not HP t620. Remember, we are changing the default values only. And "default" is, how come, the default value for the IMAGE parameter (it is the value that you see in the GUI as "no setting"). The GUI may still override any of our results and scripts can not interfere with actual "set" parameters (since they only modify defaults...)
Scripted web actions
Scripted actions might well be the most powerful scripting interface that is available. It allows to create your own web services within the context of NoTouch Center, in your favorite scripting language, with full database and JVM access, to extend the NoTouch Center GUI, or provide interactive or non-interactive services. You can create your own web pages, as well as URL-based/REST programming interfaces that you call from other systems.
To understand how this works, an understand of MVC development as well as servlet based architecture is helpful (but not required). Without taking any background knowledge into account, you know that visiting a URL yields some output in a web browser. The output is usually a web page that shows information and links to other web pages, or it might something to download in text or even binary format. Now what is behind that URL? Usually it is some sort of program that is executed gets information from various sources like a database and then uses design templates to merge the data content into the design finally delivers a complete web page back. This is how all dynamic web pages work and so does NoTouch Center.
NoTouch Center brings the a special container URL /easyadmin/sAction.do that you can use to execute custom scripts. The name of the script is passed as the "s" parameter value. Thus, /easyadmin/sAction.do?s=myScript will execute the scripts/actions/myScript.x script (x being the extension of your favorite scripting engine, e.g. py or js or rb). These scripts can:
- return a complete rendered HTML page in the resultHtml variable
- use NoTouch Center's Velocity template engine by returning resultVar, set of variables and template, the name of the custom Velocity .vm template that you want to use
- use custom MIME types (other than the default of text/html) and a special Content-Disposition: header to use file downloads
Return pre-rendered HTML content
The easiest way to get into scripted web actions is to return a pre-rendered HTML content. Take the contents of the sample "simple.py" script:
logger.info("Simple Test Script running - Hello, world!")
htmlResult="<h1>Simple Test Script</h1>"
htmlResult=htmlResult+"This is the output of the simple test script"
Put this as simple.py in in the scripts/actions directory and then visit the /easyadmin/actions/sAction.do?s=simple with your browser, being logged into NoTouch Center from the same web browser.
There is not much to it, and you already have tremendous flexibility. However, you will also find out that creating HTML in code is tedious and error-prone, so let's move on the more flexible approach with templating.
Scripts and custom templates
Now lets look at the "owntemplate" example that you can find in the sample directory. There is a script named "owntemplate.py":
logger.info("Owntemplate script running")
template="owntemplate"
name="Test script"
resultVars['name']=name
Also, there is a second file named scripts/actions/templates/owntemplate.vm
<h1>Owntemplate example</h1>
This is an example for using scripts with their own templates.<br>
This is the name: $name<br>
Looking at both of them you can see how this works:
- In the script we set the variable template - denoting that we want to feed the script's variables into owntemplate.vm (you do not need to specify path and extension).
- Also, in the script we set the 'name' entry of resultVars - this is what the template can access as $name
In other words, everything you put into the resultVars array will show up in the template, accessible with the $ sign.
The template language and processor in use is Apache Velocity: http://velocity.apache.org - Please use the original Velocity documentation to see how Velocity works. One thing to keep in mind though - no matter what scripting language you use, Velocity is run in the JVM and assumes that variables are of JVM type. For instance, instead of native lists or dictionaries, please make sure that your script returns Java types, like ArrayList or HashMap.
Database access
Web action scripts get a variable named conn that represents an active database connection. It is of type java.sql.Connection - regular JDBC rules apply. You create a statement, get a ResultSet, iterate over it, and do something with the data.
Variables
These are the variables passed in and out of the action scripts:
- logger. The usual logger object.
- conn. Active database connection of type java.sql.connection
- reqParams. Map of the request parameters (i.e. the things you put up into the URL or POST request variables)
- remoteAddr. IP address of the requestor. May be 127.0.0.1 if using Reverse proxy
- request. Complete HttpServletRequest object.
- contentType. By default, this is "text/html". Set it to whatever MIME type you are delivering back.
- contentDisposition. By default, there is no content disposition set. People use this to instruct the browser to not display the content, but rather download it. For instance, one could do this: contentDisposition="attachment; filename=myDownload.csv" to have the browser download a file and name it myDownload.csv.
- htmlResult. A text string containing complete and final HTML content. By default, empty. If you intend to use a custom template, leave this empty!
- template. The file name (without path and without the .vm extension) of a valid Velocity file, stored in the scripts/actions/templates directory.
- templateCharset. By default, "UTF-8". Use it if you have encoding problems, e.g. to set it to "ISO8859-1" or any other encoding.
- resultVars. Map of variable names and their values. Will be fed into the Velocity template.
Scripts without user authentication
You will notice that by default you can call scripts only if you are logged in. That means, simply executing wget on your script URLs will always return NoTouch Center's login page. In most cases, this is the desired behavior.
There are situations however where you might want to call a script in an automated way or from program or script on another machine. You can run "unauthenticated" scripts also in NoTouch Center:
- The script must be placed in the "nonauth" subdirectory, i.e. scripts/actions/nonauth
- The containing URL is sNAAction.do, so the a sample call would be /easyadmin/sNAAction.do?s=myScript
Note: These scripts can still be executed in an authenticated context. In fact, if they are, NoTouch Center will execute the user name executing the scripts, if not, it will simply log "Script executed from" and append the remoteAddr variable.
We recommend to build at least some sense of security into your scripts:
- Introduce an URL parameter named "mysecret" that contains an unguessable value (password equivalent)
- Use the remoteAddr and X-Forwarded-For headers at least for a crude host-based authentication.
Scripted web actions properties
To disable scripted web actions, you can set this in the Configuration properties:
lmc.scriptedActions=false
By default scripted web actions are on, simply because as long as you don't write a script and place it in appropriate places, nothing happens anyway. Also there is not a performance impact and as long as nobody visits the URL, once again nothing happens.
System events
The following "magic codes" refer to system events. A script with exactly that filename and a proper extension would be executed when the described event happens:
- startup. Executed when starting up NoTouch Center. No special variables are passed besides the logger object and no return value is read.
- shutdown. Executed when terminating NoTouch Center. No special variables are passed besides the logger object and no return value is read.
- autoassign. Executed when a new client announces, autoassign is enabled but the builtin parameters to do yield an autoassign result. NoTouch Center expects a variable "result" to be set and contain the valid COID value (Integer) of the target group the client should be put in. If the script can not make a decision, set result to 0 (zero). These variables are passed in:
- id referring to the client's MAC address
- data referring to the full data dictionary the client sends over. For the status values, look into the nested directory data['STATUS']
Periodic scripts
If you place scripts following a certain naming convention into the /opt/center/scripts/periodic subdirectory, these will be executed according to their filename. The general format is PERIOD-TIME-description.xyz - with PERIOD being a string hourly, daily, weekly, monthly and TIME being a time designator depending on the period. The description portion is free-form - use it to describe what the script does.
For example, a script named hourly-8-test.py would be executed hourly at the 8th minute of the hour. On the other hand, daily-1730-foo.js will be executed daily at 5:30p/17:30. A monthly script that is supposed to be executed on the 12th, at 6:30 in the morning would look like this: monthly-120630-bar.py. Similarly, in a weekly case, weekly-12000.js would mean day one of the week (Monday), 20:00 (8pm). Please note that monthly and weekly scripts may not be executed exactly at that moment as they use forward-calculated interval that does not take different month lengths or leap years into account. If you want that, better write a daily script and check the date for certain trigger dates.
Client-Center communication protocol
By placing script hooks in the communication protocol, one has endless possibility to modify or enhance functionality. However, it also very easy to break functionality, deliver wrong values to clients rendering them unusable. Before you start programming these scripts, we advise to first consider if your problem can be solved with a default value script - these are by far less intrusive than the communication hooks. Second, we strongly advise to test things throughly on a test/development system and not develop on a live system. Third, do not modify values behind the system administrator's back - whenever you modify a value, make sure you log it!
All these scripts get the following variables:
- logger. An object that allows to log events using NoTouch Center's usual logging mechanism: See Logging (NTC)
- id. The client's MAC address.
The following "magic codes" refer to steps in the client-to-center communication protocol. Whenever a client calls NoTouch Center's API function, the corresponding script will be executed, unless you turn "MServer scripting" off in the Settings:
- announce. This script will be called immediately when a client announces itself to NoTouch Center. The "data" dictionary is what NoTouch Center received from the client, and this is the variable that is read back and then used for further processing.
- announce-post. After NoTouch Center has done everything it has to do, it usually returns a status code to clients. Once again you have the chance to run a custom script at this point. Like in the announce script, you get the data object, however at this point it will not be read back any more. What will be read back however, is the Integer variable "result". You receive the "result" that NoTouch Center deemed appropriate, and you have the chance to modify it.
- getConfiguration. If a client wants to get a new configuration, this script will be executed. You will get "result", a dictionary containing all the configuration settings that would be sent to the client. You can modify them here, as the "result" dictionary will be read back by NoTouch Center. Also, you have access to the last status event via the "lastEvent" variable.
- getFirmwareURL. If a client wants to get a firmware update, this script will be executed. NoTouch Center reads back the "result", expecting a string containing a full URL to an LFI file, and passes this on to the client without further interpretation. Among the variables passed in are these:
- imageName. The name of the image that is used.
- calledByClient. If the script is called because of a real client request, this boolean variable will be true. If the script was called because of internal/other actions, it will be false.
- prefix. The URL prefix that was used for constructing the URL.
- result. The URL that NoTouch has deemed appropriate for the client.
- handlePeripheralInventory. Whenever a client reports its peripherals and components, this script gets called. You will get the "data" variable, that contains the data structure sent by the client. NoTouch Center will execute this script before processing the client's request and it will read back "data" - in other words, you can modify what the client sends to you before it will be processed by NoTouch Center.
Script programming guidelines
Logging/Printing
All scripting languages support console output (e.g. print statements) but do NOT use them - console print statements come with a significant performance penalty. Use the logger object instead.
For instance, to log a change you made to a parameter value:
logger.info("Script has changed "+oldResult+" to "+result)
You can also use logger.debug() and logger.error() to indicate different log message priorities. See Logging (NTC) for more information how logging is done in NoTouch Center.
Files
Script files are supposed to be plain text files. Do not write scripts with Microsoft Word or similar programs, use a plain text editor. Also make sure your script files are really scriptname.js or similar and not scriptname.js.docx
Return values
The way JSR-223 works, there is no explicit "return" statement necessary. Your scripts simply set a variable, in most cases it will be called "result", and NoTouch Center will read it out magically. There is no need for a "return result" or similar - in fact this would produce an error.
Sample scripts
A set of sample scripts is provided in the scripts/sample subdirectory of the NoTouch Center installation (/opt/center/scripts/sample).