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/virtual/<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/virtual/<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
- curl.1 the man page
- curl Releases and Downloads ... including the port for Windows
- Jason Rahm's "Demystifying iControl REST" series (DevCentral) -- This is Part I of 7 at the time of this article.
- iControl REST API reference (DevCentral)
- iControl® REST API User Guide (DevCentral) -- Link is for 12.1. Search for the older versions.
The command to change the Description field on the virtual is missing "virtual/" in the URI.
Here is the correct syntax.
curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual/<vs> \
-X PATCH -H "Content-Type: application/json" \
-d '{"description": "Hello World!"}'Similarly, the correct syntax to disable the virtual server is as below:
curl -sku admin:admin https://<host>/mgmt/tm/ltm/virtual/<vs> \ -X PATCH -H "Content-Type: application/json" \ -d '{"disabled": true}' \
- JRahmAdmin
nice find! Fixed...
Hi , you can solve it by using a transaction.
This requires 4 steps:
- open the transaction
- remove the previous profile
- add the new profile
- 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
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
- Satoshi_Toyosa1Ret. Employee
Thank you, Arie. You are right. It has to be "disabled:true" for disabling and "enabled:true" for enabling. I have updated the doc.
- ArieAltostratus
On a related note, sending the following:
{ "description": "Description here", "enabled": false }
Results in a 200 OK, but the state is not changed.
- ArieAltostratus
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
- Satoshi_Toyosa1Ret. Employee
Added (sorry for being late).
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