Introduction
While there is a lot of merit for using a proper development environment and SDK, sometimes scripting can suffice for automation tasks. The NetScaler Nitro REST interface makes it possible to accomplish pretty much whatever you need by building structured queries with the appropriate headers, urlstem + query parameters, and json formatted payload. The available command objects, examples, and syntax can all be found in the documentation bundle that can be downloaded directly from the NetScaler you are automating against from the Downloads tab in the Administrative UI. Moving forward, the full set of API reference material will be located in eDocs. Currently the complete object list must be obtained by extracting the documentation bundle for the specific NetScaler build. Another great place to start with Nitro is the Citrix NetScaler NITRO Getting Started Guide which outlines the syntax and structure for using the REST interface.
So really all we need is a means of issuing a properly formatted REST request from an HTTP agent that we can call from the shell. PERL, PHP, and PYTHON are definitely good options and may be more appropriate if you have more complicated operations to orchestrate, but cURL is also a good alternative and many admins are already familiar with the syntax and usage of this tool. The cURL library function is already included in many *nix distros and there is even a version available for Windows as well.
Building the Request
REST (REpresentational State Transfer) is an architectural style based on simple HTTP requests and responses between the client and the server. REST is used to query or change the state of objects on the server side. In REST, the server side is modeled as a set of entities where each entity is identified by a unique URL. For a complete REST transaction, we have to construct a proper Request based on specific URLs, HTTP Verbs, Content-Type Header, and Payload objects.
URL Format:
There are two basic URL stems to use with Nitro:
http://<NSIP>/nitro/<nitro_version>/stat/ – to retrieve entity and system statistics
http://<NSIP>/nitro/<nitro_version>/config/ – to perform configuration operations
We are only concentrating on configuration operations for the purposes of this article so I will limit the examples to that syntax. The next piece is the resource or object entity you are wanting to perform operations on:
http://<NSIP>/nitro/<nitro_version>/config/<resource_type>
Optionally, depending on the specific operation action we are wanting to perform, you may also need to specify an object entity name:
http://<NSIP>/nitro/<nitro_version>/config/<resource_type>/<resource_name>
Lastly, there is the specific action we are performing on the object. This may be an “add” or a “bind” action:
http://<NSIP>/nitro/<nitro_version>/config/<resource_type>?action=add
There are many other variations to this format that assist you with filtering and output formatting and you can get more information on that in the documentation bundle or Citrix NetScaler NITRO Getting Started Guide. In our example, we want to add a server object firstly and that syntax would be as follows:
https://<NSIP>/nitro/v1/config/server?action=add
HTTP Verb:
The correct HTTP Verb to use with Nitro REST calls depends on the operation you are performing on specific objects. The rule of thumb is CRUD – which is a whimsical acronym for Create, Read, Update, and Delete. The following table summarizes those options in more common terms:
Operation | HTTP Verb |
Retrieve Statistics or State | GET |
Add/Save Entities or Config | POST |
Modify Attributes of an Object | PUT |
Delete and Object | DELETE |
Content-Type:
The Content-Type is a custom one that the NetScaler Nitro REST interface understands. The earlier versions of Nitro in 9.3 used a Content-Type of application/x-www-form-urlencoded but the payload format will be different for those legacy requests so if you are on 10.x is it best to stick with the new format.
Content-Type:application/vnd.com.citrix.netscaler/<resource_type>+json – The +json tells the REST Nitro interface to expect a payload in the json format
Content-Type Examples:
Content-Type:application/vnd.com.citrix.netscaler.server+json
Content-Type:application/vnd.com.citrix.netscaler.servicegroup+json
The API Documentation bundle for the build you are working with contains all of the resource type objects available on the NetScaler.
Payload Format
The REST payload or “Body” for Nitro should match what we have specified in the Content-Type and that is json or JavaScript Object Notation. The basic syntax is of an object (resource_type) reference followed by a grouping of comma separated name:value pairs of attributes that are specific to the object in question. The groupings are delimited by “{}“
{“resource_type”:{“attribute_name1”:”attribute_value1”,“attribute_name2”:”attribute_value2”,….}
Singular Payload Example:
{“server”:{“name”:”test1″,”ipaddress”:”1.2.3.4″}}
It is also possible to perform bulk operations within the groupings by using a “[]” to denote which sub objects you want to repeat operations on.
Bulk Payload Example:
{“server”:[{“name”:”test1″,”ipaddress”:”1.2.3.4″},{“name”:”test2″,”ipaddress”:”1.2.3.5″}]
Some Basic Shell Examples Using cURL
Ok so now that we have a basic idea of how to construct the Request, we can look at how to leverage cURL to make this request with the desired format and method. The following are basic examples of some pretty standard operations that you might need in an automation environment:
Add a New Server Node:
# curl -s -i -k -H “Content-Type:application/vnd.com.citrix.netscaler.server+json” –basic –user automation:password -X POST -d ‘{“server”:{“name”:”test1″,”ipaddress”:”1.2.3.4″}}’ https://[nsip]/nitro/v1/config/server?action=add
Bind the Server to an Existing Service Group:
# curl -s -i -k -H “Content-Type:application/vnd.com.citrix.netscaler.servicegroup_servicegroupmember_binding+json” –basic –user automation:password -X POST -d ‘{“servicegroup_servicegroupmember_binding”:{“servicegroupname”:”test_service_group”,”servername”:”test1″,”port”:”80″}}’ https://[nsip]/nitro/v1/config/servicegroup_servicegroupmember_binding/test_service_group?action=bind
Save the Config:
# curl -s -i -k -H “Content-Type:application/vnd.com.citrix.netscaler.nsconfig+json” –basic –user automation:password -X POST -d ‘{“nsconfig”:{}}’ https://[nsip]/nitro/v1/config/nsconfig?action=save
option syntax reference:
-s – runs the command silently
-i – forces the return of the HTTP Header codes (you can also invoke –v for verbose mode for diagnostics and troubleshooting)
-k – is required if you want to use SSL (recommended) and have not bound an SSL certificate signed by a Trusted Certificate Authority (CA)
-H – allows us to specify the custom headers that Nitro consumes to set the base object type (you can specify the Accept: application/json header here but the default of */* would suffice)
Authentication and Command Authorization
While cURL does have fairly robust options from an authentication perspective, when remote calls are done against Nitro you have to provide some form of explicit user authentication. Nitro does not currently support the use of other authentication protocols such as GSS/Kerberos or certificate auth that do not require a password or even those which are supported by the integrated NS AAA Traffic Management engine (fingers crossed for a future enhancement here). However there are some options:
- Issue a logon request with the user/pass in the json payload and record the session cookie in a text file that cURL specifies via the –c cookies.txt option so it can be used in subsequent requests with the –b cookies.txt option
POST /nitro/v1/config/login/ HTTP/1.1
Content-Type:application/vnd.com.citrix.netscaler.login+json Host: 1.2.3.4
Accept: */*
Content-Length:51{“login”:{“username”:”nsroot”,”password”:”nsroot”}}
HTTP/1.0 201 Created
Date: Thu, 06 Feb 2014 01:12:57 GMT
Server: Apache
Set-Cookie: SESSID=d73fb417489417ef4336896e7156db98; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: SESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/
Set-Cookie: NITRO_AUTH_TOKEN=%23%23CA41D2CDD2A29E5A38F757589730ADC3C09DC72A7FAE425023C12FBA182C; path=/nitro/v1
Content-Length: 0
Connection: close
Content-Type: text/html
- Specify Authentication Headers with the –H option: X-NITRO-USER:nsroot –H X-NITRO-PASS:nsroot
- Use Basic Authentication by specifying the –basic –user nsroot:nsroot options
In any of the above instances, it is recommended to also use HTTPS so that the communication is secured with SSL to prevent whatever credentials you are using from being snooped. HTTP can be used while you are testing of your script because that is obviously easier to perform de-bugging. File level security is also a concern since this is a flat file which contains fixed credentials.
Command Authorization is another important consideration to be aware of when using Scripting or the SDK. Much of the references are specifying the nsroot account, but that is not a best practice for obvious reasons. The account used should and can be restricted to specific operations and this can be done through the configuration of a Command Authorization Policy bound to the user account. This can be very generic such as:
add system user automation 100a26e3cfb59d27cf1c05a1f6d88575c1d565aa6371c393a -encrypted -externalAuth DISABLED -timeout 60 add system cmdPolicy automation_allow_policy ALLOW “(^save\\s+ns\\s+config)|(^save\\s+ns\\s+config\\s+.*)|(^add\\s+server)|(^add\\s+server\\s+.*)|(^bind\\s+serviceGroup)| (^bind\\s+serviceGroup\\s+.*)” bind system user automation automation_allow_policy 100
or something more specific by using regular expressions to even control the names of entities that can be created. More information regarding Command Policies can be found in eDocs.
Error Handling and Troubleshooting
Nitro returns standard HTTP Response codes and a couple NetScaler specific ones which you can trap or display as output via echo. Shell scripts also normally return basic exit codes as well such as “0” for success and “1” for error if you are calling these externally in your automation environment. The table to the left outlines what possible codes can be returned and their respective meaning.
All NITRO operations are logged in the /var/log/nitro.log file on the appliance so you can simply tail-f this to see the response codes:
Feb 4 17:12:36 <local5.info> Lab-10 httpd: Netscaler_ip 10.233.50.12 – User automation – Remote_ip 10.233.50.12 – Method POST – Command { “params”: { “format”:”json”, “action”: “add”, “httpheaders”: “yes” } }{ “server”: { “name”: “test1”, “IPAddress”: “1.2.3.4” } } – Status “{ “errorcode”: 273, “message”: “Resource already exists [name, test1]”, “severity”: “ERROR” }”
Putting it together in a cURL Shell Script
Most of the challenge of calling cURL from a shell script is just formatting. The json syntax presents a challenge name:value pairs with respect to quotations so you have to just make sure you are escaping your quotes properly within the script to avoid execution errors.
Download Example cURL Shell Script for Automation
Tips:
- You will need to make sure you have execute permissions configured and you can set this by issuing a # chmod 744 [filename] command after uploading the file
- This script was written for version 10.x of Nitro so it will not work against version 9.3
- The script assumes you have setup a specific account to authenticate against
- Using a SNIP or Subnet IP Address with Management enabled is also a best practice in most environments. This will always point to the master NS in an HA pair configuration and may also be required when the management network is segmented from the resource network where the automation is being executed from