summaryrefslogtreecommitdiffstats
path: root/docs/testing/developer/devguide/api.rst
blob: 491c70f8a0076169b3f73f2153596414492ba482 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
.. This work is licensed under a Creative Commons Attribution 4.0 International License.
.. http://creativecommons.org/licenses/by/4.0


***************************************
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.

.. 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/