diff options
Diffstat (limited to 'docs/testing/developer/devguide')
-rw-r--r-- | docs/testing/developer/devguide/api.rst | 239 |
1 files changed, 237 insertions, 2 deletions
diff --git a/docs/testing/developer/devguide/api.rst b/docs/testing/developer/devguide/api.rst index eb2b0d67..48ae3ae4 100644 --- a/docs/testing/developer/devguide/api.rst +++ b/docs/testing/developer/devguide/api.rst @@ -1,2 +1,237 @@ -- Which framework has been used and why -- How to extend to more api +********************************************** +QTIP RESTful Application Programming Interface +********************************************** + +Abstract +######## + +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. + +.. code-block:: python + + @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 from ``operationID``. + + Adding endpoints: + + New endpoints can be defined in ``paths`` section in ``swagger.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`` + + .. code-block:: python + + 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`` in ``api``. + + .. code-block:: python + + 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 using + ``add_api`` class method. + + In ``__main__`` + + .. code-block:: python + + def get_app(): + app = connexion.App(__name__, specification_dir=swagger_dir) + app.add_api('swagger.yaml', base_path='/v1.0', strict_validation=True) + return app + + + Extending it to add new APIs. The new API should have all endpoints mapped using ``operationId``. + + .. code-block:: python + + 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) + + +.. _Connexion: https://connexion.readthedocs.io/en/latest/ +.. _Open API specification: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md +.. _Swagger Editor: http://editor.swagger.io/ |