diff --git a/docs/API-v1.md b/docs/API-v1.md index ee6fa26..12496e4 100644 --- a/docs/API-v1.md +++ b/docs/API-v1.md @@ -1,17 +1,41 @@ # Table of contents -1. [API](#API) -2. [Token operations](#token) -3. [VM operations](#vmops) -4. [Add disks](#adddisks) -5. [VM snapshots](#vmsnapshots) -6. [Status and metrics](#statusmetrics) -7. [Pool configuration](#poolconfig) -8. [Ondemand VM provisioning](#ondemandvm) +1. [API](#API) +2. [Postman collection](#postman) +3. [Token operations](#token) +4. [VM operations](#vmops) +5. [Add disks](#adddisks) +6. [VM snapshots](#vmsnapshots) +7. [Status and metrics](#statusmetrics) +8. [Pool configuration](#poolconfig) +9. [Ondemand VM provisioning](#ondemandvm) ### API vmpooler provides a REST API for VM management. The following examples use `curl` for communication. +### Postman collection + +An example postman collection can be imported by [downloading it](postman/vmpooler FULL API.postman_collection.json) and the [generic environment file](postman/vmpooler generic.postman_environment.json) +You will have to set the values in the environment file as per your local setup. The full API is modeled and some queries should be run in order, because we use the post-test script to save important returned values that are required by the other APIs. + +![postman](postman/postman_screenshot.png) + +You can string API requests in a specific order to run use cases for example: + +folder "create vm"/"ondemand" you can run + +Step 1) **ondemand vm** will use the _url, api_prefix, token and ondemand_pool_name_ from the env file and as a post-test script save the returned "request_id" in a collection variable + +Step 2) **check ondemand** will use the collection variable from Step 1) to query the API, and a post-test script will save the returned "hostname" in a collection variable, to be used in other APIs like 'Add additional disk' or 'Create a snapshot' + +folder "other vm requests" + +Step 2) **Create a snapshot** will use the "hostname" from the previous request + +folder "create vm"/"ondemand" you can finally run + +Step 3) **delete ondemand** will use the collection variable from Step 1) to delete the VM via the "request_id" + #### Token operations Token-based authentication can be used when requesting or modifying VMs. The `/token` route can be used to create, query, or delete tokens. See the provided YAML configuration example, [vmpooler.yaml.example](vmpooler.yaml.example), for information on configuring an authentication store to use when performing token operations. diff --git a/docs/postman/postman_screenshot.png b/docs/postman/postman_screenshot.png new file mode 100644 index 0000000..8adaeff Binary files /dev/null and b/docs/postman/postman_screenshot.png differ diff --git a/docs/postman/vmpooler FULL API.postman_collection.json b/docs/postman/vmpooler FULL API.postman_collection.json new file mode 100644 index 0000000..8d6c894 --- /dev/null +++ b/docs/postman/vmpooler FULL API.postman_collection.json @@ -0,0 +1,1059 @@ +{ + "info": { + "_postman_id": "02b9b224-1d71-4d97-a1bc-43141a7ea1bd", + "name": "vmpooler FULL API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "tokens", + "item": [ + { + "name": "Get a list of issued tokens", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "username", + "value": "{{ldap_user}}", + "type": "string" + }, + { + "key": "password", + "value": "{{ldap_pass}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/{{api_prefix}}/token/", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "token", + "" + ] + } + }, + "response": [] + }, + { + "name": "Generate a new authentication token", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "username", + "value": "{{ldap_user}}", + "type": "string" + }, + { + "key": "password", + "value": "{{ldap_pass}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{url}}/token/", + "host": [ + "{{url}}" + ], + "path": [ + "token", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get information about an existing token (including associated VMs)", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/{{api_prefix}}/token/{{token}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "token", + "{{token}}" + ] + } + }, + "response": [] + }, + { + "name": "Delete an authentication token", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "username", + "value": "{{ldap_user}}", + "type": "string" + }, + { + "key": "password", + "value": "{{ldap_pass}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{url}}/{{api_prefix}}/token/{{tokentodelete}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "token", + "{{tokentodelete}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "create vm", + "item": [ + { + "name": "ondemand", + "item": [ + { + "name": "1) ondemandvm", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"job_id\", jsonData.request_id);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"{{ondemand_pool_name}}\":1}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/ondemandvm/", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "ondemandvm", + "" + ] + } + }, + "response": [] + }, + { + "name": "2) check ondemandvm", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "var poolname = pm.environment.get(\"ondemand_pool_name\");", + "if (jsonData[poolname].hostname) {", + " pm.collectionVariables.set(\"vm_hostname\", jsonData[poolname].hostname[0]);", + " console.log(\"saving returned hostname to vm_hostname = \" + jsonData[poolname].hostname[0])", + "}" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/ondemandvm/{{job_id}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "ondemandvm", + "{{job_id}}" + ] + } + }, + "response": [] + }, + { + "name": "3) delete ondemandvm", + "request": { + "method": "DELETE", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/ondemandvm/{{job_id}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "ondemandvm", + "{{job_id}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "pooled", + "item": [ + { + "name": "1) pooled vm allocation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "var poolname = pm.environment.get(\"pooled_pool_name\");", + "pm.collectionVariables.set(\"vm_hostname\", jsonData[poolname].hostname);", + "console.log(\"saving returned hostname to vm_hostname = \" + jsonData[poolname].hostname)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"{{pooled_pool_name}}\":1}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "" + ] + } + }, + "response": [] + }, + { + "name": "1) pooled vm allocation using params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "var poolname = pm.environment.get(\"pooled_pool_name\");", + "pm.collectionVariables.set(\"vm_hostname\", jsonData[poolname].hostname);", + "console.log(\"saving returned hostname to vm_hostname = \" + jsonData[poolname].hostname)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"{{pooled_pool_name}}\":1}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/ubuntu-2004-64", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "ubuntu-2004-64" + ], + "query": [ + { + "key": "", + "value": "", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "2) Schedule a checked-out VM for deletion", + "request": { + "method": "DELETE", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}" + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "other vm requests", + "item": [ + { + "name": "Retrieve a list of available VM pools", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm" + ] + } + }, + "response": [] + }, + { + "name": "Query metadata information for a checked-out VM", + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}" + ] + } + }, + "response": [] + }, + { + "name": "Modify a checked-out VM", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"lifetime\":\"2\"}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}" + ] + } + }, + "response": [ + { + "name": "lifetime", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"lifetime\":\"2\"}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}" + ] + } + }, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": null + }, + { + "name": "tags", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"tags\":{\"department\":\"engineering\",\"user\":\"jdoe\"}}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}" + ] + } + }, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": null + } + ] + }, + { + "name": "2) Add an additional disk to a running VM", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}/disk/{{additional_disk_size}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}", + "disk", + "{{additional_disk_size}}" + ] + } + }, + "response": [] + }, + { + "name": "2) Create a snapshot of a running VM Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "var hostname = pm.collectionVariables.get(\"vm_hostname\")", + "var hostname_notfqdn = hostname.split('.')[0]", + "pm.collectionVariables.set(\"snapshot_uuid\", jsonData[hostname_notfqdn].snapshot);", + "console.log(\"saving returned snapshot uuid to snapshot_uuid = \" + jsonData[hostname_notfqdn].snapshot)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}/snapshot/", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}", + "snapshot", + "" + ] + } + }, + "response": [] + }, + { + "name": "3) Revert a VM back to a snapshot", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/vm/{{vm_hostname}}/snapshot/{{snapshot_uuid}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "vm", + "{{vm_hostname}}", + "snapshot", + "{{snapshot_uuid}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "pool management", + "item": [ + { + "name": "1) Change pool size without having to restart the service.", + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"{{pool_management}}\":\"new_size\"}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/config/poolsize/", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "config", + "poolsize", + "" + ] + } + }, + "response": [] + }, + { + "name": "2) Delete an overridden pool size. This results in the values from VMPooler's config being used. Copy", + "request": { + "method": "DELETE", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "url": { + "raw": "{{url}}/{{api_prefix}}/config/poolsize/{{pool_management}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "config", + "poolsize", + "{{pool_management}}" + ] + } + }, + "response": [] + }, + { + "name": "1) Change the template configured for a pool, and replenish the pool with instances built from the new template. Copy", + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"{{pool_management}}\":\"templates/new_template\"}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/config/pooltemplate/", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "config", + "pooltemplate", + "" + ] + } + }, + "response": [] + }, + { + "name": "2) Delete an overridden pool template. This results in the values from VMPooler's config being used.", + "request": { + "method": "DELETE", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/config/pooltemplate/{{pool_management}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "config", + "pooltemplate", + "{{pool_management}}" + ] + } + }, + "response": [] + }, + { + "name": "poolreset: Clear all pending and ready instances in a pool, and deploy replacements", + "request": { + "method": "POST", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "default" + } + ], + "body": { + "mode": "raw", + "raw": "{\"{{pool_management}}\":\"1\"}" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/poolreset", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "poolreset" + ] + } + }, + "response": [] + } + ], + "description": "API to manage pool configuration \"live\". For example you can replace an existing pool size or template, in which case the new value will 'override' the pool config loaded at startup (on disk)" + }, + { + "name": "status: A \"live\" status endpoint, representing the current state of the service", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/status", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "poolstat", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/poolstat?pool={{pooled_pool_name}}", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "poolstat" + ], + "query": [ + { + "key": "pool", + "value": "{{pooled_pool_name}}" + }, + { + "key": "pool", + "value": "{{ondemand_pool_name}}", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "totalrunning", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/totalrunning", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "totalrunning" + ] + } + }, + "response": [] + }, + { + "name": "config returns the \"live\" pool configurations", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/config", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "config" + ] + } + }, + "response": [] + }, + { + "name": "full_config returns the full configuration, config, providers, pools etc", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/full_config", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "full_config" + ] + } + }, + "response": [] + }, + { + "name": "summary: Returns a summary, or report, for the timespan between from and to (inclusive) parameters Copy", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-AUTH-TOKEN", + "value": "{{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}/{{api_prefix}}/summary?from=YYYY-MM-DD&to=YYYY-MM-DD", + "host": [ + "{{url}}" + ], + "path": [ + "{{api_prefix}}", + "summary" + ], + "query": [ + { + "key": "from", + "value": "YYYY-MM-DD" + }, + { + "key": "to", + "value": "YYYY-MM-DD" + } + ] + } + }, + "response": [] + } + ], + "variable": [ + { + "key": "job_id", + "value": "" + }, + { + "key": "vm_hostname", + "value": "" + }, + { + "key": "snapshot_uuid", + "value": "" + } + ] +} \ No newline at end of file diff --git a/docs/postman/vmpooler generic.postman_environment.json b/docs/postman/vmpooler generic.postman_environment.json new file mode 100644 index 0000000..3363a90 --- /dev/null +++ b/docs/postman/vmpooler generic.postman_environment.json @@ -0,0 +1,63 @@ +{ + "id": "8c770a8b-db0d-4722-82d0-ec06712b9a44", + "name": "vmpooler generic", + "values": [ + { + "key": "url", + "value": "https://myserver.com/", + "type": "default", + "enabled": true + }, + { + "key": "token", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "api_prefix", + "value": "api/v2", + "type": "default", + "enabled": true + }, + { + "key": "ldap_user", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "ldap_pass", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "ondemand_pool_name", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "pooled_pool_name", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "additional_disk_size", + "value": "8", + "type": "default", + "enabled": true + }, + { + "key": "pool_management", + "value": "", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2022-04-01T17:17:09.868Z", + "_postman_exported_using": "Postman/9.15.2" +} \ No newline at end of file