REST ❤ Swagger, 2 minutes to create an API SDK ⏱ – Codegen Demo – Part 2
Overview
After a short introduction of Swagger Codegen and the context of the demo made for a French vMUG Webinar, we will see how to apply this to VMware products like vCenter and vCloud Director.
As this post is following the steps made in the part 1, I strongly encourage you to start with the previous post.
Pre-requisites
Same as previous post + we will rely on some environment variables to set some local settings:
1echo "Exporting vCenter env..."
2export VMWARE_VCENTER_HOSTNAME="vcsa.vupti.me"
3export VMWARE_VCENTER_USERNAME="demo@vsphere.local"
4export VMWARE_VCENTER_PASSWORD='************'
5
6echo "Exporting vCD env..."
7export VMWARE_VCD_HOSTNAME="vcd.vupti.me"
8export VMWARE_VCD_USERNAME="demo@orgdemo"
9export VMWARE_VCD_PASSWORD="************"
vCenter SDK with Codegen
1# Get the API swagger description
2curl -sk https://$VMWARE_VCENTER_HOSTNAME/apiexplorer/json/vcenter.json \
3 > codegen/in/vcenter.json
4
5# Create a config file for our future python module
6echo '{
7 "packageName":"vc_client",
8 "projectName":"vc-client",
9 "packageVersion":"6.7.0"
10}' > codegen/in/config_vc_client.json
11
12# Generate the SDK
13docker run --rm -v ${PWD}/codegen:/local \
14 swaggerapi/swagger-codegen-cli generate \
15 -i /local/in/vcenter.json \
16 -o /local/out/python-vc \
17 -c /local/in/config_vc_client.json \
18 -l python
19
20# Install new python mdule with PIP
21pip install codegen/out/python-vc
So in the above section, we created a new API SDK for a subset of vCenter REST APIs (and we do not rely on the pyvmomi
project).
We can now use it:
Prepare a client object
1import vc_client
2from vc_client.rest import ApiException
3from utils import *
4import os
5logger = logging.getLogger("DEMO_VCENTER")
6
7# Configure API
8configuration = vc_client.Configuration()
9configuration.verify_ssl = False
10configuration.host = f"https://{os.environ.get('VMWARE_VCENTER_HOSTNAME')}/rest"
11
12# credentials
13_username = os.environ.get('VMWARE_VCENTER_USERNAME')
14_password = os.environ.get('VMWARE_VCENTER_PASSWORD')
15auth_str = basic_auth_str(_username, _password)
16
17# Create a client
18client = vc_client.ApiClient(configuration)
So we import our new vc_client
module, we setup the target hostname and the authentication settings.
Get a new session
New goal: getting a session to use a cookie-based authentication instead of providing username/password for each request:
1# Get a new session
2try:
3 logger.debug("Starting a new session")
4 s = client.call_api(
5 '/com/vmware/cis/session',
6 "POST",
7 header_params={
8 "Authorization": auth_str,
9 })
10except ApiException as e:
11 print("Exception when creating session: %s\n" % e)
12 exit(-1)
13logger.info("New session is created")
14
15# Set the cookie according to the previous request result
16logger.debug("Setting new session authorization token in cookies")
17client.cookie = s[2].get('Set-Cookie')
18logger.info(f"Client cookies updated: {client.cookie}")
As you see, we rely on our module code by using the client.call_api
instruction and the cookie update: client.cookie = s[2].get('Set-Cookie')
.
Output:
1DEMO_VCENTER Starting a new session
2DEMO_VCENTER New session is created
3DEMO_VCENTER Setting new session authorization token in cookies
4DEMO_VCENTER Client cookies updated: vmware-api-session-id=5cebc...;Path=/rest;Secure;HttpOnly
Get some content
We will use our new session to get some data from the vCenter API.
Listing VMs:
1# List VM
2logger.debug("Listing VMs...")
3instance = vc_client.VM_Api(client)
4for vm in instance.list().value:
5 logger.info(
6 f"{vm.name}: {vm.power_state} / vCPU: {vm.cpu_count} / Mem: {vm.memory_size_mi_b} Mb"
7 )
8 keep_last = vm
Output:
1DEMO_VCENTER Listing VMs...
2DEMO_VCENTER LRI_RancherMaster: POWERED_ON / vCPU: 4 / Mem: 8192 Mb
3DEMO_VCENTER LRI_vRO: POWERED_ON / vCPU: 2 / Mem: 6144 Mb
4DEMO_VCENTER LRI_vcd: POWERED_ON / vCPU: 2 / Mem: 12288 Mb
5DEMO_VCENTER LRI_rabbitmq: POWERED_ON / vCPU: 2 / Mem: 3072 Mb
6DEMO_VCENTER ESXi7-01: POWERED_ON / vCPU: 2 / Mem: 4096 Mb
7DEMO_VCENTER vcsa7: POWERED_ON / vCPU: 2 / Mem: 12288 Mb
8DEMO_VCENTER ESXi7-02: POWERED_ON / vCPU: 2 / Mem: 4096 Mb
9DEMO_VCENTER ESXi7-03: POWERED_ON / vCPU: 2 / Mem: 4096 Mb
10DEMO_VCENTER LRI_ubuntu01: POWERED_ON / vCPU: 2 / Mem: 1024 Mb
11DEMO_VCENTER LRI_photon01: POWERED_ON / vCPU: 1 / Mem: 2048 Mb
12DEMO_VCENTER DCScope: POWERED_ON / vCPU: 2 / Mem: 8192 Mb
13DEMO_VCENTER FaH_1.0.0_01: POWERED_ON / vCPU: 8 / Mem: 8192 Mb
As you can see, we kept the last VM from the list to get more details:
1# Get more details for last VM
2logger.debug("Getting details about last VM...")
3vm_detailled = instance.get(keep_last.vm).value
4logger.info("Data:\n" +
5 json.dumps(vm_detailled.to_dict(), indent=2, default=str))
6
7# Get its network
8logger.debug("Getting a specifc detail about a VM:")
9nic = vm_detailled.nics[0].value
10logger.info(f"{keep_last.name} MAC address: {nic.mac_address}")
Output (only the VM details content):
1{
2 "memory": {
3 "hot_add_limit_mi_b": null,
4 "hot_add_enabled": false,
5 "hot_add_increment_size_mi_b": null,
6 "size_mi_b": 8192
7 },
8 "hardware": {
9 "upgrade_status": "NONE",
10 "upgrade_version": null,
11 "version": "VMX_13",
12 "upgrade_error": null,
13 "upgrade_policy": "NEVER"
14 },
15 "disks": [
16 {
17 "value": {
18 "label": "Hard disk 1",
19 "scsi": {
20 "unit": 0,
21 "bus": 0
22 },
23 "capacity": 4294967296,
24 "type": "SCSI",
25 "sata": null,
26 "ide": null,
27 "backing": {
28 "type": "VMDK_FILE",
29 "vmdk_file": "[vsanDatastore] d40d765e-a8c1-6d10-0d40-a0369fbcc808/FaH_1.0.0_01.vmdk"
30 }
31 },
32 "key": "2000"
33 }
34 ],
35 "cdroms": [
36 {
37 "value": {
38 "label": "CD/DVD drive 1",
39 "backing": {
40 "type": "CLIENT_DEVICE",
41 "device_access_type": "PASSTHRU",
42 "iso_file": null,
43 "host_device": null,
44 "auto_detect": null
45 },
46 "state": "NOT_CONNECTED",
47 "type": "IDE",
48 "sata": null,
49 "allow_guest_control": true,
50 "ide": {
51 "primary": true,
52 "master": true
53 },
54 "start_connected": false
55 },
56 "key": "3000"
57 }
58 ],
59 "boot_devices": [
60 {
61 "type": "CDROM",
62 "nic": null,
63 "disks": null
64 }
65 ],
66 "nics": [
67 {
68 "value": {
69 "wake_on_lan_enabled": false,
70 "state": "CONNECTED",
71 "label": "Network adapter 1",
72 "backing": {
73 "network": "dvportgroup-100",
74 "distributed_port": "69",
75 "distributed_switch_uuid": "50 2f 42 20 b1 c3 a5 2a-0d 95 b6 0c ab 7e b5 da",
76 "network_name": null,
77 "opaque_network_id": null,
78 "type": "DISTRIBUTED_PORTGROUP",
79 "opaque_network_type": null,
80 "connection_cookie": 1884582175,
81 "host_device": null
82 },
83 "start_connected": true,
84 "pci_slot_number": 160,
85 "type": "VMXNET3",
86 "upt_compatibility_enabled": true,
87 "allow_guest_control": true,
88 "mac_type": "ASSIGNED",
89 "mac_address": "00:50:56:af:db:98"
90 },
91 "key": "4000"
92 }
93 ],
94 "parallel_ports": [],
95 "guest_os": "VMWARE_PHOTON_64",
96 "name": "FaH_1.0.0_01",
97 "power_state": "POWERED_ON",
98 "sata_adapters": [],
99 "cpu": {
100 "count": 8,
101 "hot_add_enabled": false,
102 "hot_remove_enabled": false,
103 "cores_per_socket": 1
104 },
105 "serial_ports": [],
106 "floppies": [],
107 "scsi_adapters": [
108 {
109 "value": {
110 "pci_slot_number": 16,
111 "type": "LSILOGIC",
112 "label": "SCSI controller 0",
113 "scsi": {
114 "unit": 7,
115 "bus": 0
116 },
117 "sharing": "NONE"
118 },
119 "key": "1000"
120 }
121 ],
122 "boot": {
123 "retry_delay": 10000,
124 "enter_setup_mode": false,
125 "delay": 0,
126 "type": "BIOS",
127 "network_protocol": null,
128 "efi_legacy_boot": null,
129 "retry": false
130 }
131}
vCloud Director SDK with Codegen
Last example, using vCloud Director.
1# Get the API swagger description
2curl -sk https://$VMWARE_VCD_HOSTNAME/api-explorer/tenant/orgdemo/cloudapi.json \
3 > codegen/in/cloudapi.json
4
5# Create a config file for our future python module
6echo '{
7 "packageName":"vcd_client",
8 "projectName":"vcd-client",
9 "packageVersion":"9.7.1"
10}' > codegen/in/config_vcd_client.json
11
12# Generate the SDK
13docker run --rm -v ${PWD}/codegen:/local \
14 swaggerapi/swagger-codegen-cli generate \
15 -i /local/in/cloudapi.json \
16 -o /local/out/python-vcd \
17 -c /local/in/config_vcd_client.json \
18 -l python
19
20# Install new python mdule with PIP
21pip install codegen/out/python-vcd
Prepare a client object
We import our new vcd_client
module, we setup the target hostname, the authentication settings.
1import vcd_client
2from vcd_client.rest import ApiException
3from utils import *
4import os
5logger = logging.getLogger("DEMO_VCD")
6
7# Configure API
8configuration = vcd_client.Configuration()
9configuration.verify_ssl = False
10configuration.host = f"https://{os.environ.get('VMWARE_VCD_HOSTNAME')}/cloudapi"
11
12# credentials
13_username = os.environ.get('VMWARE_VCD_USERNAME')
14_password = os.environ.get('VMWARE_VCD_PASSWORD')
15auth_str = basic_auth_str(_username, _password)
16
17# Create a client
18client = vcd_client.ApiClient(configuration)
Get a new session
1# Get a new session
2try:
3 logger.debug("Starting a new session")
4 s = vcd_client.SessionsApi(client)
5 s_headers = s.login_with_http_info(authorization=auth_str)[2]
6except ApiException as e:
7 print("Exception when creating session: %s\n" % e)
8 exit(-1)
9logger.info("New session is created")
10
11# Update client with access token
12logger.debug("Setting new session authorization token in headers")
13configuration.api_key_prefix['Authorization'] = 'Bearer'
14configuration.api_key['Authorization'] = s_headers.get(
15 "X-VMWARE-VCLOUD-ACCESS-TOKEN"
16)
17client = vcd_client.ApiClient(configuration)
18logger.info(f"Client credentials updated to use access token: {s_headers.get('X-VMWARE-VCLOUD-ACCESS-TOKEN')}")
Output:
1DEMO_VCD Starting a new session
2DEMO_VCD New session is created
3DEMO_VCD Setting new session authorization token in headers
4DEMO_VCD Client credentials updated to use access token: eyJhbGciOiJSUzI1NiJ9...
Get some content
We will use our session and list our rights in the current organization:
1# List rights of the current user
2logger.debug("Getting rights of the current user")
3rapi = vcd_client.RightsApi(client)
4page, page_size = 1, 25
5try:
6 for right in rapi.query_rights(page, page_size).values:
7 logger.info(" - ".join([right.name, right.id, right.right_type]))
8except ApiException as e:
9 logger.error("Exception when calling RightsApi->query_rights: %s\n" % e)
Output (only fist part):
1DEMO_VCD Getting rights of the current user
2DEMO_VCD Organization vDC Gateway: Configure DNS - urn:vcloud:right:d85b0e92-b9e8-31af-9b19-23cd00cae7e7 - MODIFY
3DEMO_VCD Organization vDC Gateway: View DNS - urn:vcloud:right:c6563392-f6b3-3dd6-9720-b304e6319672 - VIEW
4DEMO_VCD Token: Manage - urn:vcloud:right:23e7a571-4928-3c49-891f-f835474a9dc3 - MODIFY
5DEMO_VCD Token: Manage All - urn:vcloud:right:67878e89-9d94-302f-92fd-997313c68ee1 - MODIFY
6DEMO_VCD API Explorer: View - urn:vcloud:right:9ff43a6c-2c50-3b53-b00f-6f020b6bb5a0 - VIEW
Conclusion
As you see, it is very easy to generate a new API client SDK from a VMware product. Authentication could require some customization but the most limiting thing will be linked to the limited available actions through the REST API on some products.
Anyway, for the available and documented REST API parts, you can now deliver/provide a lot of SDK, even without knowing the bases of the used language.