API - Application Programming Interface¶
QTIP consists of different tools(metrics) to benchmark the NFVI. These metrics fall under different NFVI subsystems(QPI’s) such as compute, storage and network. A plan consists of one or more QPI’s, depending upon how the end-user would want to measure performance. API is designed to expose a RESTful interface to the user for executing benchmarks and viewing respective scores.
Framework¶
QTIP API has been created using the Python package Connexion. It has been chosen for a number of reasons. It follows API First approach to create micro-services. Hence, firstly the API specifications are defined from the client side perspective, followed by the implementation of the micro-service. It decouples the business logic from routing and resource mapping making design and implementation cleaner.
It has two major components:
API Specifications
The API specification is defined in a yaml or json file. Connexion follows Open API specification to determine the design and maps the endpoints to methods in python.
- Micro-service Implementation
- Connexion maps the
operationId
corresponding to every operation in API Specification to methods in python which handles request and responses.
As explained, QTIP consists of metrics, QPI’s and plans. The API is designed to provide a RESTful interface to all these components. It is responsible to provide listing and details of each individual element making up these components.
Design¶
Specification¶
API’s entry point (main
) runs connexion App
class object after adding API Specification
using App.add_api
method. It loads specification from swagger.yaml
file by specifying
specification_dir
.
Connexion reads API’s endpoints(paths), operations, their request and response parameter
details and response definitions from the API specification i.e. swagger.yaml
in this case.
Following example demonstrates specification for the resource plans
.
paths:
/plans/{name}:
get:
summary: Get a plan by plan name
operationId: qtip.api.controllers.plan.get_plan
tags:
- Plan
- Standalone
parameters:
- name: name
in: path
description: Plan name
required: true
type: string
responses:
200:
description: Plan information
schema:
$ref: '#/definitions/Plan'
404:
description: Plan not found
schema:
$ref: '#/definitions/Error'
501:
description: Resource not implemented
schema:
$ref: '#/definitions/Error'
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
definitions:
Plan:
type: object
required:
- name
properties:
name:
type: string
description:
type: string
info:
type: object
config:
type: object
Every operationId
in above operations corresponds to a method in controllers.
QTIP has three controller modules each for plan, QPI and metric. Connexion will
read these mappings and automatically route endpoints to business logic.
Swagger Editor can be explored to play with more such examples and to validate the specification.
Controllers¶
The request is handled through these methods and response is sent back to the client. Connexion takes care of data validation.
@common.check_endpoint_for_error(resource='Plan')
def get_plan(name):
plan_spec = plan.Plan(name)
return plan_spec.content
In above code get_plan
takes a plan name and return its content.
The decorator check_endpoint_for_error
defined in common
is used to handle error
and return a suitable error response.
During Development the server can be run by passing specification file(swagger.yaml
in this case) to connexion cli -
connexion run <path_to_specification_file> -v
Extending the Framework¶
Modifying Existing API:¶
API can be modified by adding entries in
swagger.yaml
and adding the corresponding controller mapped fromoperationID
.Adding endpoints:
New endpoints can be defined in
paths
section inswagger.yaml
. To add a new resource dummy -paths: /dummies: get: summary: Get all dummies operationId: qtip.api.controllers.dummy.get_dummies tags: - dummy responses: 200: description: Foo information schema: $ref: '#/definitions/Dummy default: description: Unexpected error schema: $ref: '#/definitions/Error'And then model of the resource can be defined in the
definitions
section.definitions: Dummy: type: object required: - name properties: name: type: string description: type: string id: type: string
- Adding controller methods:
Methods for handling requests and responses for every operation for the endpoint added can be implemented in
controller
.In
controllers.dummy
def get_dummies(): all_dummies = [<code to get all dummies>] return all_dummies, httplib.OK- Adding error responses
Decorators for handling errors are defined in
common.py
inapi
.from qtip.api import common @common.check_endpoint_for_error(resource='dummy',operation='get') def get_dummies() all_dummies = [<code to get all dummies>] return all_dummies
Adding new API:¶
API can easily be extended by adding more APIs to
Connexion.App
class object usingadd_api
class method.In
__main__
def get_app(): app = connexion.App(__name__, specification_dir=swagger_dir) app.add_api('swagger.yaml', base_path='/v1.0', strict_validation=True) return appExtending it to add new APIs. The new API should have all endpoints mapped using
operationId
.from qtip.api import __main__ my_app = __main__.get_app() my_app.add_api('new_api.yaml',base_path'api2',strict_validation=True) my_app.run(host="0.0.0.0", port=5000)