At the heart of the OpenStack cloud framework is the Nova API Service (Ken Pepple, 2011). This API Service makes command and control of the Nova Compute programmatically available to users in realization
of the definition of cloud computing. The API endpoints are basic http web services which handle authentication, authorization, and basic command and control functions using the Openstack API interface.
By default, nova-api listens on port 8774 for the OpenStack API. To accept and fulfill API requests, nova-api initiates most of the orchestration activities (such as boot servers and create flavors), and also enforces
some policies (mostly authentication, authorization, and quota checks). For some requests, it will fulfill the entire request itself by querying the database and then returning the answer. For more complicated
requests, it will pass messages to other daemons through a combination of writing information to the database and adding messages to the queue.
WSGI and Middleware
WSGI, an interface specification, specifies both server and application side interfaces. If an application is written to the WSGI spec then it will run on any server written to that spec (Phillip J. Eby, 2003).
These WSGI applications can be stacked. Middleware (those in the middle of the stack) must implement both sides of the WSGI interface, application and server. For the application on top of it it'll behave as a
server whereas it'll behave as an application for the application below.
The WSGI server's only job is to receive the request from the client, pass it to the application and then send the response provided by the application to the client – nothing else. The finer details must be supplied
by the application or middleware.
Webob, Routes, and Paste
Routes is a Python re-implementation of the Rails routes system for mapping URLs to application actions, and conversely to generate URLs. Routes makes it easy to create pretty and concise URLs that are
RESTful with little effort (Routes Documentation).
Webob is a Python library that provides wrappers around the WSGI request environment, and an object to help create WSGI responses. The objects map much of the specified behavior of HTTP, including header
parsing, content negotiation and correct handling of conditional and range requests (Webob).
Paste Deployment is a system for finding and configuring WSGI applications and servers. For WSGI application consumers it provides a single, simple function (loadapp) for loading a WSGI application from a
configuration file or a Python Egg. For WSGI application providers it only asks for a single, simple entry point to your application, so that application users don’t need to be exposed to the implementation details of
your application (Paste Deployment).
Nova API Architecture
<a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/e93514d3-c4f0-4aa0-8844-497f370090f5/resource/BLOGS_UPLOADED_IMAGES/novaapiarchitecture.jpeg" target="_blank" style="margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; color: rgb(25, 112, 176); text-decoration: none;">
nova.wsgi.Server serves as an embedded web server in every Nova API worker processes. It encapsulates and manages an eventlet greened version of WSGI server which runs on a self managed green
thread pool(size configurable) and a supplied socket with customized configuration(port number to listen & maximum number of queued connections).
Instead of directly feeding in a WSGI application to the server mentioned above, nova.api.openstack.compute.APIRouter is introduced to allow attaching multiple WSGI applications to a single WSGI server, which
is the reason why every core APIs and extension resources are themselves independent WSGI applications.
nova.wsgi.Router, super class of nova.api.openstack.compute.APIRouter, uses routes.middleware.RoutesMiddleware internally to map incoming requests to these WSGI applications.
nova.api.openstack.wsgi.Resource (implementation of nova.Application) is the one which actually defines the webob wrapped wsgi application entrance point and handles incoming requests. All core and
extension APIs are essentially controllers of nova.api.openstack.wsgi.Resource which is also responsible for its controller methods dispatching.
Extension API
There two ways to extend openstack API - creating new WSGI resources or extending controllers of existing WSGI resources. Both ways require writing a new module to declare controller classes to handle
requests and implement extensions.ExtensionDescriptor to register the newly created resource or controller extensions. Multiply resources and/or controller extensions can be defined in a single API module.
Security Policy
Security policies are enforced by nova.openstack.common.policy. Each API can have their own policies defined for every of its operations or on the whole API in nova/policy.json.
Note, rules definition will be interpreted recursively until the last one is resolved, and roles will be tested against the user's credentials.
Nova API Service Initialization
nova.wsgi.Server is not hard coded to load a nova.api.openstack.APIRouter implementation - instead how the router, along with some other filters, are deployed to the WSGI server is done by paste and
configured through paste configuration file. For instance, user authentication and rate limit are achieved by these filters. The name of the paste configuration file is api-paste.ini, and it's typically located
under /etc/nova.
Extension API Loading
Based on the osapi_compute_extension configuration, ExtensionManager will either load standard or selected extensions. The entry points for loading these extensions are defined in contrib package init
stage (standard_extensions and select_extensions). standard extension will load extension modules under given location (the contrib package for Compute and Volume APIs) complying with the standard
extension naming convention (a class inside a module has the same name as the module with initial capitalized), while selective extension loads the ones configured in nova configuration file.
The exact loading process consists of two stage: extension module loading and extension API setup, as illustrated in the following diagram:
An incoming request will first arrive at the nova.api.openstack.APIRouter instance and be handed over to its internally managed routes.middleware. RoutesMiddleware
instance where the request will be further routed to its destined WSGI resource according to the route mapping setup during the initialization phase. After a
WSGI resource (a core or extension API resource) receives the request, nova.api.openstack.wsgi.Resource multiplexes the incoming request to a request handling
method (index, create, delete... or other custom actions) which is the final place where a request is served.
How to write a core API
- write a controller to implement the standard actions (e.g. index, create, delete ......) and possibly other custom actions for the new RESTful resource
- set up the RESTful resource in APIRouter implementation, e.g. nova.api.openstack.compute.APIRouter for Compute
How to write an extension API
1. write a controller(s) to implement the standard actions (e.g. index, create, delete .....) and possibly other custom actions for the new RESTful resource(s)
or an existing one(s)
2. define a subclass of extensions.ExtensionDescriptor with concrete implementations of get_resources or/and get_controller_extensions methods to set up the new
resources or/and controller extensions, depending on whether it's requeired to register new RESTful resources, extend the controllers of existing RESTful
resources, or both.
3. put the controller(s) and the extensions descriptor class into a module which has the same name as the extensions descriptor with the initial in lower case in
order to let nova.api.openstack.extensions.load_standard_extensions recognize it when the system is configured to load standard extension.
4. place the module under a package where it can be loaded, this is typically the contrib package
5. supply a custom_routes_fn when creating resource extension (extensions.ResourceExtension) to set up a custom resource if desired, for example getting rid off
the {tenent_id} route component
Below is an simple extension API definition named “Documents”. The controller implementation defines the five basic operations (CRUD and List) to this Documents
collection and its member resource.
class DocumentController(object):
“””the Document s API controller deceleration”””
def index(self,req):
“””return a list of documents “””
pass
def create(self,req, body):
“””create a document “””
pass
def show(self,req, id):
“”” read the details of a document given its id”””
pass
def update(self, req, id, body):
“””updates a document given its id and content “””
pass
def delete(self,req, id):
“””removes a document given its id”””
pass
class Documents(extensions.ExtensionDescriptor):
“”“ExtensionDescriptor implementation”””
name = "document"
alias ="os-documents"
namespace ="......"
updated = "......"
def get_resources(self):
“”” register the new Documents RESTful resource ”””
resources = [extensions.ResourceExtension('os-documents',DocumentController())]
return resources
These operations can be invoked as follows,
GET v2/{tenant_id}/ os-documents
POST v2/{tenant_id}/ os-documents
GET v2/{tenant_id}/ os-documents/{document_id}
PUT v2/{tenant_id}/ os-documents/{document_id}
DELETE v2/{tenant_id}/ os-documents/{document_id}
How to debug Nova API in Pydev
1. avoid eventlet blindly greening everything (I suspect the monkey patch would make the debugger not able to block the thread execution.)
change eventlet.monkey_patch(os=False) to eventlet.monkey_patch(all=False,socket=True,select=True) in nova/bin/nova-api
2. make sure pydevd.py module is included in your python path
e.g. PYTHONPATH=/usr/share/eclipse/dropins/pydev/eclipse/plugins/org.python.pydev_2.6
3. insert
import pydevd;
pydevd.settrace()
to where you want to start debugging
4. start debug server, and switch to debug perspective in eclipse, click on Pydev menu and select start debug server on the pull down list run the remote program
i.e. nova/bin/nova-api for nova API
the program should be associated to the debug server then
Note:
- The reason why we cannot simply right click on nova-api and go Debug As--> Python Run in eclipse is that this will attach the debugger to the parent process
while its child processes are actually the ones that accept and serve incoming requests.
- Only one process can be associated to the debugger at a time.
References:
Ken, Pepple. (2011). Deploying Openstack. : O'Reilly Media.Phillip J. Eby. (07 Dec 2003). Python Web Server Gateway Interface. In PEP 333. Retrieved 29 Sep. 2012, from
http://www.python.org/dev/peps/pep-0333/Routes Documentation. Retrieved 25 Sep. 2012, from
http://routes.readthedocs.org/en/latest/Webob WSGI request and response objects. In Webob.org. Retrieved 25 Sep. 2012, from
http://webob.org/Paste Deployment. In Python Paste. Retrieved 25 Sep. 2012, from
http://pythonpaste.org/deploy/