iControl REST Cookbook - Virtual Server (ltm virtual)

This cookbook lists selected ready-to-use iControl REST curl commands for virtual-server related resources. Each recipe consists of the curl command, it's tmsh equivalent, and sample output.

In this cookbook, the following curl options are used. 

   Option      Meaning
______________________________________________________________________________________
   -s          Suppress progress meter. Handy when you want to pipe the output.
______________________________________________________________________________________
   -k          Allows "insecure" SSL connections.
______________________________________________________________________________________
   -u          Specify user ID and password. For the start, you should use the "admin"
               account that you normally use to access the Configuration Utility.
               When you specify the password at the same time,
               concatenate with ":". e.g., admin:admin.
______________________________________________________________________________________
   -X <method> Specify the HTTP method. When omitted, the default is GET. In the REST
               framework, POST means create (tmsh create), PATCH means overwriting the
               existing resource with the data sent (tmsh modify), and PATCH is for 
               merging (ditto).
______________________________________________________________________________________
   -H <Header> Specify the request header. When you send (POST, PATCH, PUT) data, you
               need to tell the server that the data is in JSON format.
               i.e., -H "Content-Type: application/json.
______________________________________________________________________________________
   -d 'data'   The JSON data to send. Note that you need to quote the entire json
               blob, and each "name":"value" pairs must be quoted. When you have 
               nested quotes, make sure you escape (\) them.

Get information of the virtual <vs>

tmsh list ltm <vs>

curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual/<vs>

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstate',
  name: 'vs',
  fullPath: 'vs',
  generation: 1109,
  selfLink: 'https://localhost/mgmt/tm/ltm/virtual/vs?ver=12.1.0',
  addressStatus: 'yes',
  autoLasthop: 'default',
  cmpEnabled: 'yes',
  connectionLimit: 0,
  description: 'TestData',
  destination: '/Common/192.168.184.226:80',
  enabled: true,
  gtmScore: 0,
  ipProtocol: 'tcp',
  mask: '255.255.255.255',
  mirror: 'disabled',
  mobileAppTunnel: 'disabled',
  nat64: 'disabled',
  pool: '/Common/vs-pool',
  poolReference: { link: 'https://localhost/mgmt/tm/ltm/pool/~Common~vs-pool?ver=12.1.0' },
  rateLimit: 'disabled',
  rateLimitDstMask: 0,
  rateLimitMode: 'object',
  rateLimitSrcMask: 0,
  serviceDownImmediateAction: 'none',
  source: '0.0.0.0/0',
  sourceAddressTranslation: { type: 'automap' },
  sourcePort: 'preserve',
  synCookieStatus: 'not-activated',
  translateAddress: 'enabled',
  translatePort: 'enabled',
  vlansDisabled: true,
  vsIndex: 4,
  rules: [ '/Common/irule' ],
  rulesReference: [ { link: 'https://localhost/mgmt/tm/ltm/rule/~Common~iRuleTest?ver=12.1.0' } ],
  policiesReference: {
    link: 'https://localhost/mgmt/tm/ltm/virtual/~Common~vs/policies?ver=12.1.0',
    isSubcollection: true
  },
  profilesReference: {
    link: 'https://localhost/mgmt/tm/ltm/virtual/~Common~vs/profiles?ver=12.1.0',
     isSubcollection: true
  }
}

Get only specfic field of the virtual <vs>

The naming convension for the parameters is slightly different from the ones on tmsh, so look for the familiar names in the GET response above. The example below queris the Default Pool (pool).

tmsh list ltm <vs> pool

curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual/<vs>?options=pool

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstate',
  name: 'vs',
  fullPath: 'vs',
  generation: 1,
  selfLink: 'https://localhost/mgmt/tm/ltm/virtual/vs?options=pool&ver=12.1.1',
  pool: '/Common/vs-pool',
  poolReference: {
    link: 'https://localhost/mgmt/tm/ltm/pool/~Common~vs-pool?ver=12.1.1'
  }
}

Get all the information of the virtual <vs>

Unlike the tmsh equivalent, iControl REST GET does not return the configuration information of the attached policies and profiles. To see them, use

expandSubcollections
.

tmsh list ltm <vs>

curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual/<vs>?expandSubcollections=true

Sample Output

{
    "addressStatus": "yes",
    "autoLasthop": "default",
    "cmpEnabled": "yes",
    "connectionLimit": 0,
    "destination": "/Common/192.168.184.240:80",
    "enabled": true,
    "fullPath": "vs",
    "generation": 291,
    "gtmScore": 0,
    "ipProtocol": "tcp",
    "kind": "tm:ltm:virtual:virtualstate",
    "mask": "255.255.255.255",
    "mirror": "disabled",
    "mobileAppTunnel": "disabled",
    "name": "vs",
    "nat64": "disabled",
    "policiesReference": {
        "isSubcollection": true,
        "link": "https://localhost/mgmt/tm/ltm/virtual/~Common~vs/policies?ver=13.1.0"
    },
    "pool": "/Common/CentOS-all80",
    "poolReference": {
        "link": "https://localhost/mgmt/tm/ltm/pool/~Common~CentOS-all80?ver=13.1.0"
    },
    "profilesReference": {
        "isSubcollection": true,
        "items": [
            {
                "context": "all",
                "fullPath": "/Common/http",
                "generation": 291,
                "kind": "tm:ltm:virtual:profiles:profilesstate",
                "name": "http",
                "nameReference": {
                    "link": "https://localhost/mgmt/tm/ltm/profile/http/~Common~http?ver=13.1.0"
                },
                "partition": "Common",
                "selfLink": "https://localhost/mgmt/tm/ltm/virtual/~Common~vs/profiles/~Common~http?ver=13.1.0"
            },
            {
                "context": "all",
                "fullPath": "/Common/tcp",
                "generation": 287,
                "kind": "tm:ltm:virtual:profiles:profilesstate",
                "name": "tcp",
                "nameReference": {
                    "link": "https://localhost/mgmt/tm/ltm/profile/tcp/~Common~tcp?ver=13.1.0"
                },
                "partition": "Common",
                "selfLink": "https://localhost/mgmt/tm/ltm/virtual/~Common~vs/profiles/~Common~tcp?ver=13.1.0"
            }
        ],
        "link": "https://localhost/mgmt/tm/ltm/virtual/~Common~vs/profiles?ver=13.1.0"
    },
    "rateLimit": "disabled",
    "rateLimitDstMask": 0,
    "rateLimitMode": "object",
    "rateLimitSrcMask": 0,
    "selfLink": "https://localhost/mgmt/tm/ltm/virtual/vs?expandSubcollections=true&ver=13.1.0",
    "serviceDownImmediateAction": "none",
    "source": "0.0.0.0/0",
    "sourceAddressTranslation": {
        "type": "automap"
    },
    "sourcePort": "preserve",
    "synCookieStatus": "not-activated",
    "translateAddress": "enabled",
    "translatePort": "enabled",
    "vlansDisabled": true,
    "vsIndex": 2
}

Get stats of the virtual <vs>

tmsh show ltm <vs>

curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual/<vs>/stats

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstats',
  generation: 1109,
  selfLink: 'https://localhost/mgmt/tm/ltm/virtual/vs/stats?ver=12.1.0',
  entries:
   { 'https://localhost/mgmt/tm/ltm/virtual/vs/~Common~vs/stats':
      { nestedStats:
         { kind: 'tm:ltm:virtual:virtualstats',
           selfLink: 'https://localhost/mgmt/tm/ltm/virtual/vs/~Common~vs/stats?ver=12.1.0',
           entries:
            { 'clientside.bitsIn': { value: 12880 },
              'clientside.bitsOut': { value: 34592 },
              'clientside.curConns': { value: 0 },
              'clientside.evictedConns': { value: 0 },
              'clientside.maxConns': { value: 2 },
              'clientside.pktsIn': { value: 26 },
              'clientside.pktsOut': { value: 26 },
              'clientside.slowKilled': { value: 0 },
              'clientside.totConns': { value: 6 },
              cmpEnableMode: { description: 'all-cpus' },
              cmpEnabled: { description: 'enabled' },
              csMaxConnDur: { value: 37 },
              csMeanConnDur: { value: 29 },
              csMinConnDur: { value: 17 },
              destination: { description: '192.168.184.226:80' },
              'ephemeral.bitsIn': { value: 0 },
              'ephemeral.bitsOut': { value: 0 },
              'ephemeral.curConns': { value: 0 },
              'ephemeral.evictedConns': { value: 0 },
              'ephemeral.maxConns': { value: 0 },
              'ephemeral.pktsIn': { value: 0 },
              'ephemeral.pktsOut': { value: 0 },
              'ephemeral.slowKilled': { value: 0 },
              'ephemeral.totConns': { value: 0 },
              fiveMinAvgUsageRatio: { value: 0 },
              fiveSecAvgUsageRatio: { value: 0 },
              tmName: { description: '/Common/vs' },
              oneMinAvgUsageRatio: { value: 0 },
              'status.availabilityState': { description: 'available' },
              'status.enabledState': { description: 'enabled' },
              'status.statusReason': { description: 'The virtual server is available' },
              syncookieStatus: { description: 'not-activated' },
              'syncookie.accepts': { value: 0 },
              'syncookie.hwAccepts': { value: 0 },
              'syncookie.hwSyncookies': { value: 0 },
              'syncookie.hwsyncookieInstance': { value: 0 },
              'syncookie.rejects': { value: 0 },
              'syncookie.swsyncookieInstance': { value: 0 },
              'syncookie.syncacheCurr': { value: 0 },
              'syncookie.syncacheOver': { value: 0 },
              'syncookie.syncookies': { value: 0 },
              totRequests: { value: 4 }
            }
         }
      }
   }
}

Change one of the configuration options of the virtual <vs>

The command below changes the Description field of the virtual ("description" in tmsh and iControl REST).

tmsh modify ltm virtual <vs> description "Hello World!"

curl -sku admin:admin  https://<host>/mgmt/tm/ltm/<vs> \
  -X PATCH -H "Content-Type: application/json" \
  -d '{"description": "Hello World!"}'  

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstate',
  name: 'vs',
  ... 
  description: 'Hello World!',       <==== Changed.
  ...
}

Disable the virtual <vs>

The command syntax is same as above: To change the state of a virtual from "enabled" to "disabled", send "disabled":true. For enabling the virtual, use "enabled":true. Note that the Boolean type true/false does not require quotations.

tmsh modify ltm virtual <vs> disabled

curl -sku admin:admin  https://<host>/mgmt/tm/ltm/<vs> \
  -X PATCH -H "Content-Type: application/json" \
  -d '{"disabled": true}' \

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstate',
  name: 'vs',
  fullPath: 'vs',
  ...
  disabled: true,  <== Changed
  ...
}

Add another iRule to <vs>

When the virtual has iRules already attached, you need to send the existing ones too along with the additional one. For example, to add /Common/testRule1 to the virtual with /Common/testRule1, specify both in an array (square brackets). Note that the /Common/testRule2 iRule object should be already created.

tmsh modify ltm virtual <vs> rules {testRule1 testRule2}

curl -sku admin:admin  https://<host>/mgmt/tm/ltm/virtual/<vs> \
  -X PATCH -H "Content-Type: application/json" \
  -d '{"rules": ["/Common/testRule1", "/Common/testRule2"] }' 

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstate',
  name: 'vs',
  fullPath: 'vs',
  ...
  rules: [ '/Common/test1', '/Common/test2' ],  <== Changed
  rulesReference:
   [ { link: 'https://localhost/mgmt/tm/ltm/rule/~Common~test1?ver=12.1.1' },
     { link: 'https://localhost/mgmt/tm/ltm/rule/~Common~test2?ver=12.1.1' } ],
  ...
}

Create a new virtual <vs>

You can create a skeleton virtual by specifying only Destination Address and Mask. The remaining parameters such as profiles are set to default. You can later modify the parameters by PATCH-ing.

tmsh create ltm virtual <vs> destination <ip:port> mask <ip>

curl -sku admin:admin -X POST -H "Content-Type: application/json" \
  -d '{"name": "vs", "destination":"192.168.184.230:80", "mask":"255.255.255.255"}' \
  https://<host>/mgmt/tm/ltm/virtual

Sample Output

{
  kind: 'tm:ltm:virtual:virtualstate',
  name: 'vs',
  partition: 'Common',
  fullPath: '/Common/vs',
  ...
  destination: '/Common/192.168.184.230:80',  <== Created
  ...
  mask: '255.255.255.255',                    <== Created
  ...
}

Create a new virtual <vs> with a lot of parameters

You can specify all the essential parameters upon creation. This example creates a new virtual with pool, default persistence profile, profiles, iRule, and source address translation. The call fails if any of the parameters conflicts. For example, you cannot specify "Cookie Persistence" without specifying appropriate profiles. If you do not specify any profile, it falls back to the default

fastL4
, which is not compatible with Cookie Persistence.

tmsh create ltm virtual <vs> destination <ip:port> mask <ip> pool <pool> persist replace-all-with { cookie } profiles add { tcp http clientssl } rules { <rule> } source-address-translation { type automap }

curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual -H "Content-Type: application/json" -X POST
  -d '{"name": "vs", \
        "destination": "10.10.10.10:10", \
        "mask": "255.255.255.255", \
        "pool": "CentOS-all80", \
        "persist": [ {"name": "cookie"} ], \
       "profilesReference": {"items": [ {"context": "all", "name": "http"}, {"context": "all", "name": "tcp"}, {"context": "clientside", "name": "clientssl"}] }, \
       "rules": [ "ShowVersion" ], \
       "sourceAddressTranslation": {"type": "automap"} }'

Sample Output

{
    "addressStatus": "yes",
    "autoLasthop": "default",
    "cmpEnabled": "yes",
    "connectionLimit": 0,
    "destination": "/Common/10.10.10.10:10",
    "enabled": true,
    "fullPath": "/Common/test",
    "generation": 592,
    "gtmScore": 0,
    "ipProtocol": "tcp",
    "kind": "tm:ltm:virtual:virtualstate",
    "mask": "255.255.255.255",
    "mirror": "disabled",
    "mobileAppTunnel": "disabled",
    "name": "vs",
    "nat64": "disabled",
    "partition": "Common",
    "persist": [
        {
            "name": "cookie",
            "nameReference": {
                "link": "https://localhost/mgmt/tm/ltm/persistence/cookie/~Common~cookie?ver=13.1.0"
            },
            "partition": "Common",
            "tmDefault": "yes"
        }
    ],
    "policiesReference": {
        "isSubcollection": true,
        "link": "https://localhost/mgmt/tm/ltm/virtual/~Common~test/policies?ver=13.1.0"
    },
    "pool": "/Common/CentOS-all80",
    "poolReference": {
        "link": "https://localhost/mgmt/tm/ltm/pool/~Common~CentOS-all80?ver=13.1.0"
    },
    "profilesReference": {
        "isSubcollection": true,
        "link": "https://localhost/mgmt/tm/ltm/virtual/~Common~test/profiles?ver=13.1.0"
    },
    "rateLimit": "disabled",
    "rateLimitDstMask": 0,
    "rateLimitMode": "object",
    "rateLimitSrcMask": 0,
    "rules": [
        "/Common/ShowVersion"
    ],
    "rulesReference": [
        {
            "link": "https://localhost/mgmt/tm/ltm/rule/~Common~ShowVersion?ver=13.1.0"
        }
    ],
    "selfLink": "https://localhost/mgmt/tm/ltm/virtual/~Common~test?ver=13.1.0",
    "serviceDownImmediateAction": "none",
    "source": "0.0.0.0/0",
    "sourceAddressTranslation": {
        "type": "automap"
    },
    "sourcePort": "preserve",
    "synCookieStatus": "not-activated",
    "translateAddress": "enabled",
    "translatePort": "enabled",
    "vlansDisabled": true,
    "vsIndex": 52
}

Delete a virtual <vs>

tmsh delete ltm virtual <vs>

curl -sku admin:admin https://192.168.226.55/mgmt/tm/ltm/virtual/<vs> -X DELETE

Sample Output

No output (just 200 OK and no response body)

References

Updated Jun 06, 2023
Version 2.0

Was this article helpful?

7 Comments

  • Hi Satoshi,
    thanks for the nice summary (5*). Perhaps you may want add an example for assigning profiles, persistency and an iRule to a virtual server, please (sample POST as follows):

    {"name":"virtual_test.bit","destination":"10.100.100.155:443","pool":"pool_test.bit","persist":[{"name":"persist_cookie"}],"fallbackPersistence":"source_addr","profilesReference":{"items":[{"name":"tcp","context":"all"},{"name":"profile_clientssl_test.bit","context":"clientside"},{"name":"profile_http"}]},"rules":["rule_test.bit"]}

    When using cURL on BIG-IP´s bash it might be usefull to pipe the output via

    | json-format

    Thanks, Stephan

  • Arie's avatar
    Arie
    Icon for Altostratus rankAltostratus

    The section about disabling VIPs seems to be incorrect.

    curl -sku admin:admin  https://<host>/mgmt/tm/ltm/<vs> \
      -X PATCH -H "Content-Type: application/json" \
      -d '{"enabled": false}' \

    Results in HTTP 400 Bad Request.

    {
        "code": 400,
        "message": "one or more properties must be specified",
        "errorStack": [],
        "apiError": 26214401
    }

    Changing the JSON to the following does the trick:

    {
        "disabled": true
    }

    Subsequently, the JSON response is:

    {
        "kind": "tm:ltm:virtual:virtualstate",
        "name": "TestVIP",
        [...]
        "disabled": true,
        [...]
    }

    For comparison, an enabled VIP looks like this:

    {
        "kind": "tm:ltm:virtual:virtualstate",
        "name": "TestVIP",
        [...]
        "enabled": true,
        [...]
    }

    Version: 12.1.2

  • Arie's avatar
    Arie
    Icon for Altostratus rankAltostratus

    On a related note, sending the following:

    {
        "description": "Description here",
        "enabled": false
    }

    Results in a 200 OK, but the state is not changed.

  • Thank you, Arie. You are right. It has to be "disabled:true" for disabling and "enabled:true" for enabling. I have updated the doc.

  • When disabling virtual servers admins tend to miss the deactivation the related virtual address.
    Thats why the VIP is still resolved with ARP requests and responds to PING.
    Important note: The following changes affect all virtual servers using this virtual address.

    To deactive a virtual address it is required to apply i.e. the following:

    curl -sk -u admin: -H 'Content-Type: application/json' \
    -X PATCH -d '{"arp": "disabled", "enabled": "no", "icmpEcho": "disabled"}' \
    https://localhost/mgmt/tm/ltm/virtual-address/10.131.131.101 | python -m json.tool

    To reactivate it the following request would be used:

    curl -sk -u admin: -H 'Content-Type: application/json' \
    -X PATCH -d '{"arp": "enabled", "enabled": "yes", "icmpEcho": "enabled"}' \
    https://localhost/mgmt/tm/ltm/virtual-address/10.131.131.101 | python -m json.tool

    The commands above are the equivalents to tmsh:

    modify ltm virtual-address 10.131.131.101 enabled no arp disabled icmp-echo disabled
    modify ltm virtual-address 10.131.131.101 enabled yes arp enabled icmp-echo enabled
  • Hi  , you can solve it by using a transaction.

    This requires 4 steps:

    1. open the transaction
    2. remove the previous profile
    3. add the new profile
    4. run the transaction and optionally monitor the progress

    Btw, I have scripted this in an Ansible play by using the the URI module with plain access to the REST API.
    In my case I´m using a token based authentication. But it will probably work with basic auth as well. So the authentication isnt described below.

    These are the steps in detail:
    The transaction will be opened as follows:

    POST '{}' to path ./mgmt/tm/transaction with header "Content-Type: application/json"

    You will get some payload back including the transaction identifier transId. The transaction identifier will be required in the following steps and used by an http-header.

    Now you can add operations to the transaction.
    At first you delete the current profile:

    DELETE to path ./mgmt/tm/ltm/virtual/~<partition-name>~<virtual-name>/profiles/<profile-name> with header "X-F5-REST-Coordination-Id: <transId>"

    Now you add the new profile (the clientside context or serverside context wont be relevant in case of your http profile and can be omitted):

    POST '{"name":"<profile-name>","partition":"<partition-name>","context":"<context>"}' to path ./mgmt/tm/ltm/virtual/~<partition-name>~<virtual-name>/profiles with headers "X-F5-REST-Coordination-Id: <transId>" and "Content-Type: application/json"

    The transaction can be released now (no need to add the transaction ID header as it is contained in the path):

    PUT or PATCH '{"state": "VALIDATING"}' to path ./mgmt/tm/transaction/<transId> with header "Content-Type: application/json"

    Finally you may want to monitor the transactions execution in a loop until the returned data has a json.state.lower() == 'completed' (no headers required as well):

    GET to path ./mgmt/tm/transaction/<transId>

    Thats it so far. Transactions work nicely as well in tmsh.

    Cheers, Stephan