This is a blog about the best practices to develop, test, debug and scale REST APIs in an OpenStack cloud. OpenStack is a great way to deploy a cloud in data centers and is one of the most popular open-source projects to deploy highly-scalable clouds. OpenStack is all about RESTful services. Every service in OpenStack talks to every other service using REST APIs. As a cloud operator deploying OpenStack, it is very important to understand how REST APIs work in order to debug and scale an OpenStack cloud effectively.
What is a RESTful service?
- Representational State Transfer (REST) is a stateless, client-server based, cacheable web service
- Uses HTTP protocol for communication
- Data is considered as a “resource” and accessed using Uniform Resource Identifiers (URIs) that typically look like a web link
- URI is RFC3986 (RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax)
- Uses HTTP clients like curl, browsers (Google Chrome Postman) and wget
REST in OpenStack
I gave a talk "Best REST in OpenStack" at Cisco Live in Las Vegas in 2016. My talk covered the following topics:
- Run OpenStack Neutron CLIs and analyze REST packets in WireShark
- REST for big data use cases – REST pagination in Neutron
- Implementing a RESTful server using Python Flask
The slides from my talk are at http://www.slideshare.net/Vikram_Hosakote/best-rest-in-openstack.
The scripts I used in my talk are at GitHub - vhosakot/Cisco-Live-Workshop: Scripts for Cisco Live Workshops.
Troubleshooting / debugging REST APIs
Sample script to troubleshoot / debug REST APIs using Python
import httplib
import logging
import requests
httplib.HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
result = requests.get('http://www.cisco.com/')
Output of above Python script with REST API debug messages
>>> result = requests.get('http://www.cisco.com/')
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): www.cisco.com
send: 'GET / HTTP/1.1\r\nHost: www.cisco.com\r\nConnection: keep-alive\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: python-requests/2.10.0\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Server: Apache
header: ETag: "10839-53789488e5b33"
header: Accept-Ranges: bytes
header: Content-Encoding: gzip
header: CDCHOST: wemxweb-publish-prod1-01
header: Content-Length: 14215
header: Content-Type: text/html
header: Expires: Wed, 13 Jul 2016 20:25:27 GMT
header: Cache-Control: max-age=0, no-cache, no-store
header: Pragma: no-cache
header: Date: Wed, 13 Jul 2016 20:25:27 GMT
header: Connection: keep-alive
header: access-control-allow-origin: *
DEBUG:requests.packages.urllib3.connectionpool:"GET / HTTP/1.1" 200 14215
>>> print result
<Response [200]>
Sample script to mock a REST server to unit-test Python REST APIs
$ python
>>> import requests
>>> import requests_mock
>>> session = requests.Session()
>>> adapter = requests_mock.Adapter()
>>> session.mount('mock', adapter)
>>> adapter.register_uri('GET', 'mock://test.com', text='data')
>>> resp = session.get('mock://test.com')
>>> resp.status_code, resp.text
(200, 'data')
https://github.com/openstack/requests-mock
https://pypi.python.org/pypi/mock-server/0.3.7
https://github.com/gabrielfalcao/HTTPretty
http://python-mock-tutorial.readthedocs.io/en/latest/mock.html
REST API caching and cache-aware REST clients
RFC 7234 - https://tools.ietf.org/html/rfc7234#section-5
REST supports the following HTTP cache-control headers:
- max-age
- max-stale
- min-fresh
- no-cache
- no-store
- no-transform
- only-if-cached
- must-revalidate
- public
- private
Bulk REST operations and REST API batching
Bulk REST operations allow us to perform REST operations (GET, POST, PUT, DELETE) on bulk (more than one) objects (networks, ports, subnets, etc) at once. This reduces the number of REST messages between client and the server.
http://developer.openstack.org/api-ref-networking-v2.html#bulkCreateNetwork
https://wiki.openstack.org/wiki/Neutron/APIv2-specification#Bulk_version
Bulk REST API JSON request format to create five neutron networks in OpenStack using POST operation:
POST v2.0/networks.json
Content-Type: application/json
Accept: application/json
{
"networks": [
{
"name": "sample_network_1",
"admin_state_up": false
},
{
"name": "sample_network_2",
"admin_state_up": false
}
{
"name": "sample_network_3”,
"admin_state_up": true
}
{
"name": "sample_network_4”,
"admin_state_up": true
}
{
"name": "sample_network_5”,
"admin_state_up": false
}]
}