Endurance

Application programming interface (API)

This chapter covers the appliance’s REST API - the port it is served on, how requests are formed and authenticated, where the full endpoint reference is published, and a worked example that builds a complete Layer 7 service using API requests alone.

Endurance has been built from the ground up with an API-first approach to meet modern infrastructure challenges. Every appliance function is accessible through a modern RESTful API, enabling headless operation, full automation and seamless integration with existing toolchains, with real-time updates available via WebSocket connections.

The appliance can be configured either using the WebUI or by sending API requests directly. In fact, when the WebUI is used, it calls the same API in the background to configure the appliance. This means that anything that can be done in the WebUI can also be achieved through the API.

API calls can be formed and sent using any RESTful-compliant application or API test client. This enables Endurance to be configured without using the WebUI.

The API Port

The API is served over HTTPS on the same IP address and port as the WebUI. By default, the API listens on port 443. The listening IP address and port are configured on the User Interface page - for details, refer to User interface.

Sending API Requests

All API endpoints share a common base URL that is formed from the appliance’s IP address and API port, followed by the API version:

https://<appliance-IP-address>:<API-port>/api/v1/

For example, the base URL for the appliance used throughout this manual would be https://10.11.40.70:443/api/v1/.

Each request uses the standard HTTP method appropriate to the action being performed:

  • GET - retrieve one or more existing objects.

  • POST - create a new object.

  • PUT - update an existing object, or apply pending configuration changes.

  • DELETE - remove an existing object.

Request and response bodies are formatted as JSON.

Every response shares the same envelope, whether the request succeeded or failed:

{
  "status": "success",
  "uid": "ef6-744-6d8-263",
  "message": "Successfully returned all services info",
  "data": []
}
  • status - either success or error.

  • uid - a unique identifier for the request. Quote it when reporting a problem to our support team.

  • message - a human-readable summary of the outcome.

  • data - whatever the request returned. For a request that reads, creates or updates an object this is the object itself (or a list of objects); where there is nothing to return it is an empty list.

When a request fails, status is error and an additional error object lists the individual problems. Each carries its own message and, where the problem relates to a particular input, the field it applies to:

{
  "status": "error",
  "uid": "b57-681-e6c-8c8",
  "message": "Invalid user credentials.",
  "data": [],
  "error": {
    "errors": [
      {
        "type": "invalid",
        "message": "Invalid user credentials.",
        "field": ""
      }
    ]
  }
}

As with the WebUI, configuration changes made through the API are staged and do not take effect until they are applied. To apply pending changes and restart the affected services on every node in the cluster, send:

PUT https://<appliance-IP-address>:<API-port>/api/v1/services/apply

To apply the pending changes on a single appliance only, include its appliance ID (returned when authenticating) in the path:

PUT https://<appliance-IP-address>:<API-port>/api/v1/<appliance-id>/services/apply

Authentication

Endurance uses token-based (bearer token) authentication. Before sending any other request, you must authenticate to obtain an access token.

To authenticate:

  1. Send a POST request to the token endpoint, passing the same email address and password used to log into the appliance in the request body:

    Request
    POST https://<appliance-IP-address>:<API-port>/api/v1/token
    Body
    {
      "email": "<your-email-address>",
      "password": "<your-password>"
    }
  2. The response contains the access token, together with the appliance ID, in its data object:

    {
      "data": {
        "token": "<access-token>",
        "appliance_id": "<appliance-id>"
      }
    }
  3. Include the token as a bearer token in the Authorization header of all subsequent requests:

    Authorization: Bearer <access-token>

The token obtained above is tied to your interactive login and is short-lived - it carries an expiry, after which it is rejected and a fresh token must be requested. For a script or integration that should not embed a user account’s password, create a personal access token instead - it has its own, much longer expiry and is limited to the endpoints you select when it is created. A personal access token is presented in exactly the same way, as a bearer token in the Authorization header. See Personal access tokens.

API Reference

Complete reference documentation for the API - covering every available endpoint, along with its request parameters and response fields - is provided in the Endurance API reference.

Configuring Endurance Using Bruno

Bruno is a free, open-source API client, much like Postman, and is available for Linux, Windows and macOS. The difference is that it runs entirely on your own machine: your requests, environments and credentials are stored as plain files on your disk and are never synced to a vendor’s cloud or tied to an online account. You don’t have to use Bruno to talk to the Endurance API - any REST client will do - but the ready-made example presented below is a Bruno collection.

Bruno stores its requests and environment settings in a collection. A collection must be run in order to send the requests it contains.

Downloading Bruno

Bruno can be downloaded from the Bruno downloads page.

Download & Import the Ready-made Example Collection

The ready-made example Bruno collection can be downloaded from SimpleTestEnvironment.zip.

The example collection is the same as the test environment presented in the Getting Started chapter.

Once downloaded, run Bruno (the screenshots below show Bruno v4.1.0).

To import the collection:

  1. Under Quick Actions, click Import Collection.

    import collection
  2. In the pop-up, select choose file(s), then browse to and select the file downloaded previously and click Open.

  3. Under Location, click Browse, then choose a location to store the new collection and click Select Folder.

  4. Click Import to import the collection.

  5. Under Collections, click Simple Test Environment to select the collection.

Environment Variables

The Test Environment collection uses several environment variables in the API requests; these must be modified to suit your environment.

To edit the environment variables:

  1. Click on No Environment in the top right corner, then select Test Environment.

  2. Click on Test Environment in the top right corner and select Configure.

    bruno environment settings
    • Change the IP address for node-0 to match your appliance.

    • Change port to the port the API is served on, if it is not the default of 443.

    • Change email to match the email address you use to log in to your appliance.

    • Change password to match the password you use to log in to your appliance.

    • Change the IP address for fip to the IP address you’d like to use for the Virtual Service.

    • Change the IP address for real1-Ip to the address of your first Real Server.

    • Change the IP address for real2-Ip to the address of your second Real Server.

    • Click Save.

Command Overview

The table below provides a summary of the API requests that are used to configure the simple test environment:

Command Group Request Purpose

Login

Login

Login and obtain access token

Add Floating IP

Get Management Group

Determine the groupId for the management subnet

Add Floating IP

Add the floating IP to the management subnet

Configure Layer 7 Service

Add Real Server 1

Add first real server

Add Real Server 2

Add second real server

Add Backend

Add the backend for the Layer 7 service

Add Default ACL

Add the default ACL to link frontend and backend

Add Frontend

Add the frontend for the Layer 7 service

Reload Services

Reload Required Services

Restart services to apply new settings (HAProxy and NF Tables)

Command Details

The following sections provide full details of all API requests.

Login

Request
POST https://{{node-0}}:{{port}}/api/v1/token
Body
{
  "email": "{{email}}",
  "password": "{{password}}"
}
Post Response Script
bru.setEnvVar("token", res.body.data.token);
bru.setEnvVar("localnodeid", res.body.data.appliance_id);

Get Management Group

Request
GET https://{{node-0}}:{{port}}/api/v1/ipgroups/
Auth
Bearer token : {{token}}
Post Response Script
var groupId = ""
for (let i = 0; i < res.body.data.length; i++) {
    if (res.body.data[i].name == "Management") {
        groupId = res.body.data[i].id
    }
}
bru.setEnvVar("groupId", groupId);

Add Floating IP

Request
POST https://{{node-0}}:{{port}}/api/v1/fips
Body
{
  "groupId": "{{groupId}}",
  "ip": "{{fip}}",
  "enabled": "true"
}
Auth
Bearer token : {{token}}
Post Response Script
bru.setEnvVar("fipId", res.body.data.id);

Add Real Server 1

Request
POST https://{{node-0}}:{{port}}/api/v1/haproxy/rips
Body
{
  "name": "RS-01",
  "ipDomain": "{{real1-Ip}}",
  "port": 80,
  "weight": 100,
  "minconns": 0,
  "maxconns": 0,
  "enabledCount": false
}
Auth
Bearer token : {{token}}
Post Response Script
bru.setEnvVar("real1-Id", res.body.data.id);

Add Real Server 2

Request
POST https://{{node-0}}:{{port}}/api/v1/haproxy/rips
Body
{
  "name": "RS-02",
  "ipDomain": "{{real2-Ip}}",
  "port": 80,
  "weight": 100,
  "minconns": 0,
  "maxconns": 0,
  "enabledCount": false
}
Auth
Bearer token : {{token}}
Post Response Script
bru.setEnvVar("real2-Id", res.body.data.id);

Add Backend

Request
POST https://{{node-0}}:{{port}}/api/v1/haproxy/backends
Body
{
  "name": "Web-Cluster-Backend",
  "mode": "tcp",
  "ripIds": ["{{real1-Id}}","{{real2-Id}}"],
  "balanceMode": "leastconn",
  "persistenceMode": "none",
  "healthCheck": "connectToPort",
  "healthCheckPort": 80,
  "healthCheckRequestToSend": "/",
  "healthCheckResponseExpected": "",
  "encryptToBackend": false,
  "enableGzipCompression": false,
  "headers": "",
  "httpPipeline": "httpKeepAlive",
  "httpPretendKeepalive": false,
  "httpAcceptInvalidResponse": false,
  "httpRequestTimeout": true,
  "tunnelTimeout": "1h",
  "TCPKeepalive": false,
  "connectionRedispatch": true,
  "abortOnClose": false,
  "slowStartTime": "3s",
  "backendSourceAddress": "",
  "enableServerConnTimeout": true,
  "serverTimeout": "43s",
  "enableFeedbackAgent": false,
  "feedbackAgentPort": 3333,
  "feedbackAgentInterval": "2s",
  "fallbackPersistence": false,
  "enableTProxy": false,
  "sendProxyProtocol": "off"
}
Auth
Bearer token : {{token}}
Post Response Script
bru.setEnvVar("backend-id", res.body.data.id);

Add Default ACL

Request
POST https://{{node-0}}:{{port}}/api/v1/haproxy/acls
Body
{
  "redirectLocation": "{{backend-id}}",
  "redirectType": "backend",
  "name": "Web-Cluster default",
  "conditionSet": {
    "terms": [
      {
        "conditionGroups": [
          {
            "conditions": [
              {
                "conditionType": "default",
                "conditionValues": []
              }
            ]
          }
        ]
      }
    ]
  }
}
Auth
Bearer token : {{token}}
Post Response Script
bru.setEnvVar("acl-default-id", res.body.data.id);

Add Frontend

Request
POST https://{{node-0}}:{{port}}/api/v1/haproxy/frontends
Body
{
  "name": "Web-Cluster",
  "ports": [80],
  "mode": "tcp",
  "fipID": "{{fipId}}",
  "aclIds": ["{{acl-default-id}}"],
  "SSLEnable": false,
  "SSLTLSv130rttEnable": false,
  "tcpKeepalive": false,
  "acceptProxyProtocol": false,
  "SSLMinVer": "TLSv1.2",
  "SSLMaxVer": "TLSv1.3",
  "httpsForceToRedirect": false,
  "httpsRedirectCode": 301,
  "hstsEnable": false,
  "hstsTimeout": 6,
  "sslCerts": [],
  "maximumConnections": 40000,
  "enableClientConnTimeout": false,
  "clientTimeout": "42s",
  "httpPipeLine": "httpKeepAlive",
  "httpAcceptInvalidRequest": false,
  "httpRequestTimeout": true,
  "httpPretendKeepalive": false,
  "setXForwardedforHeader": false
}
Auth
Bearer token : {{token}}
Post Response Script
bru.setEnvVar("frontend-id", res.body.data.id);

Reload Required Services

Request
PUT https://{{node-0}}:{{port}}/api/v1/{{localnodeid}}/services/apply
Auth
Bearer token : {{token}}

Run the Collection

Under Collections, right-click Simple Test Environment, select Run then click Run 9 Requests.

bruno run

The above image shows that all requests have completed successfully. This confirms that the simple test environment has been created.

For details on viewing/modifying and testing the configuration, please refer to the Getting Started chapter.

Every endpoint, its parameters and its responses are listed in the API Reference.