Soft Patch Panel
 help / color / mirror / Atom feed
* [spp] [PATCH] spp-ctl: SPP controller with Web API
@ 2018-09-12 23:25 Itsuro ODA
  2018-09-18 10:00 ` Yasufumi Ogawa
                   ` (2 more replies)
  0 siblings, 3 replies; 33+ messages in thread
From: Itsuro ODA @ 2018-09-12 23:25 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

spp-ctl is a SPP controller with a REST like web API.

spp-ctl maintains the connections from the SPP processes and at
the same time exposes the API for the user to request for the
SPP processes.

Background and motivation:

Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
Therefore, spp-agent, a component of networking-spp which make
SPP available on OpenStack environment, implements SPP controller
in itself. (see. https://github.com/openstack/networking-spp )

Either CLI or spp-agent, there is a problem that other people can
not request to SPP processes while using. spp-ctl is invented to
solve this problem.

Both CLI and spp-agent can be used spp-ctl to request SPP
processes instead of owning contoroller itself. In that case,
multiple people can request to SPP processes at the same time.
Note that spp-agent has a plan to change to use spp-ctl.
It is also available not using CLI but requesting spp-ctl
directly.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/README.rst        | 102 +++++
 src/spp-ctl/api-reference.rst | 790 ++++++++++++++++++++++++++++++++++
 src/spp-ctl/requirements.txt  |   3 +
 src/spp-ctl/spp-ctl           |  11 +
 src/spp-ctl/spp_ctl.py        | 158 +++++++
 src/spp-ctl/spp_proc.py       | 184 ++++++++
 src/spp-ctl/spp_webapi.py     | 440 +++++++++++++++++++
 7 files changed, 1688 insertions(+)
 create mode 100644 src/spp-ctl/README.rst
 create mode 100644 src/spp-ctl/api-reference.rst
 create mode 100644 src/spp-ctl/requirements.txt
 create mode 100755 src/spp-ctl/spp-ctl
 create mode 100644 src/spp-ctl/spp_ctl.py
 create mode 100644 src/spp-ctl/spp_proc.py
 create mode 100644 src/spp-ctl/spp_webapi.py

diff --git a/src/spp-ctl/README.rst b/src/spp-ctl/README.rst
new file mode 100644
index 0000000..847b9dc
--- /dev/null
+++ b/src/spp-ctl/README.rst
@@ -0,0 +1,102 @@
+====================================
+spp-ctl: SPP controller with Web API
+====================================
+
+Overview
+========
+
+spp-ctl is a SPP controller with a REST like web API.
+
+spp-ctl maintains the connections from the SPP processes and at the same
+time exposes the API for the user to request for the SPP processes.
+
+Background and motivation
+-------------------------
+
+Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
+Therefore, spp-agent, a component of networking-spp which make SPP
+available on OpenStack environment, implements SPP controller in
+itself. (see. https://github.com/openstack/networking-spp )
+
+Either CLI or spp-agent, there is a problem that other people can not
+request to SPP processes while using. spp-ctl is invented to solve this
+problem.
+
+Both CLI and spp-agent can be used spp-ctl to request SPP processes
+instead of owning contoroller itself. In that case, multiple people
+can request to SPP processes at the same time.
+Note that spp-agent has a plan to change to use spp-ctl.
+It is also available not using CLI but requesting spp-ctl directly.
+
+Architecture
+------------
+
+The design goal of spp-ctl is to be as simple as possible.
+It is stateless. Basically, spp-ctl only converts API requests into
+commands of SPP processes and throws request, thouth it does syntax and
+lexical check for API requests.
+
+spp-ctl adopts bottle (it is simple and well known) as a web framework
+and eventlet for parallel processing. spp-ctl can process multiple APIs
+at the same time, however, requests for per SPP process are serialized
+internally.
+
+
+Setup
+=====
+
+spp-ctl is a simple program written in python3. Installation of related
+packages is as follows (assume ubuntu).
+
+::
+
+  $ sudo apt update
+  $ sudo apt install python3
+  $ sudo apt install python3-pip
+  $ sudo pip3 install -r requirements.txt
+
+Usage
+-----
+
+::
+
+  usage: spp-ctl [-p PRI_PORT] [-s SEC_PORT] [-a API_PORT]
+
+  optional arguments:
+    -p PRI_PORT  primary port. default is 5555.
+    -s SEC_PORT  secondary port. default is 6666.
+    -a API_PORT  web api port. default is 7777.
+
+Using systemd
+-------------
+
+Although spp-ctl runs as a daemon process normaly, it assumes to the
+use of systemd and does not daemonize itself.
+
+The service file for systemd is simple as shown below::
+
+  [Unit]
+  Description = SPP Controller
+
+  [Service]
+  ExecStart = {SPP install path}/spp_ctl/spp-ctl -p 5555 -s 6666 -a 7777
+  User = root
+
+API Usage
+=========
+
+For API details, see API-reference_.
+
+.. _API-reference: ./api-reference.rst
+
+Since spp-ctl provides the web API, for example, you can use curl to execute
+requests as follows::
+
+  $ curl http://localhost:7777/v1/processes
+  [{"type": "primary"}, {"client-id": 1, "type": "vf"}, {"client-id": 2, "type": "vf"}]
+  $ curl http://localhost:7777/v1/vfs/1
+  ... snip
+  $ curl -X POST http://localhost:7777/v1/vfs/1/components \
+    -d '{"core": 2, "name": "forward_0_tx", "type": "forward"}'
+  $
+
diff --git a/src/spp-ctl/api-reference.rst b/src/spp-ctl/api-reference.rst
new file mode 100644
index 0000000..b8bf488
--- /dev/null
+++ b/src/spp-ctl/api-reference.rst
@@ -0,0 +1,790 @@
+=============
+API Reference
+=============
+
+Overview
+========
+
+Spp-ctl provides simple REST like API. It supports http only, not https.
+
+Request and Response
+--------------------
+
+Request body is json format. It is accepted both "text/plain" and "application/json" for the content-type header.
+
+Response of GET is json format and the content-type is "application/json" if the request success.
+
+If a request fails, the response is a text which shows error reason and the content-type is "text/plain".
+
+Error code
+----------
+
+Spp-ctl does basic syntax and lexical check of a request.
+
++-------+------------------------------------------------------------------------------+
+| Error | Description                                                                  |
++=======+==============================================================================+
+| 400   | Syntax or lexical error of a request.                                        |
+|       | Or an SPP process returns error for the command correspondings to a request. |
++-------+------------------------------------------------------------------------------+
+| 404   | URL is not supported for spp-ctl.                                            |
+|       | Or there is no SPP process of client-id in a URL.                            |
++-------+------------------------------------------------------------------------------+
+| 500   | A system error occurs in the spp-ctl.                                        |
+|       | ex. communication error between an SPP processes.                            |
++-------+------------------------------------------------------------------------------+
+
+
+API independent of the process type
+===================================
+
+GET /v1/processes
+-----------------
+
+Show the SPP processes connected to the spp-ctl.
+
+Normarl response codes: 200
+
+Response
+^^^^^^^^
+
+An array of process objects.
+
+process object:
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| type      | string  | process type. one of "primary", "vf" or "nfv".              |
++-----------+---------+-------------------------------------------------------------+
+| client-id | integer | client id. if type is "primary" this member does not exist. |
++-----------+---------+-------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+::
+
+[{"type": "primary"}, {"type": "vf", "client-id": 1}, {"type": "nfv", "client-id": 2}]
+
+
+API for spp_vf
+==============
+
+GET /v1/vfs/{client_id}
+-----------------------
+
+Get the information of the spp_vf process.
+
+Normal response codes: 200
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Response
+^^^^^^^^
+
++------------------+---------+-------------------------------------------------------------+
+| Name             | Type    | Description                                                 |
++==================+=========+=============================================================+
+| client-id        | integer | client id.                                                  |
++------------------+---------+-------------------------------------------------------------+
+| ports            | array   | an array of port ids used by the process.                   |
++------------------+---------+-------------------------------------------------------------+
+| components       | array   | an array of component objects in the process.               |
++------------------+---------+-------------------------------------------------------------+
+| classifier_table | array   | an array of classifier tables in the process.               |
++------------------+---------+-------------------------------------------------------------+
+
+component object:
+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| core    | integer | core id running on the component                                    |
++---------+---------+---------------------------------------------------------------------+
+| name    | string  | an array of port ids used by the process.                           |
++---------+---------+---------------------------------------------------------------------+
+| type    | string  | an array of component objects in the process.                       |
++---------+---------+---------------------------------------------------------------------+
+| rx_port | array   | an array of port objects connected to the rx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+| tx_port | array   | an array of port objects connected to the tx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+
+port object:
+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}.       |
++---------+---------+---------------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port.                        |
++---------+---------+---------------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+-------------------------------------------------------------------+
+| Name      | Type    | Description                                                       |
++===========+=========+===================================================================+
+| operation | string  | "add", "del" or "none".                                           |
++-----------+---------+-------------------------------------------------------------------+
+| id        | integer | vlan id.                                                          |
++-----------+---------+-------------------------------------------------------------------+
+| pcp       | integer | vlan pcp.                                                         |
++-----------+---------+-------------------------------------------------------------------+
+
+classifier table:
+
++-----------+---------+----------------------------------------------------------------------+
+| Name      | Type    | Description                                                          |
++===========+=========+======================================================================+
+| type      | string  | "mac" or "vlan".                                                     |
++-----------+---------+----------------------------------------------------------------------+
+| value     | string  | "mac address" for "mac" type. "vlan id/mac address" for "vlan" type. |
++-----------+---------+----------------------------------------------------------------------+
+| port      | string  | port id applied to classify.                                         |
++-----------+---------+----------------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+::
+
+{
+"client-id": 1,
+"ports": ["phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"],
+"components": [
+{
+"core": 2,
+"name": "forward_0_tx",
+"type": "forward",
+"rx_port": [
+{
+"port": "ring:0",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+],
+"tx_port": [
+{
+"port": "vhost:0",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+]
+},
+{
+"core": 3,
+"type": "unuse"
+},
+{
+"core": 4,
+"type": "unuse"
+},
+{
+"core": 5,
+"name": "forward_1_rx",
+"type": "forward",
+"rx_port": [
+{
+"port": "vhost:1",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+],
+"tx_port": [
+{
+"port": "ring:3",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+]
+},
+{
+"core": 6,
+"name": "classifier",
+"type": "classifier_mac",
+"rx_port": [
+{
+"port": "phy:0",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+],
+"tx_port": [
+{
+"port": "ring:0",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+},
+{
+"port": "ring:2",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+]
+},
+{
+"core": 7,
+"name": "merger",
+"type": "merge",
+"rx_port": [
+{
+"port": "ring:1",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+},
+{
+"port": "ring:3",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+],
+"tx_port": [
+{
+"port": "phy:0",
+"vlan": { "operation": "none", "id": 0, "pcp": 0 }
+}
+]
+}
+},
+"classifier_table": [
+{
+"type": "mac",
+"value": "FA:16:3E:7D:CC:35",
+"port": "ring:0"
+}
+]
+}
+
+Note: The component which type is "unused" is to indicate unused core.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};status
+
+
+POST /v1/vfs/{client_id}/components
+-----------------------------------
+
+Start the component.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-----------+---------+----------------------------------------------------------------+
+| Name      | Type    | Description                                                    |
++===========+=========+================================================================+
+| name      | string  | component name. must be unique in the process.                 |
++-----------+---------+----------------------------------------------------------------+
+| core      | integer | core id.                                                       |
++-----------+---------+----------------------------------------------------------------+
+| type      | string  | component type. one of "forward", "merge" or "classifier_mac". |
++-----------+---------+----------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+::
+
+{"name": "forwarder1", "core": 12, "type": "forward"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful POST request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};component start {name} {core} {type}
+
+
+DELETE /v1/vfs/{sec id}/components/{name}
+-----------------------------------------
+
+Stop the component.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+| name      | string  | component name.                                             |
++-----------+---------+-------------------------------------------------------------+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful POST request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};component stop {name}
+
+
+PUT /v1/vfs/{client_id}/components/{name}/ports
+-----------------------------------------------
+
+Add or Delete port to the component.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+| name      | string  | component name.                                             |
++-----------+---------+-------------------------------------------------------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| action  | string  | "attach" or "detach".                                               |
++---------+---------+---------------------------------------------------------------------+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}.       |
++---------+---------+---------------------------------------------------------------------+
+| dir     | string  | "rx" or "tx".                                                       |
++---------+---------+---------------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port. it can be omitted.     |
++---------+---------+---------------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+-------------------------------------------------------------------+
+| Name      | Type    | Description                                                       |
++===========+=========+===================================================================+
+| operation | string  | "add", "del" or "none".                                           |
++-----------+---------+-------------------------------------------------------------------+
+| id        | integer | vlan id. ignored when operation is "del" or "none".               |
++-----------+---------+-------------------------------------------------------------------+
+| pcp       | integer | vlan pcp. ignored when operation is "del" or "none".              |
++-----------+---------+-------------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+::
+
+{"action": "attach", "port": "vhost:1", "dir": "rx",
+"vlan": {"operation": "add", "id": 677, "pcp": 0}
+}
+
+::
+
+{"action": "detach", "port": "vhost:0", "dir": "tx"}
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful PUT request.
+
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+action is "attach"
+
+::
+
+sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
+
+action is "detach"
+
+::
+
+sec {client_id};port del {port} {dir} {name}
+
+
+PUT /v1/vfs/{sec id}/classifier_table
+-------------------------------------
+
+Set or Unset classifier table.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-------------+-----------------+-------------------------------------------------------------------+
+| Name        | Type            | Description                                                       |
++=============+=================+===================================================================+
+| action      | string          | "add" or "del".                                                   |
++-------------+-----------------+-------------------------------------------------------------------+
+| type        | string          | "mac" or "vlan".                                                  |
++-------------+-----------------+-------------------------------------------------------------------+
+| vlan        | integer or null | vlan id when type is "vlan. null or omitted when type is "mac".   |
++-------------+-----------------+-------------------------------------------------------------------+
+| mac_address | string          | mac address.                                                      |
++-------------+-----------------+-------------------------------------------------------------------+
+| port        | string          | port id.                                                          |
++-------------+-----------------+-------------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+::
+
+{"action": "add", "type": "mac",
+"mac_address": "FA:16:3E:7D:CC:35", "port": "ring:0"
+}
+
+::
+
+{"action": "del", "type": "vlan", "vlan": 475,
+"mac_address": "FA:16:3E:7D:CC:35", "port": "ring:0"
+}
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful PUT request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+type is "mac"
+
+::
+
+classifier_table {action} mac {mac_address} {port}
+
+type is "vlan"
+
+::
+
+classifier_table {action} vlan {vlan} {mac_address} {port}
+
+
+API for spp_nfv/spp_vm
+======================
+
+GET /v1/nfvs/{client_id}
+------------------------
+
+Get the information of the spp_nfv/spp_vm process.
+
+Normal response codes: 200
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Response
+^^^^^^^^
+
++------------------+---------+-------------------------------------------------------------+
+| Name             | Type    | Description                                                 |
++==================+=========+=============================================================+
+| client-id        | integer | client id.                                                  |
++------------------+---------+-------------------------------------------------------------+
+| status           | string  | "Running" or "Idle".                                        |
++------------------+---------+-------------------------------------------------------------+
+| ports            | array   | an array of port ids used by the process.                   |
++------------------+---------+-------------------------------------------------------------+
+| patches          | array   | an array of patches.                                        |
++------------------+---------+-------------------------------------------------------------+
+
+patch objest
+
++----------+---------+-------------------------------------------------------------+
+| Name     | Type    | Description                                                 |
++==========+=========+=============================================================+
+| src      | string  | source port id.                                             |
++----------+---------+-------------------------------------------------------------+
+| dst      | string  | destination port id.                                        |
++----------+---------+-------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+::
+
+{
+"client-id": 1,
+"status": "Running",
+"ports": ["phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"],
+"patches": [
+{"src": "vhost:0", "dst": "ring:0"},
+{"src": "ring:1", "dst": "vhost:1"}
+]
+}
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};status
+
+
+PUT /v1/nfvs/{client_id}/forward
+--------------------------------
+
+Start or Stop forwarding.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| action    | string  | "start" or "stop".                                          |
++-----------+---------+-------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+::
+
+{"action": "start"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful PUT request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+action is "start"
+
+::
+
+sec {client_id};forward
+
+action is "stop"
+
+::
+
+sec {client_id};stop
+
+
+PUT /v1/nfvs/{client_id}/ports
+------------------------------
+
+Add or Delete port.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-----------+---------+---------------------------------------------------------------+
+| Name      | Type    | Description                                                   |
++===========+=========+===============================================================+
+| action    | string  | "add" or "del".                                               |
++-----------+---------+---------------------------------------------------------------+
+| port      | string  | port id. port id is the form {interface_type}:{interface_id}. |
++-----------+---------+---------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+::
+
+{"action": "add", "port": "vhost:0"}
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful PUT request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};{action} {interface_type} {interface_id}
+
+
+PUT /v1/nfvs/{client_id}/patches
+--------------------------------
+
+Add a patch.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++----------+---------+-------------------------------------------------------------+
+| Name     | Type    | Description                                                 |
++==========+=========+=============================================================+
+| src      | string  | source port id.                                             |
++----------+---------+-------------------------------------------------------------+
+| dst      | string  | destination port id.                                        |
++----------+---------+-------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+::
+
+{"src": "vhost:0", "dst": "ring:0"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful PUT request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};patch {src} {dst}
+
+
+DELETE /v1/nfvs/{client_id}/patches
+-----------------------------------
+
+Reset patches.
+
+Normal response codes: 204
+
+Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------------------------------------------------------+
+| Name      | Type    | Description                                                 |
++===========+=========+=============================================================+
+| client_id | integer | client id.                                                  |
++-----------+---------+-------------------------------------------------------------+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful DELETE request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+sec {client_id};patch reset
+
+
+API for spp_primary
+===================
+
+GET /v1/primary/status
+----------------------
+
+Show statistical information.
+
+Normal response codes: 200
+
+Response
+^^^^^^^^
+
+There is no data at the moment. The statistical information will be returned when spp_primary
+implements it.
+
+
+DELETE /v1/primary/status
+-------------------------
+
+Clear statistical information.
+
+Normal response codes: 204
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful PUT request.
+
diff --git a/src/spp-ctl/requirements.txt b/src/spp-ctl/requirements.txt
new file mode 100644
index 0000000..cc52d48
--- /dev/null
+++ b/src/spp-ctl/requirements.txt
@@ -0,0 +1,3 @@
+eventlet
+bottle
+netaddr
diff --git a/src/spp-ctl/spp-ctl b/src/spp-ctl/spp-ctl
new file mode 100755
index 0000000..645611b
--- /dev/null
+++ b/src/spp-ctl/spp-ctl
@@ -0,0 +1,11 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import sys
+
+from spp_ctl import main
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/src/spp-ctl/spp_ctl.py b/src/spp-ctl/spp_ctl.py
new file mode 100644
index 0000000..e168747
--- /dev/null
+++ b/src/spp-ctl/spp_ctl.py
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import eventlet
+eventlet.monkey_patch()
+
+import argparse
+import errno
+import json
+import logging
+import socket
+import subprocess
+
+import spp_proc
+import spp_webapi
+
+
+LOG = logging.getLogger(__name__)
+
+
+MSG_SIZE = 4096
+
+
+class Controller(object):
+
+    def __init__(self, pri_port, sec_port, api_port):
+        self.web_server = spp_webapi.WebServer(self, api_port)
+        self.procs = {}
+        self.init_connection(pri_port, sec_port)
+
+    def start(self):
+        self.web_server.start()
+
+    def init_connection(self, pri_port, sec_port):
+        self.pri_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.pri_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.pri_sock.bind(('127.0.0.1', pri_port))
+        self.pri_sock.listen(1)
+        self.primary_listen_thread = eventlet.greenthread.spawn(
+            self.accept_primary)
+
+        self.sec_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sec_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.sec_sock.bind(('127.0.0.1', sec_port))
+        self.sec_sock.listen(1)
+        self.secondary_listen_thread = eventlet.greenthread.spawn(
+            self.accept_secondary)
+
+    def accept_primary(self):
+        while True:
+            conn, _ = self.pri_sock.accept()
+            proc = self.procs.get(spp_proc.ID_PRIMARY)
+            if proc is not None:
+                LOG.warning("spp_primary reconnect !")
+                with proc.sem:
+                    try:
+                        proc.conn.close()
+                    except Exception:
+                        pass
+                    proc.conn = conn
+                # NOTE: when spp_primary restart, all secondarys must be
+                # restarted. this is out of controle of spp-ctl.
+            else:
+                LOG.info("primary connected.")
+                self.procs[spp_proc.ID_PRIMARY] = spp_proc.PrimaryProc(conn)
+
+    def accept_secondary(self):
+        while True:
+            conn, _ = self.sec_sock.accept()
+            LOG.debug("sec accepted: get process id")
+            proc = self._get_proc(conn)
+            if proc is None:
+                LOG.error("get process id failed")
+                conn.close()
+                continue
+            old_proc = self.procs.get(proc.id)
+            if old_proc:
+                LOG.warning("%s(%d) reconnect !", old_proc.type, old_proc.id)
+                if old_proc.type != proc.type:
+                    LOG.warning("type changed ! new type: %s", proc.type)
+                with old_proc.sem:
+                    try:
+                        old_proc.conn.close()
+                    except Exception:
+                        pass
+            else:
+                LOG.info("%s(%d) connected.", proc.type, proc.id)
+            self.procs[proc.id] = proc
+
+    @staticmethod
+    def _continue_recv(conn):
+        try:
+            # must set non-blocking to recieve remining data not to happen
+            # blocking here.
+            # NOTE: usually MSG_DONTWAIT flag is used for this purpose but
+            # this flag is not supported under eventlet.
+            conn.setblocking(False)
+            data = b""
+            while True:
+                try:
+                    rcv_data = conn.recv(MSG_SIZE)
+                    data += rcv_data
+                    if len(rcv_data) < MSG_SIZE:
+                        break
+                except socket.error as e:
+                    if e.args[0] == errno.EAGAIN:
+                        # OK, no data remining. this happens when recieve data
+                        # length is just multiple of MSG_SIZE.
+                        break
+                    raise e
+            return data
+        finally:
+            conn.setblocking(True)
+
+    @staticmethod
+    def _send_command(conn, command):
+        conn.sendall(command.encode())
+        data = conn.recv(MSG_SIZE)
+        if data and len(data) == MSG_SIZE:
+            # could not receive data at once. recieve remining data.
+            data += self._continue_recv(conn)
+        if data:
+            data = data.decode()
+        return data
+
+    def _get_proc(self, conn):
+        # it is a bit ad hoc. send "_get_clinet_id" command and try to
+        # decode reply for each proc type. if success, that is the type.
+        data = self._send_command(conn, "_get_client_id")
+        for proc in [spp_proc.VfProc, spp_proc.NfvProc]:
+            sec_id = proc._decode_client_id(data)
+            if sec_id is not None:
+                return proc(sec_id, conn)
+
+    def get_processes(self):
+        procs = []
+        for proc in self.procs.values():
+            p = {"type": proc.type}
+            if proc.id != spp_proc.ID_PRIMARY:
+                p["client-id"] = proc.id
+            procs.append(p)
+        return procs
+
+
+def main():
+    parser = argparse.ArgumentParser(description="SPP Controller")
+    parser.add_argument("-p", dest='pri_port', type=int, default=5555,
+                        action='store', help="primary port")
+    parser.add_argument("-s", dest='sec_port', type=int, default=6666,
+                        action='store', help="secondary port")
+    parser.add_argument("-a", dest='api_port', type=int, default=7777,
+                        action='store', help="web api port")
+    args = parser.parse_args()
+
+    logging.basicConfig(level=logging.DEBUG)
+
+    controller = Controller(args.pri_port, args.sec_port, args.api_port)
+    controller.start()
diff --git a/src/spp-ctl/spp_proc.py b/src/spp-ctl/spp_proc.py
new file mode 100644
index 0000000..929942a
--- /dev/null
+++ b/src/spp-ctl/spp_proc.py
@@ -0,0 +1,184 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import bottle
+import eventlet
+import json
+import logging
+
+import spp_ctl
+
+
+LOG = logging.getLogger(__name__)
+
+ID_PRIMARY = 0
+TYPE_PRIMARY = "primary"
+TYPE_VF = "vf"
+TYPE_NFV = "nfv"
+
+
+def exec_command(func):
+    """Define the common function of sending command & receiving reply
+    as a decorator.
+    each method for executing command has only to return command string.
+    ex.
+    @exec_command
+    def some_command(self, ...):
+        return "command string of some_command"
+    """
+    def wrapper(self, *args, **kwargs):
+        with self.sem:
+            command = func(self, *args, **kwargs)
+            LOG.info("%s(%d) command executed: %s", self.type, self.id,
+                     command)
+            data = spp_ctl.Controller._send_command(self.conn, command)
+            if data is None:
+                raise RuntimeError("%s(%d): %s: no-data returned" %
+                                   (self.type, self.id, command))
+            LOG.debug("reply: %s", data)
+            return self._decode_reply(data)
+    return wrapper
+
+
+class SppProc(object):
+    def __init__(self, proc_type, id, conn):
+        self.id = id
+        self.type = proc_type
+        # NOTE: executing command is serialized by using a semaphore
+        # for each process.
+        self.sem = eventlet.semaphore.Semaphore(value=1)
+        self.conn = conn
+
+
+class VfProc(SppProc):
+
+    def __init__(self, id, conn):
+        super(VfProc, self).__init__(TYPE_VF, id, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        data = json.loads(data)
+        result = data["results"][0]
+        if result["result"] == "error":
+            msg = result["error_details"]["message"]
+            raise bottle.HTTPError(400, "command error: %s" % msg)
+        return data
+
+    @staticmethod
+    def _decode_client_id(data):
+        try:
+            data = VfProc._decode_reply(data)
+            return data["client_id"]
+        except:
+            return None
+
+    @exec_command
+    def get_status(self):
+        return "status"
+
+    @exec_command
+    def start_component(self, comp_name, core_id, comp_type):
+        return ("component start {comp_name} {core_id} {comp_type}"
+                .format(**locals()))
+
+    @exec_command
+    def stop_component(self, comp_name):
+        return "component stop {comp_name}".format(**locals())
+
+    @exec_command
+    def port_add(self, port, direction, comp_name, op, vlan_id, pcp):
+        command = "port add {port} {direction} {comp_name}".format(**locals())
+        if op != "none":
+            command += " %s" % op
+            if op == "add_vlantag":
+                command += " %d %d" % (vlan_id, pcp)
+        return command
+
+    @exec_command
+    def port_del(self, port, direction, comp_name):
+        return "port del {port} {direction} {comp_name}".format(**locals())
+
+    @exec_command
+    def set_classifier_table(self, mac_address, port):
+        return ("classifier_table add mac {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def clear_classifier_table(self, mac_address, port):
+        return ("classifier_table del mac {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def set_classifier_table_with_vlan(self, mac_address, port,
+                                       vlan_id):
+        return ("classifier_table add vlan {vlan_id} {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def clear_classifier_table_with_vlan(self, mac_address, port,
+                                         vlan_id):
+        return ("classifier_table del vlan {vlan_id} {mac_address} {port}"
+                .format(**locals()))
+
+
+class NfvProc(SppProc):
+
+    def __init__(self, id, conn):
+        super(NfvProc, self).__init__(TYPE_NFV, id, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        return data.strip('\0')
+
+    @staticmethod
+    def _decode_client_id(data):
+        try:
+            return int(NfvProc._decode_reply(data))
+        except:
+            return None
+
+    @exec_command
+    def get_status(self):
+        return "status"
+
+    @exec_command
+    def port_add(self, if_type, if_num):
+        return "add {if_type} {if_num}".format(**locals())
+
+    @exec_command
+    def port_del(self, if_type, if_num):
+        return "del {if_type} {if_num}".format(**locals())
+
+    @exec_command
+    def patch_add(self, src_port, dst_port):
+        return "patch {src_port} {dst_port}".format(**locals())
+
+    @exec_command
+    def patch_reset(self):
+        return "patch reset"
+
+    @exec_command
+    def forward(self):
+        return "forward"
+
+    @exec_command
+    def stop(self):
+        return "stop"
+
+
+class PrimaryProc(SppProc):
+
+    def __init__(self, conn):
+        super(PrimaryProc, self).__init__(TYPE_PRIMARY, ID_PRIMARY, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        return data.strip('\0')
+
+    @exec_command
+    def status(self):
+        return "status"
+
+    @exec_command
+    def clear(self):
+        return "clear"
diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
new file mode 100644
index 0000000..3ee7893
--- /dev/null
+++ b/src/spp-ctl/spp_webapi.py
@@ -0,0 +1,440 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import bottle
+import errno
+import json
+import logging
+import netaddr
+import re
+import socket
+import subprocess
+import sys
+
+import spp_proc
+
+
+LOG = logging.getLogger(__name__)
+
+
+class KeyRequired(bottle.HTTPError):
+
+    def __init__(self, key):
+        msg = "key(%s) required." % key
+        super(KeyRequired, self).__init__(400, msg)
+
+
+class KeyInvalid(bottle.HTTPError):
+
+    def __init__(self, key, value):
+        msg = "invalid key(%s): %s." % (key, value)
+        super(KeyRequired, self).__init__(400, msg)
+
+
+class BaseHandler(bottle.Bottle):
+    """Define common methods for each handler."""
+
+    def __init__(self, controller):
+        super(BaseHandler, self).__init__()
+        self.ctrl = controller
+
+        self.default_error_handler = self._error_handler
+        bottle.response.default_status = 404
+
+    def _error_handler(self, res):
+        # use "text/plain" as content_type rather than bottle's default
+        # "html".
+        res.content_type = "text/plain"
+        return res.body
+
+    def _validate_port(self, port):
+        try:
+            if_type, if_num = port.split(":")
+            if if_type not in ["phy", "vhost", "ring"]:
+                raise
+            int(if_num)
+        except:
+            raise KeyInvalid('port', port)
+
+    def log_url(self):
+        LOG.info("%s %s called", bottle.request.method, bottle.request.path)
+
+    def log_response(self):
+        LOG.info("response: %s", bottle.response.status)
+
+    # following three decorators do common works for each API.
+    # each handler 'install' appropriate decorators.
+    #
+    def get_body(self, func):
+        """Get body and set it to method argument.
+        content-type is OK whether application/json or plain text.
+        """
+        def wrapper(*args, **kwargs):
+            req = bottle.request
+            if req.method in ["POST", "PUT"]:
+                if req.get_header('Content-Type') == "application/json":
+                    body = req.json
+                else:
+                    body = json.loads(req.body.read().decode())
+                kwargs['body'] = body
+                LOG.info("body: %s", body)
+            return func(*args, **kwargs)
+        return wrapper
+
+    def check_sec_id(self, func):
+        """Get and check proc and set it to method argument."""
+        def wrapper(*args, **kwargs):
+            sec_id = kwargs.pop('sec_id', None)
+            if sec_id is not None:
+                proc = self.ctrl.procs.get(sec_id)
+                if proc is None or proc.type != self.type:
+                    raise bottle.HTTPError(404,
+                                           "sec_id %d not found." % sec_id)
+                kwargs['proc'] = proc
+            return func(*args, **kwargs)
+        return wrapper
+
+    def make_response(self, func):
+        """Convert plain response to bottle.HTTPResponse."""
+        def wrapper(*args, **kwargs):
+            ret = func(*args, **kwargs)
+            if ret is None:
+                return bottle.HTTPResponse(status=204)
+            else:
+                r = bottle.HTTPResponse(status=200, body=json.dumps(ret))
+                r.content_type = "application/json"
+                return r
+        return wrapper
+
+
+class WebServer(BaseHandler):
+    """Top level handler.
+    handlers are hierarchized using 'mount' as follows:
+    /          WebServer
+    /v1          V1Handler
+       /vfs        V1VFHandler
+       /nfvs       V1NFVHandler
+       /primary    V1PrimaryHandler
+    """
+
+    def __init__(self, controller, api_port):
+        super(WebServer, self).__init__(controller)
+        self.api_port = api_port
+
+        self.mount("/v1", V1Handler(controller))
+
+        # request and response logging.
+        self.add_hook("before_request", self.log_url)
+        self.add_hook("after_request", self.log_response)
+
+    def start(self):
+        self.run(server='eventlet', host='localhost', port=self.api_port,
+                 quiet=True)
+
+
+class V1Handler(BaseHandler):
+    def __init__(self, controller):
+        super(V1Handler, self).__init__(controller)
+
+        self.set_route()
+
+        self.mount("/vfs", V1VFHandler(controller))
+        self.mount("/nfvs", V1NFVHandler(controller))
+        self.mount("/primary", V1PrimaryHandler(controller))
+
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/processes', 'GET', callback=self.get_processes)
+
+    def get_processes(self):
+        LOG.info("get processes called.")
+        return self.ctrl.get_processes()
+
+
+class V1VFHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1VFHandler, self).__init__(controller)
+        self.type = spp_proc.TYPE_VF
+
+        self.set_route()
+
+        self.install(self.check_sec_id)
+        self.install(self.get_body)
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/<sec_id:int>', 'GET', callback=self.vf_get)
+        self.route('/<sec_id:int>/components', 'POST',
+                   callback=self.vf_comp_start)
+        self.route('/<sec_id:int>/components/<name>', 'DELETE',
+                   callback=self.vf_comp_stop)
+        self.route('/<sec_id:int>/components/<name>', 'PUT',
+                   callback=self.vf_comp_port)
+        self.route('/<sec_id:int>/classifier_table', 'PUT',
+                   callback=self.vf_classifier)
+
+    def convert_vf_info(self, data):
+        info = data["info"]
+        vf = {}
+        vf["client-id"] = info["client-id"]
+        vf["ports"] = []
+        for key in ["phy", "vhost", "ring"]:
+            for idx in info[key]:
+                vf["ports"].append(key + ":" + str(idx))
+        vf["components"] = info["core"]
+        vf["classifier_table"] = info["classifier_table"]
+
+        return vf
+
+    def vf_get(self, proc):
+        return self.convert_vf_info(proc.get_status())
+
+    def _validate_vf_comp_start(self, body):
+        for key in ['name', 'core', 'type']:
+            if key not in body:
+                raise KeyRequired(key)
+        if not isinstance(body['name'], str):
+            raise KeyInvalid('name', body['name'])
+        if not isinstance(body['core'], int):
+            raise KeyInvalid('core', body['core'])
+        if body['type'] not in ["forward", "merge", "classifier_mac"]:
+            raise KeyInvalid('type', body['type'])
+
+    def vf_comp_start(self, proc, body):
+        self._validate_vf_comp_start(body)
+        proc.start_component(body['name'], body['core'], body['type'])
+
+    def vf_comp_stop(self, proc, name):
+        proc.stop_component(name)
+
+    def _validate_vf_comp_port(self, body):
+        for key in ['action', 'port', 'dir']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["attach", "detach"]:
+            raise KeyInvalid('action', body['action'])
+        if body['dir'] not in ["rx", "tx"]:
+            raise KeyInvalid('dir', body['dir'])
+        self._validate_port(body['port'])
+
+        if body['action'] == "attach":
+            vlan = body.get('vlan')
+            if vlan:
+                try:
+                    if vlan['operation'] not in ["none", "add", "del"]:
+                        raise
+                    if vlan['operation'] == "add":
+                        int(vlan['id'])
+                        int(vlan['pcp'])
+                except:
+                    raise KeyInvalid('vlan', vlan)
+
+    def vf_comp_port(self, proc, name, body):
+        self._validate_vf_comp_port(body)
+
+        if body['action'] == "attach":
+            op = "none"
+            vlan_id = 0
+            pcp = 0
+            vlan = body.get('vlan')
+            if vlan:
+                if vlan['operation'] == "add":
+                    op = "add_vlantag"
+                    vlan_id = vlan['id']
+                    pcp = vlan['pcp']
+                elif vlan['operation'] == "del":
+                    op = "del_vlantag"
+            proc.port_add(body['port'], body['dir'],
+                          name, op, vlan_id, pcp)
+        else:
+            proc.port_del(body['port'], body['dir'], name)
+
+    def _validate_mac(self, mac_address):
+        try:
+            netaddr.EUI(mac_address)
+        except:
+            raise KeyInvalid('mac_address', mac_address)
+
+    def _validate_vf_classifier(self, body):
+        for key in ['action', 'type', 'port', 'mac_address']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["add", "del"]:
+            raise KeyInvalid('action', body['action'])
+        if body['type'] not in ["mac", "vlan"]:
+            raise KeyInvalid('type', body['type'])
+        self._validate_port(body['port'])
+        self._validate_mac(body['mac_address'])
+
+        if body['type'] == "vlan":
+            try:
+                int(body['vlan'])
+            except:
+                raise KeyInvalid('vlan', body.get('vlan'))
+
+    def vf_classifier(self, proc, body):
+        self._validate_vf_classifier(body)
+
+        port = body['port']
+        mac_address = body['mac_address']
+
+        if body['action'] == "add":
+            if body['type'] == "mac":
+                proc.set_classifier_table(mac_address, port)
+            else:
+                proc.set_classifier_table_with_vlan(
+                    mac_address, port, body['vlan'])
+        else:
+            if body['type'] == "mac":
+                proc.clear_classifier_table(mac_address, port)
+            else:
+                proc.clear_classifier_table_with_vlan(
+                    mac_address, port, body['vlan'])
+
+
+class V1NFVHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1NFVHandler, self).__init__(controller)
+        self.type = spp_proc.TYPE_NFV
+
+        self.set_route()
+
+        self.install(self.check_sec_id)
+        self.install(self.get_body)
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/<sec_id:int>', 'GET', callback=self.nfv_get)
+        self.route('/<sec_id:int>/forward', 'PUT',
+                   callback=self.nfv_forward)
+        self.route('/<sec_id:int>/ports', 'PUT',
+                   callback=self.nfv_port)
+        self.route('/<sec_id:int>/patches', 'PUT',
+                   callback=self.nfv_patch_add)
+        self.route('/<sec_id:int>/patches', 'DELETE',
+                   callback=self.nfv_patch_del)
+
+    def convert_nfv_info(self, data):
+        nfv = {}
+        lines = data.splitlines()
+        if len(lines) < 3:
+            return {}
+        p = re.compile("Client ID (\d+) (\w+)")
+        m = p.match(lines[0])
+        if m:
+            nfv['client_id'] = int(m.group(1))
+            nfv['status'] = m.group(2)
+
+        ports = {}
+        outs = []
+        for line in lines[2:]:
+            if not line.startswith("port_id"):
+                break
+            arg1, _, arg2, arg3 = line.split(",")
+            _, port_id = arg1.split(":")
+            if arg2 == "PHY":
+                port = "phy:" + port_id
+            else:
+                if_type, rest = arg2.split("(")
+                if_num = rest.rstrip(")")
+                if if_type == "RING":
+                    port = "ring:" + if_num
+                elif if_type == "VHOST":
+                    port = "vhost:" + if_num
+                else:
+                    port = if_type + ":" + if_num
+            ports[port_id] = port
+            _, out_id = arg3.split(":")
+            if out_id != "none":
+                outs.append((port_id, out_id))
+        nfv['ports'] = list(ports.values())
+        patches = []
+        if outs:
+            for src_id, dst_id in outs:
+                patches.append({"src": ports[src_id], "dst": ports[dst_id]})
+        nfv['patches'] = patches
+
+        return nfv
+
+    def nfv_get(self, proc):
+        return self.convert_nfv_info(proc.get_status())
+
+    def _validate_nfv_forward(self, body):
+        if 'action' not in body:
+            raise KeyRequired('action')
+        if body['action'] not in ["start", "stop"]:
+            raise KeyInvalid('action', body['action'])
+
+    def nfv_forward(self, proc, body):
+        if body['action'] == "start":
+            proc.forward()
+        else:
+            proc.stop()
+
+    def _validate_nfv_port(self, body):
+        for key in ['action', 'port']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["add", "del"]:
+            raise KeyInvalid('action', body['action'])
+        self._validate_port(body['port'])
+
+    def nfv_port(self, proc, body):
+        self._validate_nfv_port(body)
+
+        if_type, if_num = body['port'].split(":")
+        if body['action'] == "add":
+            proc.port_add(if_type, if_num)
+        else:
+            proc.port_del(if_type, if_num)
+
+    def _validate_nfv_patch(self, body):
+        for key in ['src', 'dst']:
+            if key not in body:
+                raise KeyRequired(key)
+        self._validate_port(body['src'])
+        self._validate_port(body['dst'])
+
+    def nfv_patch_add(self, proc, body):
+        self._validate_nfv_patch(body)
+        proc.patch_add(body['src'], body['dst'])
+
+    def nfv_patch_del(self, proc):
+        proc.patch_reset()
+
+
+class V1PrimaryHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1PrimaryHandler, self).__init__(controller)
+
+        self.set_route()
+
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/status', 'GET', callback=self.get_status)
+        self.route('/status', 'DELETE', callback=self.clear_status)
+
+    def _get_proc(self):
+        proc = self.ctrl.procs.get(spp_proc.ID_PRIMARY)
+        if proc is None:
+            raise bottle.HTTPError(404, "primary not found.")
+        return proc
+
+    def convert_status(self, data):
+        # no data returned at the moment.
+        # some data will be returned when the primary becomes to
+        # return statistical information.
+        return {}
+
+    def get_status(self):
+        proc = self._get_proc()
+        return self.convert_status(proc.status())
+
+    def clear_status(self):
+        proc = self._get_proc()
+        proc.clear()
-- 
2.17.0
-- 
Itsuro ODA <oda@valinux.co.jp>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [spp] [PATCH] spp-ctl: SPP controller with Web API
  2018-09-12 23:25 [spp] [PATCH] spp-ctl: SPP controller with Web API Itsuro ODA
@ 2018-09-18 10:00 ` Yasufumi Ogawa
  2018-09-18 21:40   ` Itsuro ODA
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
  2 siblings, 1 reply; 33+ messages in thread
From: Yasufumi Ogawa @ 2018-09-18 10:00 UTC (permalink / raw)
  To: Itsuro ODA, spp

> spp-ctl is a SPP controller with a REST like web API.
> 
> spp-ctl maintains the connections from the SPP processes and at
> the same time exposes the API for the user to request for the
> SPP processes.
> 
> Background and motivation:
> 
> Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
> Therefore, spp-agent, a component of networking-spp which make
> SPP available on OpenStack environment, implements SPP controller
> in itself. (see. https://github.com/openstack/networking-spp )
> 
> Either CLI or spp-agent, there is a problem that other people can
> not request to SPP processes while using. spp-ctl is invented to
> solve this problem.
> 
> Both CLI and spp-agent can be used spp-ctl to request SPP
> processes instead of owning contoroller itself. In that case,
> multiple people can request to SPP processes at the same time.
> Note that spp-agent has a plan to change to use spp-ctl.
> It is also available not using CLI but requesting spp-ctl
> directly.
Hi Itsuro,

Thanks a lot for your proposal and contribution! It looks well defined APIs for existing SPP commands. I think it is also a good 
idea to fill the gap between usages of CLI and OpenStack.

For considering our conventions, I would like to ask you to revise your patch for the reasons described below.

* Patch should be divided into several patches for each of files if you add newly created files because it is difficult to 
understand the status of review if it is not divided.

* All of documentation should be contained in `docs/guides`. Your proposal is for adding new entity and it need to create a new 
sub-directory. I think `docs/guides/spp-ctl` is appropriate.

* `index.rst` is required in `docs/guides/spp-ctl`.

* It is better to rename `README.rst` to `overview.rst` for the convention.

* `requirements.txt` should not be included in `src` directory. Do you think to move it to the project root?

Thanks,
Yasufumi
> 
> Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
> ---
>   src/spp-ctl/README.rst        | 102 +++++
>   src/spp-ctl/api-reference.rst | 790 ++++++++++++++++++++++++++++++++++
>   src/spp-ctl/requirements.txt  |   3 +
>   src/spp-ctl/spp-ctl           |  11 +
>   src/spp-ctl/spp_ctl.py        | 158 +++++++
>   src/spp-ctl/spp_proc.py       | 184 ++++++++
>   src/spp-ctl/spp_webapi.py     | 440 +++++++++++++++++++
>   7 files changed, 1688 insertions(+)
>   create mode 100644 src/spp-ctl/README.rst
>   create mode 100644 src/spp-ctl/api-reference.rst
>   create mode 100644 src/spp-ctl/requirements.txt
>   create mode 100755 src/spp-ctl/spp-ctl
>   create mode 100644 src/spp-ctl/spp_ctl.py
>   create mode 100644 src/spp-ctl/spp_proc.py
>   create mode 100644 src/spp-ctl/spp_webapi.py
> 
> diff --git a/src/spp-ctl/README.rst b/src/spp-ctl/README.rst
> new file mode 100644
> index 0000000..847b9dc
> --- /dev/null
> +++ b/src/spp-ctl/README.rst
> @@ -0,0 +1,102 @@
> +====================================
> +spp-ctl: SPP controller with Web API
> +====================================
> +
> +Overview
> +========
> +
> +spp-ctl is a SPP controller with a REST like web API.
> +
> +spp-ctl maintains the connections from the SPP processes and at the same
> +time exposes the API for the user to request for the SPP processes.
> +
> +Background and motivation
> +-------------------------
> +
> +Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
> +Therefore, spp-agent, a component of networking-spp which make SPP
> +available on OpenStack environment, implements SPP controller in
> +itself. (see. https://github.com/openstack/networking-spp )
> +
> +Either CLI or spp-agent, there is a problem that other people can not
> +request to SPP processes while using. spp-ctl is invented to solve this
> +problem.
> +
> +Both CLI and spp-agent can be used spp-ctl to request SPP processes
> +instead of owning contoroller itself. In that case, multiple people
> +can request to SPP processes at the same time.
> +Note that spp-agent has a plan to change to use spp-ctl.
> +It is also available not using CLI but requesting spp-ctl directly.
> +
> +Architecture
> +------------
> +
> +The design goal of spp-ctl is to be as simple as possible.
> +It is stateless. Basically, spp-ctl only converts API requests into
> +commands of SPP processes and throws request, thouth it does syntax and
> +lexical check for API requests.
> +
> +spp-ctl adopts bottle (it is simple and well known) as a web framework
> +and eventlet for parallel processing. spp-ctl can process multiple APIs
> +at the same time, however, requests for per SPP process are serialized
> +internally.
> +
> +
> +Setup
> +=====
> +
> +spp-ctl is a simple program written in python3. Installation of related
> +packages is as follows (assume ubuntu).
> +
> +::
> +
> +  $ sudo apt update
> +  $ sudo apt install python3
> +  $ sudo apt install python3-pip
> +  $ sudo pip3 install -r requirements.txt
> +
> +Usage
> +-----
> +
> +::
> +
> +  usage: spp-ctl [-p PRI_PORT] [-s SEC_PORT] [-a API_PORT]
> +
> +  optional arguments:
> +    -p PRI_PORT  primary port. default is 5555.
> +    -s SEC_PORT  secondary port. default is 6666.
> +    -a API_PORT  web api port. default is 7777.
> +
> +Using systemd
> +-------------
> +
> +Although spp-ctl runs as a daemon process normaly, it assumes to the
> +use of systemd and does not daemonize itself.
> +
> +The service file for systemd is simple as shown below::
> +
> +  [Unit]
> +  Description = SPP Controller
> +
> +  [Service]
> +  ExecStart = {SPP install path}/spp_ctl/spp-ctl -p 5555 -s 6666 -a 7777
> +  User = root
> +
> +API Usage
> +=========
> +
> +For API details, see API-reference_.
> +
> +.. _API-reference: ./api-reference.rst
> +
> +Since spp-ctl provides the web API, for example, you can use curl to execute
> +requests as follows::
> +
> +  $ curl http://localhost:7777/v1/processes
> +  [{"type": "primary"}, {"client-id": 1, "type": "vf"}, {"client-id": 2, "type": "vf"}]
> +  $ curl http://localhost:7777/v1/vfs/1
> +  ... snip
> +  $ curl -X POST http://localhost:7777/v1/vfs/1/components \
> +    -d '{"core": 2, "name": "forward_0_tx", "type": "forward"}'
> +  $
> +
> diff --git a/src/spp-ctl/api-reference.rst b/src/spp-ctl/api-reference.rst
> new file mode 100644
> index 0000000..b8bf488
> --- /dev/null
> +++ b/src/spp-ctl/api-reference.rst
> @@ -0,0 +1,790 @@
> +=============
> +API Reference
> +=============
> +
> +Overview
> +========
> +
> +Spp-ctl provides simple REST like API. It supports http only, not https.
> +
> +Request and Response
> +--------------------
> +
> +Request body is json format. It is accepted both "text/plain" and "application/json" for the content-type header.
> +
> +Response of GET is json format and the content-type is "application/json" if the request success.
> +
> +If a request fails, the response is a text which shows error reason and the content-type is "text/plain".
> +
> +Error code
> +----------
> +
> +Spp-ctl does basic syntax and lexical check of a request.
> +
> ++-------+------------------------------------------------------------------------------+
> +| Error | Description                                                                  |
> ++=======+==============================================================================+
> +| 400   | Syntax or lexical error of a request.                                        |
> +|       | Or an SPP process returns error for the command correspondings to a request. |
> ++-------+------------------------------------------------------------------------------+
> +| 404   | URL is not supported for spp-ctl.                                            |
> +|       | Or there is no SPP process of client-id in a URL.                            |
> ++-------+------------------------------------------------------------------------------+
> +| 500   | A system error occurs in the spp-ctl.                                        |
> +|       | ex. communication error between an SPP processes.                            |
> ++-------+------------------------------------------------------------------------------+
> +
> +
> +API independent of the process type
> +===================================
> +
> +GET /v1/processes
> +-----------------
> +
> +Show the SPP processes connected to the spp-ctl.
> +
> +Normarl response codes: 200
> +
> +Response
> +^^^^^^^^
> +
> +An array of process objects.
> +
> +process object:
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| type      | string  | process type. one of "primary", "vf" or "nfv".              |
> ++-----------+---------+-------------------------------------------------------------+
> +| client-id | integer | client id. if type is "primary" this member does not exist. |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Response example
> +^^^^^^^^^^^^^^^^
> +
> +::
> +
> +[{"type": "primary"}, {"type": "vf", "client-id": 1}, {"type": "nfv", "client-id": 2}]
> +
> +
> +API for spp_vf
> +==============
> +
> +GET /v1/vfs/{client_id}
> +-----------------------
> +
> +Get the information of the spp_vf process.
> +
> +Normal response codes: 200
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Response
> +^^^^^^^^
> +
> ++------------------+---------+-------------------------------------------------------------+
> +| Name             | Type    | Description                                                 |
> ++==================+=========+=============================================================+
> +| client-id        | integer | client id.                                                  |
> ++------------------+---------+-------------------------------------------------------------+
> +| ports            | array   | an array of port ids used by the process.                   |
> ++------------------+---------+-------------------------------------------------------------+
> +| components       | array   | an array of component objects in the process.               |
> ++------------------+---------+-------------------------------------------------------------+
> +| classifier_table | array   | an array of classifier tables in the process.               |
> ++------------------+---------+-------------------------------------------------------------+
> +
> +component object:
> +
> ++---------+---------+---------------------------------------------------------------------+
> +| Name    | Type    | Description                                                         |
> ++=========+=========+=====================================================================+
> +| core    | integer | core id running on the component                                    |
> ++---------+---------+---------------------------------------------------------------------+
> +| name    | string  | an array of port ids used by the process.                           |
> ++---------+---------+---------------------------------------------------------------------+
> +| type    | string  | an array of component objects in the process.                       |
> ++---------+---------+---------------------------------------------------------------------+
> +| rx_port | array   | an array of port objects connected to the rx side of the component. |
> ++---------+---------+---------------------------------------------------------------------+
> +| tx_port | array   | an array of port objects connected to the tx side of the component. |
> ++---------+---------+---------------------------------------------------------------------+
> +
> +port object:
> +
> ++---------+---------+---------------------------------------------------------------------+
> +| Name    | Type    | Description                                                         |
> ++=========+=========+=====================================================================+
> +| port    | string  | port id. port id is the form {interface_type}:{interface_id}.       |
> ++---------+---------+---------------------------------------------------------------------+
> +| vlan    | object  | vlan operation which is applied to the port.                        |
> ++---------+---------+---------------------------------------------------------------------+
> +
> +vlan object:
> +
> ++-----------+---------+-------------------------------------------------------------------+
> +| Name      | Type    | Description                                                       |
> ++===========+=========+===================================================================+
> +| operation | string  | "add", "del" or "none".                                           |
> ++-----------+---------+-------------------------------------------------------------------+
> +| id        | integer | vlan id.                                                          |
> ++-----------+---------+-------------------------------------------------------------------+
> +| pcp       | integer | vlan pcp.                                                         |
> ++-----------+---------+-------------------------------------------------------------------+
> +
> +classifier table:
> +
> ++-----------+---------+----------------------------------------------------------------------+
> +| Name      | Type    | Description                                                          |
> ++===========+=========+======================================================================+
> +| type      | string  | "mac" or "vlan".                                                     |
> ++-----------+---------+----------------------------------------------------------------------+
> +| value     | string  | "mac address" for "mac" type. "vlan id/mac address" for "vlan" type. |
> ++-----------+---------+----------------------------------------------------------------------+
> +| port      | string  | port id applied to classify.                                         |
> ++-----------+---------+----------------------------------------------------------------------+
> +
> +Response example
> +^^^^^^^^^^^^^^^^
> +
> +::
> +
> +{
> +"client-id": 1,
> +"ports": ["phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"],
> +"components": [
> +{
> +"core": 2,
> +"name": "forward_0_tx",
> +"type": "forward",
> +"rx_port": [
> +{
> +"port": "ring:0",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +],
> +"tx_port": [
> +{
> +"port": "vhost:0",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +]
> +},
> +{
> +"core": 3,
> +"type": "unuse"
> +},
> +{
> +"core": 4,
> +"type": "unuse"
> +},
> +{
> +"core": 5,
> +"name": "forward_1_rx",
> +"type": "forward",
> +"rx_port": [
> +{
> +"port": "vhost:1",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +],
> +"tx_port": [
> +{
> +"port": "ring:3",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +]
> +},
> +{
> +"core": 6,
> +"name": "classifier",
> +"type": "classifier_mac",
> +"rx_port": [
> +{
> +"port": "phy:0",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +],
> +"tx_port": [
> +{
> +"port": "ring:0",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +},
> +{
> +"port": "ring:2",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +]
> +},
> +{
> +"core": 7,
> +"name": "merger",
> +"type": "merge",
> +"rx_port": [
> +{
> +"port": "ring:1",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +},
> +{
> +"port": "ring:3",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +],
> +"tx_port": [
> +{
> +"port": "phy:0",
> +"vlan": { "operation": "none", "id": 0, "pcp": 0 }
> +}
> +]
> +}
> +},
> +"classifier_table": [
> +{
> +"type": "mac",
> +"value": "FA:16:3E:7D:CC:35",
> +"port": "ring:0"
> +}
> +]
> +}
> +
> +Note: The component which type is "unused" is to indicate unused core.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};status
> +
> +
> +POST /v1/vfs/{client_id}/components
> +-----------------------------------
> +
> +Start the component.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +
> +Request(body)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+----------------------------------------------------------------+
> +| Name      | Type    | Description                                                    |
> ++===========+=========+================================================================+
> +| name      | string  | component name. must be unique in the process.                 |
> ++-----------+---------+----------------------------------------------------------------+
> +| core      | integer | core id.                                                       |
> ++-----------+---------+----------------------------------------------------------------+
> +| type      | string  | component type. one of "forward", "merge" or "classifier_mac". |
> ++-----------+---------+----------------------------------------------------------------+
> +
> +Request example
> +^^^^^^^^^^^^^^^
> +::
> +
> +{"name": "forwarder1", "core": 12, "type": "forward"}
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful POST request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};component start {name} {core} {type}
> +
> +
> +DELETE /v1/vfs/{sec id}/components/{name}
> +-----------------------------------------
> +
> +Stop the component.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +| name      | string  | component name.                                             |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful POST request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};component stop {name}
> +
> +
> +PUT /v1/vfs/{client_id}/components/{name}/ports
> +-----------------------------------------------
> +
> +Add or Delete port to the component.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +| name      | string  | component name.                                             |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Request(body)
> +^^^^^^^^^^^^^
> +
> ++---------+---------+---------------------------------------------------------------------+
> +| Name    | Type    | Description                                                         |
> ++=========+=========+=====================================================================+
> +| action  | string  | "attach" or "detach".                                               |
> ++---------+---------+---------------------------------------------------------------------+
> +| port    | string  | port id. port id is the form {interface_type}:{interface_id}.       |
> ++---------+---------+---------------------------------------------------------------------+
> +| dir     | string  | "rx" or "tx".                                                       |
> ++---------+---------+---------------------------------------------------------------------+
> +| vlan    | object  | vlan operation which is applied to the port. it can be omitted.     |
> ++---------+---------+---------------------------------------------------------------------+
> +
> +vlan object:
> +
> ++-----------+---------+-------------------------------------------------------------------+
> +| Name      | Type    | Description                                                       |
> ++===========+=========+===================================================================+
> +| operation | string  | "add", "del" or "none".                                           |
> ++-----------+---------+-------------------------------------------------------------------+
> +| id        | integer | vlan id. ignored when operation is "del" or "none".               |
> ++-----------+---------+-------------------------------------------------------------------+
> +| pcp       | integer | vlan pcp. ignored when operation is "del" or "none".              |
> ++-----------+---------+-------------------------------------------------------------------+
> +
> +Request example
> +^^^^^^^^^^^^^^^
> +
> +::
> +
> +{"action": "attach", "port": "vhost:1", "dir": "rx",
> +"vlan": {"operation": "add", "id": 677, "pcp": 0}
> +}
> +
> +::
> +
> +{"action": "detach", "port": "vhost:0", "dir": "tx"}
> +
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful PUT request.
> +
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +action is "attach"
> +
> +::
> +
> +sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
> +
> +action is "detach"
> +
> +::
> +
> +sec {client_id};port del {port} {dir} {name}
> +
> +
> +PUT /v1/vfs/{sec id}/classifier_table
> +-------------------------------------
> +
> +Set or Unset classifier table.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Request(body)
> +^^^^^^^^^^^^^
> +
> ++-------------+-----------------+-------------------------------------------------------------------+
> +| Name        | Type            | Description                                                       |
> ++=============+=================+===================================================================+
> +| action      | string          | "add" or "del".                                                   |
> ++-------------+-----------------+-------------------------------------------------------------------+
> +| type        | string          | "mac" or "vlan".                                                  |
> ++-------------+-----------------+-------------------------------------------------------------------+
> +| vlan        | integer or null | vlan id when type is "vlan. null or omitted when type is "mac".   |
> ++-------------+-----------------+-------------------------------------------------------------------+
> +| mac_address | string          | mac address.                                                      |
> ++-------------+-----------------+-------------------------------------------------------------------+
> +| port        | string          | port id.                                                          |
> ++-------------+-----------------+-------------------------------------------------------------------+
> +
> +Request example
> +^^^^^^^^^^^^^^^
> +
> +::
> +
> +{"action": "add", "type": "mac",
> +"mac_address": "FA:16:3E:7D:CC:35", "port": "ring:0"
> +}
> +
> +::
> +
> +{"action": "del", "type": "vlan", "vlan": 475,
> +"mac_address": "FA:16:3E:7D:CC:35", "port": "ring:0"
> +}
> +
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful PUT request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +type is "mac"
> +
> +::
> +
> +classifier_table {action} mac {mac_address} {port}
> +
> +type is "vlan"
> +
> +::
> +
> +classifier_table {action} vlan {vlan} {mac_address} {port}
> +
> +
> +API for spp_nfv/spp_vm
> +======================
> +
> +GET /v1/nfvs/{client_id}
> +------------------------
> +
> +Get the information of the spp_nfv/spp_vm process.
> +
> +Normal response codes: 200
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Response
> +^^^^^^^^
> +
> ++------------------+---------+-------------------------------------------------------------+
> +| Name             | Type    | Description                                                 |
> ++==================+=========+=============================================================+
> +| client-id        | integer | client id.                                                  |
> ++------------------+---------+-------------------------------------------------------------+
> +| status           | string  | "Running" or "Idle".                                        |
> ++------------------+---------+-------------------------------------------------------------+
> +| ports            | array   | an array of port ids used by the process.                   |
> ++------------------+---------+-------------------------------------------------------------+
> +| patches          | array   | an array of patches.                                        |
> ++------------------+---------+-------------------------------------------------------------+
> +
> +patch objest
> +
> ++----------+---------+-------------------------------------------------------------+
> +| Name     | Type    | Description                                                 |
> ++==========+=========+=============================================================+
> +| src      | string  | source port id.                                             |
> ++----------+---------+-------------------------------------------------------------+
> +| dst      | string  | destination port id.                                        |
> ++----------+---------+-------------------------------------------------------------+
> +
> +Response example
> +^^^^^^^^^^^^^^^^
> +
> +::
> +
> +{
> +"client-id": 1,
> +"status": "Running",
> +"ports": ["phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"],
> +"patches": [
> +{"src": "vhost:0", "dst": "ring:0"},
> +{"src": "ring:1", "dst": "vhost:1"}
> +]
> +}
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};status
> +
> +
> +PUT /v1/nfvs/{client_id}/forward
> +--------------------------------
> +
> +Start or Stop forwarding.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Request(body)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| action    | string  | "start" or "stop".                                          |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Request example
> +^^^^^^^^^^^^^^^
> +
> +::
> +
> +{"action": "start"}
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful PUT request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +action is "start"
> +
> +::
> +
> +sec {client_id};forward
> +
> +action is "stop"
> +
> +::
> +
> +sec {client_id};stop
> +
> +
> +PUT /v1/nfvs/{client_id}/ports
> +------------------------------
> +
> +Add or Delete port.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Request(body)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+---------------------------------------------------------------+
> +| Name      | Type    | Description                                                   |
> ++===========+=========+===============================================================+
> +| action    | string  | "add" or "del".                                               |
> ++-----------+---------+---------------------------------------------------------------+
> +| port      | string  | port id. port id is the form {interface_type}:{interface_id}. |
> ++-----------+---------+---------------------------------------------------------------+
> +
> +Request example
> +^^^^^^^^^^^^^^^
> +
> +::
> +
> +{"action": "add", "port": "vhost:0"}
> +
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful PUT request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};{action} {interface_type} {interface_id}
> +
> +
> +PUT /v1/nfvs/{client_id}/patches
> +--------------------------------
> +
> +Add a patch.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Request(body)
> +^^^^^^^^^^^^^
> +
> ++----------+---------+-------------------------------------------------------------+
> +| Name     | Type    | Description                                                 |
> ++==========+=========+=============================================================+
> +| src      | string  | source port id.                                             |
> ++----------+---------+-------------------------------------------------------------+
> +| dst      | string  | destination port id.                                        |
> ++----------+---------+-------------------------------------------------------------+
> +
> +Request example
> +^^^^^^^^^^^^^^^
> +
> +::
> +
> +{"src": "vhost:0", "dst": "ring:0"}
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful PUT request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};patch {src} {dst}
> +
> +
> +DELETE /v1/nfvs/{client_id}/patches
> +-----------------------------------
> +
> +Reset patches.
> +
> +Normal response codes: 204
> +
> +Error response codes: 400, 404
> +
> +Request(path)
> +^^^^^^^^^^^^^
> +
> ++-----------+---------+-------------------------------------------------------------+
> +| Name      | Type    | Description                                                 |
> ++===========+=========+=============================================================+
> +| client_id | integer | client id.                                                  |
> ++-----------+---------+-------------------------------------------------------------+
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful DELETE request.
> +
> +Equivalent CLI command
> +^^^^^^^^^^^^^^^^^^^^^^
> +
> +::
> +
> +sec {client_id};patch reset
> +
> +
> +API for spp_primary
> +===================
> +
> +GET /v1/primary/status
> +----------------------
> +
> +Show statistical information.
> +
> +Normal response codes: 200
> +
> +Response
> +^^^^^^^^
> +
> +There is no data at the moment. The statistical information will be returned when spp_primary
> +implements it.
> +
> +
> +DELETE /v1/primary/status
> +-------------------------
> +
> +Clear statistical information.
> +
> +Normal response codes: 204
> +
> +Response
> +^^^^^^^^
> +
> +There is no body content for the response of a successful PUT request.
> +
> diff --git a/src/spp-ctl/requirements.txt b/src/spp-ctl/requirements.txt
> new file mode 100644
> index 0000000..cc52d48
> --- /dev/null
> +++ b/src/spp-ctl/requirements.txt
> @@ -0,0 +1,3 @@
> +eventlet
> +bottle
> +netaddr
> diff --git a/src/spp-ctl/spp-ctl b/src/spp-ctl/spp-ctl
> new file mode 100755
> index 0000000..645611b
> --- /dev/null
> +++ b/src/spp-ctl/spp-ctl
> @@ -0,0 +1,11 @@
> +#!/usr/bin/python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
> +
> +import sys
> +
> +from spp_ctl import main
> +
> +
> +if __name__ == "__main__":
> +    sys.exit(main())
> diff --git a/src/spp-ctl/spp_ctl.py b/src/spp-ctl/spp_ctl.py
> new file mode 100644
> index 0000000..e168747
> --- /dev/null
> +++ b/src/spp-ctl/spp_ctl.py
> @@ -0,0 +1,158 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
> +
> +import eventlet
> +eventlet.monkey_patch()
> +
> +import argparse
> +import errno
> +import json
> +import logging
> +import socket
> +import subprocess
> +
> +import spp_proc
> +import spp_webapi
> +
> +
> +LOG = logging.getLogger(__name__)
> +
> +
> +MSG_SIZE = 4096
> +
> +
> +class Controller(object):
> +
> +    def __init__(self, pri_port, sec_port, api_port):
> +        self.web_server = spp_webapi.WebServer(self, api_port)
> +        self.procs = {}
> +        self.init_connection(pri_port, sec_port)
> +
> +    def start(self):
> +        self.web_server.start()
> +
> +    def init_connection(self, pri_port, sec_port):
> +        self.pri_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
> +        self.pri_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
> +        self.pri_sock.bind(('127.0.0.1', pri_port))
> +        self.pri_sock.listen(1)
> +        self.primary_listen_thread = eventlet.greenthread.spawn(
> +            self.accept_primary)
> +
> +        self.sec_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
> +        self.sec_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
> +        self.sec_sock.bind(('127.0.0.1', sec_port))
> +        self.sec_sock.listen(1)
> +        self.secondary_listen_thread = eventlet.greenthread.spawn(
> +            self.accept_secondary)
> +
> +    def accept_primary(self):
> +        while True:
> +            conn, _ = self.pri_sock.accept()
> +            proc = self.procs.get(spp_proc.ID_PRIMARY)
> +            if proc is not None:
> +                LOG.warning("spp_primary reconnect !")
> +                with proc.sem:
> +                    try:
> +                        proc.conn.close()
> +                    except Exception:
> +                        pass
> +                    proc.conn = conn
> +                # NOTE: when spp_primary restart, all secondarys must be
> +                # restarted. this is out of controle of spp-ctl.
> +            else:
> +                LOG.info("primary connected.")
> +                self.procs[spp_proc.ID_PRIMARY] = spp_proc.PrimaryProc(conn)
> +
> +    def accept_secondary(self):
> +        while True:
> +            conn, _ = self.sec_sock.accept()
> +            LOG.debug("sec accepted: get process id")
> +            proc = self._get_proc(conn)
> +            if proc is None:
> +                LOG.error("get process id failed")
> +                conn.close()
> +                continue
> +            old_proc = self.procs.get(proc.id)
> +            if old_proc:
> +                LOG.warning("%s(%d) reconnect !", old_proc.type, old_proc.id)
> +                if old_proc.type != proc.type:
> +                    LOG.warning("type changed ! new type: %s", proc.type)
> +                with old_proc.sem:
> +                    try:
> +                        old_proc.conn.close()
> +                    except Exception:
> +                        pass
> +            else:
> +                LOG.info("%s(%d) connected.", proc.type, proc.id)
> +            self.procs[proc.id] = proc
> +
> +    @staticmethod
> +    def _continue_recv(conn):
> +        try:
> +            # must set non-blocking to recieve remining data not to happen
> +            # blocking here.
> +            # NOTE: usually MSG_DONTWAIT flag is used for this purpose but
> +            # this flag is not supported under eventlet.
> +            conn.setblocking(False)
> +            data = b""
> +            while True:
> +                try:
> +                    rcv_data = conn.recv(MSG_SIZE)
> +                    data += rcv_data
> +                    if len(rcv_data) < MSG_SIZE:
> +                        break
> +                except socket.error as e:
> +                    if e.args[0] == errno.EAGAIN:
> +                        # OK, no data remining. this happens when recieve data
> +                        # length is just multiple of MSG_SIZE.
> +                        break
> +                    raise e
> +            return data
> +        finally:
> +            conn.setblocking(True)
> +
> +    @staticmethod
> +    def _send_command(conn, command):
> +        conn.sendall(command.encode())
> +        data = conn.recv(MSG_SIZE)
> +        if data and len(data) == MSG_SIZE:
> +            # could not receive data at once. recieve remining data.
> +            data += self._continue_recv(conn)
> +        if data:
> +            data = data.decode()
> +        return data
> +
> +    def _get_proc(self, conn):
> +        # it is a bit ad hoc. send "_get_clinet_id" command and try to
> +        # decode reply for each proc type. if success, that is the type.
> +        data = self._send_command(conn, "_get_client_id")
> +        for proc in [spp_proc.VfProc, spp_proc.NfvProc]:
> +            sec_id = proc._decode_client_id(data)
> +            if sec_id is not None:
> +                return proc(sec_id, conn)
> +
> +    def get_processes(self):
> +        procs = []
> +        for proc in self.procs.values():
> +            p = {"type": proc.type}
> +            if proc.id != spp_proc.ID_PRIMARY:
> +                p["client-id"] = proc.id
> +            procs.append(p)
> +        return procs
> +
> +
> +def main():
> +    parser = argparse.ArgumentParser(description="SPP Controller")
> +    parser.add_argument("-p", dest='pri_port', type=int, default=5555,
> +                        action='store', help="primary port")
> +    parser.add_argument("-s", dest='sec_port', type=int, default=6666,
> +                        action='store', help="secondary port")
> +    parser.add_argument("-a", dest='api_port', type=int, default=7777,
> +                        action='store', help="web api port")
> +    args = parser.parse_args()
> +
> +    logging.basicConfig(level=logging.DEBUG)
> +
> +    controller = Controller(args.pri_port, args.sec_port, args.api_port)
> +    controller.start()
> diff --git a/src/spp-ctl/spp_proc.py b/src/spp-ctl/spp_proc.py
> new file mode 100644
> index 0000000..929942a
> --- /dev/null
> +++ b/src/spp-ctl/spp_proc.py
> @@ -0,0 +1,184 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
> +
> +import bottle
> +import eventlet
> +import json
> +import logging
> +
> +import spp_ctl
> +
> +
> +LOG = logging.getLogger(__name__)
> +
> +ID_PRIMARY = 0
> +TYPE_PRIMARY = "primary"
> +TYPE_VF = "vf"
> +TYPE_NFV = "nfv"
> +
> +
> +def exec_command(func):
> +    """Define the common function of sending command & receiving reply
> +    as a decorator.
> +    each method for executing command has only to return command string.
> +    ex.
> +    @exec_command
> +    def some_command(self, ...):
> +        return "command string of some_command"
> +    """
> +    def wrapper(self, *args, **kwargs):
> +        with self.sem:
> +            command = func(self, *args, **kwargs)
> +            LOG.info("%s(%d) command executed: %s", self.type, self.id,
> +                     command)
> +            data = spp_ctl.Controller._send_command(self.conn, command)
> +            if data is None:
> +                raise RuntimeError("%s(%d): %s: no-data returned" %
> +                                   (self.type, self.id, command))
> +            LOG.debug("reply: %s", data)
> +            return self._decode_reply(data)
> +    return wrapper
> +
> +
> +class SppProc(object):
> +    def __init__(self, proc_type, id, conn):
> +        self.id = id
> +        self.type = proc_type
> +        # NOTE: executing command is serialized by using a semaphore
> +        # for each process.
> +        self.sem = eventlet.semaphore.Semaphore(value=1)
> +        self.conn = conn
> +
> +
> +class VfProc(SppProc):
> +
> +    def __init__(self, id, conn):
> +        super(VfProc, self).__init__(TYPE_VF, id, conn)
> +
> +    @staticmethod
> +    def _decode_reply(data):
> +        data = json.loads(data)
> +        result = data["results"][0]
> +        if result["result"] == "error":
> +            msg = result["error_details"]["message"]
> +            raise bottle.HTTPError(400, "command error: %s" % msg)
> +        return data
> +
> +    @staticmethod
> +    def _decode_client_id(data):
> +        try:
> +            data = VfProc._decode_reply(data)
> +            return data["client_id"]
> +        except:
> +            return None
> +
> +    @exec_command
> +    def get_status(self):
> +        return "status"
> +
> +    @exec_command
> +    def start_component(self, comp_name, core_id, comp_type):
> +        return ("component start {comp_name} {core_id} {comp_type}"
> +                .format(**locals()))
> +
> +    @exec_command
> +    def stop_component(self, comp_name):
> +        return "component stop {comp_name}".format(**locals())
> +
> +    @exec_command
> +    def port_add(self, port, direction, comp_name, op, vlan_id, pcp):
> +        command = "port add {port} {direction} {comp_name}".format(**locals())
> +        if op != "none":
> +            command += " %s" % op
> +            if op == "add_vlantag":
> +                command += " %d %d" % (vlan_id, pcp)
> +        return command
> +
> +    @exec_command
> +    def port_del(self, port, direction, comp_name):
> +        return "port del {port} {direction} {comp_name}".format(**locals())
> +
> +    @exec_command
> +    def set_classifier_table(self, mac_address, port):
> +        return ("classifier_table add mac {mac_address} {port}"
> +                .format(**locals()))
> +
> +    @exec_command
> +    def clear_classifier_table(self, mac_address, port):
> +        return ("classifier_table del mac {mac_address} {port}"
> +                .format(**locals()))
> +
> +    @exec_command
> +    def set_classifier_table_with_vlan(self, mac_address, port,
> +                                       vlan_id):
> +        return ("classifier_table add vlan {vlan_id} {mac_address} {port}"
> +                .format(**locals()))
> +
> +    @exec_command
> +    def clear_classifier_table_with_vlan(self, mac_address, port,
> +                                         vlan_id):
> +        return ("classifier_table del vlan {vlan_id} {mac_address} {port}"
> +                .format(**locals()))
> +
> +
> +class NfvProc(SppProc):
> +
> +    def __init__(self, id, conn):
> +        super(NfvProc, self).__init__(TYPE_NFV, id, conn)
> +
> +    @staticmethod
> +    def _decode_reply(data):
> +        return data.strip('\0')
> +
> +    @staticmethod
> +    def _decode_client_id(data):
> +        try:
> +            return int(NfvProc._decode_reply(data))
> +        except:
> +            return None
> +
> +    @exec_command
> +    def get_status(self):
> +        return "status"
> +
> +    @exec_command
> +    def port_add(self, if_type, if_num):
> +        return "add {if_type} {if_num}".format(**locals())
> +
> +    @exec_command
> +    def port_del(self, if_type, if_num):
> +        return "del {if_type} {if_num}".format(**locals())
> +
> +    @exec_command
> +    def patch_add(self, src_port, dst_port):
> +        return "patch {src_port} {dst_port}".format(**locals())
> +
> +    @exec_command
> +    def patch_reset(self):
> +        return "patch reset"
> +
> +    @exec_command
> +    def forward(self):
> +        return "forward"
> +
> +    @exec_command
> +    def stop(self):
> +        return "stop"
> +
> +
> +class PrimaryProc(SppProc):
> +
> +    def __init__(self, conn):
> +        super(PrimaryProc, self).__init__(TYPE_PRIMARY, ID_PRIMARY, conn)
> +
> +    @staticmethod
> +    def _decode_reply(data):
> +        return data.strip('\0')
> +
> +    @exec_command
> +    def status(self):
> +        return "status"
> +
> +    @exec_command
> +    def clear(self):
> +        return "clear"
> diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
> new file mode 100644
> index 0000000..3ee7893
> --- /dev/null
> +++ b/src/spp-ctl/spp_webapi.py
> @@ -0,0 +1,440 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
> +
> +import bottle
> +import errno
> +import json
> +import logging
> +import netaddr
> +import re
> +import socket
> +import subprocess
> +import sys
> +
> +import spp_proc
> +
> +
> +LOG = logging.getLogger(__name__)
> +
> +
> +class KeyRequired(bottle.HTTPError):
> +
> +    def __init__(self, key):
> +        msg = "key(%s) required." % key
> +        super(KeyRequired, self).__init__(400, msg)
> +
> +
> +class KeyInvalid(bottle.HTTPError):
> +
> +    def __init__(self, key, value):
> +        msg = "invalid key(%s): %s." % (key, value)
> +        super(KeyRequired, self).__init__(400, msg)
> +
> +
> +class BaseHandler(bottle.Bottle):
> +    """Define common methods for each handler."""
> +
> +    def __init__(self, controller):
> +        super(BaseHandler, self).__init__()
> +        self.ctrl = controller
> +
> +        self.default_error_handler = self._error_handler
> +        bottle.response.default_status = 404
> +
> +    def _error_handler(self, res):
> +        # use "text/plain" as content_type rather than bottle's default
> +        # "html".
> +        res.content_type = "text/plain"
> +        return res.body
> +
> +    def _validate_port(self, port):
> +        try:
> +            if_type, if_num = port.split(":")
> +            if if_type not in ["phy", "vhost", "ring"]:
> +                raise
> +            int(if_num)
> +        except:
> +            raise KeyInvalid('port', port)
> +
> +    def log_url(self):
> +        LOG.info("%s %s called", bottle.request.method, bottle.request.path)
> +
> +    def log_response(self):
> +        LOG.info("response: %s", bottle.response.status)
> +
> +    # following three decorators do common works for each API.
> +    # each handler 'install' appropriate decorators.
> +    #
> +    def get_body(self, func):
> +        """Get body and set it to method argument.
> +        content-type is OK whether application/json or plain text.
> +        """
> +        def wrapper(*args, **kwargs):
> +            req = bottle.request
> +            if req.method in ["POST", "PUT"]:
> +                if req.get_header('Content-Type') == "application/json":
> +                    body = req.json
> +                else:
> +                    body = json.loads(req.body.read().decode())
> +                kwargs['body'] = body
> +                LOG.info("body: %s", body)
> +            return func(*args, **kwargs)
> +        return wrapper
> +
> +    def check_sec_id(self, func):
> +        """Get and check proc and set it to method argument."""
> +        def wrapper(*args, **kwargs):
> +            sec_id = kwargs.pop('sec_id', None)
> +            if sec_id is not None:
> +                proc = self.ctrl.procs.get(sec_id)
> +                if proc is None or proc.type != self.type:
> +                    raise bottle.HTTPError(404,
> +                                           "sec_id %d not found." % sec_id)
> +                kwargs['proc'] = proc
> +            return func(*args, **kwargs)
> +        return wrapper
> +
> +    def make_response(self, func):
> +        """Convert plain response to bottle.HTTPResponse."""
> +        def wrapper(*args, **kwargs):
> +            ret = func(*args, **kwargs)
> +            if ret is None:
> +                return bottle.HTTPResponse(status=204)
> +            else:
> +                r = bottle.HTTPResponse(status=200, body=json.dumps(ret))
> +                r.content_type = "application/json"
> +                return r
> +        return wrapper
> +
> +
> +class WebServer(BaseHandler):
> +    """Top level handler.
> +    handlers are hierarchized using 'mount' as follows:
> +    /          WebServer
> +    /v1          V1Handler
> +       /vfs        V1VFHandler
> +       /nfvs       V1NFVHandler
> +       /primary    V1PrimaryHandler
> +    """
> +
> +    def __init__(self, controller, api_port):
> +        super(WebServer, self).__init__(controller)
> +        self.api_port = api_port
> +
> +        self.mount("/v1", V1Handler(controller))
> +
> +        # request and response logging.
> +        self.add_hook("before_request", self.log_url)
> +        self.add_hook("after_request", self.log_response)
> +
> +    def start(self):
> +        self.run(server='eventlet', host='localhost', port=self.api_port,
> +                 quiet=True)
> +
> +
> +class V1Handler(BaseHandler):
> +    def __init__(self, controller):
> +        super(V1Handler, self).__init__(controller)
> +
> +        self.set_route()
> +
> +        self.mount("/vfs", V1VFHandler(controller))
> +        self.mount("/nfvs", V1NFVHandler(controller))
> +        self.mount("/primary", V1PrimaryHandler(controller))
> +
> +        self.install(self.make_response)
> +
> +    def set_route(self):
> +        self.route('/processes', 'GET', callback=self.get_processes)
> +
> +    def get_processes(self):
> +        LOG.info("get processes called.")
> +        return self.ctrl.get_processes()
> +
> +
> +class V1VFHandler(BaseHandler):
> +
> +    def __init__(self, controller):
> +        super(V1VFHandler, self).__init__(controller)
> +        self.type = spp_proc.TYPE_VF
> +
> +        self.set_route()
> +
> +        self.install(self.check_sec_id)
> +        self.install(self.get_body)
> +        self.install(self.make_response)
> +
> +    def set_route(self):
> +        self.route('/<sec_id:int>', 'GET', callback=self.vf_get)
> +        self.route('/<sec_id:int>/components', 'POST',
> +                   callback=self.vf_comp_start)
> +        self.route('/<sec_id:int>/components/<name>', 'DELETE',
> +                   callback=self.vf_comp_stop)
> +        self.route('/<sec_id:int>/components/<name>', 'PUT',
> +                   callback=self.vf_comp_port)
> +        self.route('/<sec_id:int>/classifier_table', 'PUT',
> +                   callback=self.vf_classifier)
> +
> +    def convert_vf_info(self, data):
> +        info = data["info"]
> +        vf = {}
> +        vf["client-id"] = info["client-id"]
> +        vf["ports"] = []
> +        for key in ["phy", "vhost", "ring"]:
> +            for idx in info[key]:
> +                vf["ports"].append(key + ":" + str(idx))
> +        vf["components"] = info["core"]
> +        vf["classifier_table"] = info["classifier_table"]
> +
> +        return vf
> +
> +    def vf_get(self, proc):
> +        return self.convert_vf_info(proc.get_status())
> +
> +    def _validate_vf_comp_start(self, body):
> +        for key in ['name', 'core', 'type']:
> +            if key not in body:
> +                raise KeyRequired(key)
> +        if not isinstance(body['name'], str):
> +            raise KeyInvalid('name', body['name'])
> +        if not isinstance(body['core'], int):
> +            raise KeyInvalid('core', body['core'])
> +        if body['type'] not in ["forward", "merge", "classifier_mac"]:
> +            raise KeyInvalid('type', body['type'])
> +
> +    def vf_comp_start(self, proc, body):
> +        self._validate_vf_comp_start(body)
> +        proc.start_component(body['name'], body['core'], body['type'])
> +
> +    def vf_comp_stop(self, proc, name):
> +        proc.stop_component(name)
> +
> +    def _validate_vf_comp_port(self, body):
> +        for key in ['action', 'port', 'dir']:
> +            if key not in body:
> +                raise KeyRequired(key)
> +        if body['action'] not in ["attach", "detach"]:
> +            raise KeyInvalid('action', body['action'])
> +        if body['dir'] not in ["rx", "tx"]:
> +            raise KeyInvalid('dir', body['dir'])
> +        self._validate_port(body['port'])
> +
> +        if body['action'] == "attach":
> +            vlan = body.get('vlan')
> +            if vlan:
> +                try:
> +                    if vlan['operation'] not in ["none", "add", "del"]:
> +                        raise
> +                    if vlan['operation'] == "add":
> +                        int(vlan['id'])
> +                        int(vlan['pcp'])
> +                except:
> +                    raise KeyInvalid('vlan', vlan)
> +
> +    def vf_comp_port(self, proc, name, body):
> +        self._validate_vf_comp_port(body)
> +
> +        if body['action'] == "attach":
> +            op = "none"
> +            vlan_id = 0
> +            pcp = 0
> +            vlan = body.get('vlan')
> +            if vlan:
> +                if vlan['operation'] == "add":
> +                    op = "add_vlantag"
> +                    vlan_id = vlan['id']
> +                    pcp = vlan['pcp']
> +                elif vlan['operation'] == "del":
> +                    op = "del_vlantag"
> +            proc.port_add(body['port'], body['dir'],
> +                          name, op, vlan_id, pcp)
> +        else:
> +            proc.port_del(body['port'], body['dir'], name)
> +
> +    def _validate_mac(self, mac_address):
> +        try:
> +            netaddr.EUI(mac_address)
> +        except:
> +            raise KeyInvalid('mac_address', mac_address)
> +
> +    def _validate_vf_classifier(self, body):
> +        for key in ['action', 'type', 'port', 'mac_address']:
> +            if key not in body:
> +                raise KeyRequired(key)
> +        if body['action'] not in ["add", "del"]:
> +            raise KeyInvalid('action', body['action'])
> +        if body['type'] not in ["mac", "vlan"]:
> +            raise KeyInvalid('type', body['type'])
> +        self._validate_port(body['port'])
> +        self._validate_mac(body['mac_address'])
> +
> +        if body['type'] == "vlan":
> +            try:
> +                int(body['vlan'])
> +            except:
> +                raise KeyInvalid('vlan', body.get('vlan'))
> +
> +    def vf_classifier(self, proc, body):
> +        self._validate_vf_classifier(body)
> +
> +        port = body['port']
> +        mac_address = body['mac_address']
> +
> +        if body['action'] == "add":
> +            if body['type'] == "mac":
> +                proc.set_classifier_table(mac_address, port)
> +            else:
> +                proc.set_classifier_table_with_vlan(
> +                    mac_address, port, body['vlan'])
> +        else:
> +            if body['type'] == "mac":
> +                proc.clear_classifier_table(mac_address, port)
> +            else:
> +                proc.clear_classifier_table_with_vlan(
> +                    mac_address, port, body['vlan'])
> +
> +
> +class V1NFVHandler(BaseHandler):
> +
> +    def __init__(self, controller):
> +        super(V1NFVHandler, self).__init__(controller)
> +        self.type = spp_proc.TYPE_NFV
> +
> +        self.set_route()
> +
> +        self.install(self.check_sec_id)
> +        self.install(self.get_body)
> +        self.install(self.make_response)
> +
> +    def set_route(self):
> +        self.route('/<sec_id:int>', 'GET', callback=self.nfv_get)
> +        self.route('/<sec_id:int>/forward', 'PUT',
> +                   callback=self.nfv_forward)
> +        self.route('/<sec_id:int>/ports', 'PUT',
> +                   callback=self.nfv_port)
> +        self.route('/<sec_id:int>/patches', 'PUT',
> +                   callback=self.nfv_patch_add)
> +        self.route('/<sec_id:int>/patches', 'DELETE',
> +                   callback=self.nfv_patch_del)
> +
> +    def convert_nfv_info(self, data):
> +        nfv = {}
> +        lines = data.splitlines()
> +        if len(lines) < 3:
> +            return {}
> +        p = re.compile("Client ID (\d+) (\w+)")
> +        m = p.match(lines[0])
> +        if m:
> +            nfv['client_id'] = int(m.group(1))
> +            nfv['status'] = m.group(2)
> +
> +        ports = {}
> +        outs = []
> +        for line in lines[2:]:
> +            if not line.startswith("port_id"):
> +                break
> +            arg1, _, arg2, arg3 = line.split(",")
> +            _, port_id = arg1.split(":")
> +            if arg2 == "PHY":
> +                port = "phy:" + port_id
> +            else:
> +                if_type, rest = arg2.split("(")
> +                if_num = rest.rstrip(")")
> +                if if_type == "RING":
> +                    port = "ring:" + if_num
> +                elif if_type == "VHOST":
> +                    port = "vhost:" + if_num
> +                else:
> +                    port = if_type + ":" + if_num
> +            ports[port_id] = port
> +            _, out_id = arg3.split(":")
> +            if out_id != "none":
> +                outs.append((port_id, out_id))
> +        nfv['ports'] = list(ports.values())
> +        patches = []
> +        if outs:
> +            for src_id, dst_id in outs:
> +                patches.append({"src": ports[src_id], "dst": ports[dst_id]})
> +        nfv['patches'] = patches
> +
> +        return nfv
> +
> +    def nfv_get(self, proc):
> +        return self.convert_nfv_info(proc.get_status())
> +
> +    def _validate_nfv_forward(self, body):
> +        if 'action' not in body:
> +            raise KeyRequired('action')
> +        if body['action'] not in ["start", "stop"]:
> +            raise KeyInvalid('action', body['action'])
> +
> +    def nfv_forward(self, proc, body):
> +        if body['action'] == "start":
> +            proc.forward()
> +        else:
> +            proc.stop()
> +
> +    def _validate_nfv_port(self, body):
> +        for key in ['action', 'port']:
> +            if key not in body:
> +                raise KeyRequired(key)
> +        if body['action'] not in ["add", "del"]:
> +            raise KeyInvalid('action', body['action'])
> +        self._validate_port(body['port'])
> +
> +    def nfv_port(self, proc, body):
> +        self._validate_nfv_port(body)
> +
> +        if_type, if_num = body['port'].split(":")
> +        if body['action'] == "add":
> +            proc.port_add(if_type, if_num)
> +        else:
> +            proc.port_del(if_type, if_num)
> +
> +    def _validate_nfv_patch(self, body):
> +        for key in ['src', 'dst']:
> +            if key not in body:
> +                raise KeyRequired(key)
> +        self._validate_port(body['src'])
> +        self._validate_port(body['dst'])
> +
> +    def nfv_patch_add(self, proc, body):
> +        self._validate_nfv_patch(body)
> +        proc.patch_add(body['src'], body['dst'])
> +
> +    def nfv_patch_del(self, proc):
> +        proc.patch_reset()
> +
> +
> +class V1PrimaryHandler(BaseHandler):
> +
> +    def __init__(self, controller):
> +        super(V1PrimaryHandler, self).__init__(controller)
> +
> +        self.set_route()
> +
> +        self.install(self.make_response)
> +
> +    def set_route(self):
> +        self.route('/status', 'GET', callback=self.get_status)
> +        self.route('/status', 'DELETE', callback=self.clear_status)
> +
> +    def _get_proc(self):
> +        proc = self.ctrl.procs.get(spp_proc.ID_PRIMARY)
> +        if proc is None:
> +            raise bottle.HTTPError(404, "primary not found.")
> +        return proc
> +
> +    def convert_status(self, data):
> +        # no data returned at the moment.
> +        # some data will be returned when the primary becomes to
> +        # return statistical information.
> +        return {}
> +
> +    def get_status(self):
> +        proc = self._get_proc()
> +        return self.convert_status(proc.status())
> +
> +    def clear_status(self):
> +        proc = self._get_proc()
> +        proc.clear()
> 


-- 
Yasufumi Ogawa
NTT Network Service Systems Labs

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [spp] [PATCH] spp-ctl: SPP controller with Web API
  2018-09-18 10:00 ` Yasufumi Ogawa
@ 2018-09-18 21:40   ` Itsuro ODA
  0 siblings, 0 replies; 33+ messages in thread
From: Itsuro ODA @ 2018-09-18 21:40 UTC (permalink / raw)
  To: spp

Yasufumi,

Thank you for the comment.

OK, I will revise the patch and post it again according to
your suggestion.

Thanks.
Itsuro Oda

On Tue, 18 Sep 2018 19:00:08 +0900
Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp> wrote:

> > spp-ctl is a SPP controller with a REST like web API.
> > > spp-ctl maintains the connections from the SPP processes and at
> > the same time exposes the API for the user to request for the
> > SPP processes.
> > > Background and motivation:
> > > Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
> > Therefore, spp-agent, a component of networking-spp which make
> > SPP available on OpenStack environment, implements SPP controller
> > in itself. (see. https://github.com/openstack/networking-spp )
> > > Either CLI or spp-agent, there is a problem that other people can
> > not request to SPP processes while using. spp-ctl is invented to
> > solve this problem.
> > > Both CLI and spp-agent can be used spp-ctl to request SPP
> > processes instead of owning contoroller itself. In that case,
> > multiple people can request to SPP processes at the same time.
> > Note that spp-agent has a plan to change to use spp-ctl.
> > It is also available not using CLI but requesting spp-ctl
> > directly.
> Hi Itsuro,
> 
> Thanks a lot for your proposal and contribution! It looks well defined APIs for existing SPP commands. I think it is also a good idea to fill the gap between usages of CLI and OpenStack.
> 
> For considering our conventions, I would like to ask you to revise your patch for the reasons described below.
> 
> * Patch should be divided into several patches for each of files if you add newly created files because it is difficult to understand the status of review if it is not divided.
> 
> * All of documentation should be contained in `docs/guides`. Your proposal is for adding new entity and it need to create a new sub-directory. I think `docs/guides/spp-ctl` is appropriate.
> 
> * `index.rst` is required in `docs/guides/spp-ctl`.
> 
> * It is better to rename `README.rst` to `overview.rst` for the convention.
> 
> * `requirements.txt` should not be included in `src` directory. Do you think to move it to the project root?
> 
> Thanks,
> Yasufumi

-- 
Itsuro ODA <oda@valinux.co.jp>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 00/13] spp-ctl: SPP controller with Web API
  2018-09-12 23:25 [spp] [PATCH] spp-ctl: SPP controller with Web API Itsuro ODA
  2018-09-18 10:00 ` Yasufumi Ogawa
@ 2018-10-05  1:37 ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 01/13] docs: add overview of spp-ctl oda
                     ` (12 more replies)
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
  2 siblings, 13 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

spp-ctl is a SPP controller with a REST like web API.

spp-ctl maintains the connections from the SPP processes and at
the same time exposes the API for the user to request for the
SPP processes.

Background and motivation:

Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
Therefore, spp-agent, a component of networking-spp which make
SPP available on OpenStack environment, implements SPP controller
in itself. (see. https://github.com/openstack/networking-spp )

Either CLI or spp-agent, there is a problem that other people can
not request to SPP processes while using. spp-ctl is invented to
solve this problem.

Both CLI and spp-agent can be used spp-ctl to request SPP
processes instead of owning contoroller itself. In that case,
multiple people can request to SPP processes at the same time.
Note that spp-agent has a plan to change to use spp-ctl.
It is also available not using CLI but requesting spp-ctl
directly.

Itsuro Oda (9):
  docs: add overview of spp-ctl
  docs: add API reference of spp-ctl
  docs: add index of spp-ctl
  project: add requirements.txt for spp-ctl
  docs: add spp-ctl to index of doc root
  spp-ctl: add entry point
  spp-ctl: add Controller class
  spp-ctl: add web API handler
  spp-ctl: add spp command interfaces

Yasufumi Ogawa (4):
  spp-ctl: update parsing spp_nfv status
  docs: add request examples of spp-ctl
  docs: correct directives of spp-ctl
  docs: add labels and captions for tables

 docs/guides/index.rst                 |    1 +
 docs/guides/spp-ctl/api-reference.rst | 1001 +++++++++++++++++++++++++
 docs/guides/spp-ctl/index.rst         |   14 +
 docs/guides/spp-ctl/overview.rst      |  119 +++
 requirements.txt                      |    4 +
 src/spp-ctl/spp-ctl                   |   11 +
 src/spp-ctl/spp_ctl.py                |  158 ++++
 src/spp-ctl/spp_proc.py               |  187 +++++
 src/spp-ctl/spp_webapi.py             |  431 +++++++++++
 9 files changed, 1926 insertions(+)
 create mode 100644 docs/guides/spp-ctl/api-reference.rst
 create mode 100644 docs/guides/spp-ctl/index.rst
 create mode 100644 docs/guides/spp-ctl/overview.rst
 create mode 100644 requirements.txt
 create mode 100644 src/spp-ctl/spp-ctl
 create mode 100644 src/spp-ctl/spp_ctl.py
 create mode 100644 src/spp-ctl/spp_proc.py
 create mode 100644 src/spp-ctl/spp_webapi.py

-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 01/13] docs: add overview of spp-ctl
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 02/13] docs: add API reference " oda
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Add overview section of spp-ctl.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/overview.rst | 119 +++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)
 create mode 100644 docs/guides/spp-ctl/overview.rst

diff --git a/docs/guides/spp-ctl/overview.rst b/docs/guides/spp-ctl/overview.rst
new file mode 100644
index 0000000..4c7181c
--- /dev/null
+++ b/docs/guides/spp-ctl/overview.rst
@@ -0,0 +1,119 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+.. _spp_ctl_overview:
+
+spp-ctl
+=======
+
+Overview
+--------
+
+``spp-ctl`` is an alternative SPP controller which provides a REST-like
+APIs.
+
+It maintains connections between SPP processes for managing them
+via the REST-like APIs for users or other processes.
+
+Background and Motivation
+-------------------------
+
+``spp-ctl`` is designed for supporting to manage SPP from several
+controllers.
+
+There are some usecases where SPP is managed from other
+process without user inputs.
+For example, you need a intermediate process if you think of using SPP
+from a framework, such as OpenStack.
+`networking-spp
+<https://github.com/openstack/networking-spp>`_
+is a Neutron ML2 plugin for SPP and `spp-agent` works as a
+SPP controller.
+
+It is a problem to prepare several controllers for each of usages for
+CLI, OpenStack or others because it would be hard to maintain for
+updates.
+
+Spp-ctl is designed to solve the problem of several controllers with
+application-independent REST APIs.
+You can manage SPP by requesting via this APIs.
+
+Architecture
+------------
+
+The design goal of spp-ctl is to be as simple as possible.
+It is stateless.
+Basically, spp-ctl only converts API requests into commands of SPP
+processes and throws request, thouth it does syntax and lexical check
+for API requests.
+
+``spp-ctl`` adopts
+`bottle
+<https://bottlepy.org/docs/dev/>`_
+which is simple and well known as a web framework and
+`eventlet
+<http://eventlet.net/>`_
+for parallel processing.
+``spp-ctl`` accepts multiple requests at the same time and serializes them
+internally.
+
+
+Setup
+=====
+
+You are required to install Python3 and packages described in
+``requirements.txt`` via ``pip3`` for launching ``spp-ctl``.
+You might need to run ``pip3`` with ``sudo``.
+
+.. code-block:: console
+
+    $ sudo apt update
+    $ sudo apt install python3
+    $ sudo apt install python3-pip
+    $ pip3 install -r requirements.txt
+
+Usage
+-----
+
+.. code-block:: console
+
+    usage: spp-ctl [-p PRI_PORT] [-s SEC_PORT] [-a API_PORT]
+
+    optional arguments:
+      -p PRI_PORT  primary port. default is 5555.
+      -s SEC_PORT  secondary port. default is 6666.
+      -a API_PORT  web api port. default is 7777.
+
+Using systemd
+-------------
+
+`spp-ctl` is assumed to be launched as a daemon process, or managed
+by `systemd`.
+Here is a simple example of service file for systemd.
+
+::
+
+    [Unit]
+    Description = SPP Controller
+
+    [Service]
+    ExecStart = /usr/bin/python3 /path/to/spp/src/spp-ctl/spp-ctl
+    User = root
+
+
+REST APIs
+=========
+
+You can try to call ``spp-ctl`` APIs with ``curl`` command as following.
+
+.. code-block:: console
+
+    $ curl http://localhost:7777/v1/processes
+    [{"type": "primary"}, ..., {"client-id": 2, "type": "vf"}]
+    $ curl http://localhost:7777/v1/vfs/1
+    ... snip
+    $ curl -X POST http://localhost:7777/v1/vfs/1/components \
+      -d '{"core": 2, "name": "forward_0_tx", "type": "forward"}'
+
+For more details, see
+:ref:`API Reference<spp_ctl_api_ref>`.
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 02/13] docs: add API reference of spp-ctl
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
  2018-10-05  1:37   ` [spp] [PATCH v3 01/13] docs: add overview of spp-ctl oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 03/13] docs: add index " oda
                     ` (10 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Add REST API reference of spp-ctl.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 829 ++++++++++++++++++++++++++
 1 file changed, 829 insertions(+)
 create mode 100644 docs/guides/spp-ctl/api-reference.rst

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
new file mode 100644
index 0000000..4d2a641
--- /dev/null
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -0,0 +1,829 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+.. _spp_ctl_api_ref:
+
+=============
+API Reference
+=============
+
+Overview
+========
+
+``spp-ctl`` provides simple REST like API. It supports http only, not https.
+
+Request and Response
+--------------------
+
+Request body is JSON format.
+It is accepted both ``text/plain`` and ``application/json``
+for the content-type header.
+
+Response of ``GET`` is JSON format and the content-type is
+``application/json`` if the request success.
+
+If a request fails, the response is a text which shows error reason
+and the content-type is ``text/plain``.
+
+Error code
+----------
+
+``spp-ctl`` does basic syntax and lexical check of a request.
+
++------+----------------------------------------------------------------+
+| Error| Description                                                    |
++======+================================================================+
+| 400  | Syntax or lexical error, or SPP returns error for the request. |
++------+----------------------------------------------------------------+
+| 404  | URL is not supported, or no SPP process of client-id in a URL. |
++------+----------------------------------------------------------------+
+| 500  | System error occured in ``spp-ctl``.                           |
++------+----------------------------------------------------------------+
+
+
+API independent of the process type
+===================================
+
+GET /v1/processes
+-----------------
+
+Show the SPP processes connected to the ``spp-ctl``.
+
+* Normarl response codes: 200
+
+Response
+^^^^^^^^
+
+An array of process objects.
+
+process object:
+
++-----------+---------+---------------------------------------------------------------+
+| Name      | Type    | Description                                                   |
++===========+=========+===============================================================+
+| type      | string  | process type. one of ``primary``, ``vf`` or ``nfv``.          |
++-----------+---------+---------------------------------------------------------------+
+| client-id | integer | client id. if type is ``primary`` this member does not exist. |
++-----------+---------+---------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    [
+      {
+        "type": "primary"
+      },
+      {
+        "type": "vf",
+        "client-id": 1
+      },
+      {
+        "type": "nfv",
+        "client-id": 2
+      }
+    ]
+
+
+API for spp_vf
+==============
+
+GET /v1/vfs/{client_id}
+-----------------------
+
+Get the information of the ``spp_vf`` process.
+
+* Normal response codes: 200
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Response
+^^^^^^^^
+
++------------------+---------+-----------------------------------------------+
+| Name             | Type    | Description                                   |
++==================+=========+===============================================+
+| client-id        | integer | client id.                                    |
++------------------+---------+-----------------------------------------------+
+| ports            | array   | an array of port ids used by the process.     |
++------------------+---------+-----------------------------------------------+
+| components       | array   | an array of component objects in the process. |
++------------------+---------+-----------------------------------------------+
+| classifier_table | array   | an array of classifier tables in the process. |
++------------------+---------+-----------------------------------------------+
+
+component object:
+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| core    | integer | core id running on the component                                    |
++---------+---------+---------------------------------------------------------------------+
+| name    | string  | an array of port ids used by the process.                           |
++---------+---------+---------------------------------------------------------------------+
+| type    | string  | an array of component objects in the process.                       |
++---------+---------+---------------------------------------------------------------------+
+| rx_port | array   | an array of port objects connected to the rx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+| tx_port | array   | an array of port objects connected to the tx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+
+port object:
+
++---------+---------+---------------------------------------------------------------+
+| Name    | Type    | Description                                                   |
++=========+=========+===============================================================+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
++---------+---------+---------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port.                  |
++---------+---------+---------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+-------------------------------+
+| Name      | Type    | Description                   |
++===========+=========+===============================+
+| operation | string  | ``add``, ``del`` or ``none``. |
++-----------+---------+-------------------------------+
+| id        | integer | vlan id.                      |
++-----------+---------+-------------------------------+
+| pcp       | integer | vlan pcp.                     |
++-----------+---------+-------------------------------+
+
+classifier table:
+
++-----------+--------+------------------------------------------------------------+
+| Name      | Type   | Description                                                |
++===========+========+============================================================+
+| type      | string | ``mac`` or ``vlan``.                                       |
++-----------+--------+------------------------------------------------------------+
+| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
++-----------+--------+------------------------------------------------------------+
+| port      | string | port id applied to classify.                               |
++-----------+--------+------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "client-id": 1,
+      "ports": [
+        "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
+      ],
+      "components": [
+        {
+          "core": 2,
+          "name": "forward_0_tx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "ring:0",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "vhost:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 3,
+          "type": "unuse"
+        },
+        {
+          "core": 4,
+          "type": "unuse"
+        },
+        {
+          "core": 5,
+          "name": "forward_1_rx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "vhost:1",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 6,
+          "name": "classifier",
+          "type": "classifier_mac",
+          "rx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:2",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 7,
+          "name": "merger",
+          "type": "merge",
+          "rx_port": [
+            {
+              "port": "ring:1",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+      ],
+      "classifier_table": [
+        {
+          "type": "mac",
+          "value": "FA:16:3E:7D:CC:35",
+          "port": "ring:0"
+        }
+      ]
+    }
+
+The component which type is ``unused`` is to indicate unused core.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};status
+
+
+POST /v1/vfs/{client_id}/components
+-----------------------------------
+
+Start the component.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-----------+---------+----------------------------------------------------------------------+
+| Name      | Type    | Description                                                          |
++===========+=========+======================================================================+
+| name      | string  | component name. must be unique in the process.                       |
++-----------+---------+----------------------------------------------------------------------+
+| core      | integer | core id.                                                             |
++-----------+---------+----------------------------------------------------------------------+
+| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
++-----------+---------+----------------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "name": "forwarder1",
+      "core": 12,
+      "type": "forward"
+    }
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``POST`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};component start {name} {core} {type}
+
+
+DELETE /v1/vfs/{sec id}/components/{name}
+-----------------------------------------
+
+Stop the component.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``POST`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};component stop {name}
+
+
+PUT /v1/vfs/{client_id}/components/{name}/ports
+-----------------------------------------------
+
+Add or Delete port to the component.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++---------+---------+-----------------------------------------------------------------+
+| Name    | Type    | Description                                                     |
++=========+=========+=================================================================+
+| action  | string  | ``attach`` or ``detach``.                                       |
++---------+---------+-----------------------------------------------------------------+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
++---------+---------+-----------------------------------------------------------------+
+| dir     | string  | ``rx`` or ``tx``.                                               |
++---------+---------+-----------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
++---------+---------+-----------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+----------------------------------------------------------+
+| Name      | Type    | Description                                              |
++===========+=========+==========================================================+
+| operation | string  | ``add``, ``del`` or ``none``.                            |
++-----------+---------+----------------------------------------------------------+
+| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
++-----------+---------+----------------------------------------------------------+
+| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
++-----------+---------+----------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "action": "attach",
+      "port": "vhost:1",
+      "dir": "rx",
+      "vlan": {
+        "operation": "add",
+        "id": 677,
+        "pcp": 0
+      }
+    }
+
+.. code-block:: yaml
+
+    {
+      "action": "detach",
+      "port": "vhost:0",
+      "dir": "tx"
+    }
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+action is ``attach``
+
+.. code-block:: console
+
+    sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
+
+action is ``detach``
+
+.. code-block:: console
+
+    sec {client_id};port del {port} {dir} {name}
+
+
+PUT /v1/vfs/{sec id}/classifier_table
+-------------------------------------
+
+Set or Unset classifier table.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-------------+-----------------+----------------------------------------------------+
+| Name        | Type            | Description                                        |
++=============+=================+====================================================+
+| action      | string          | ``add`` or ``del``.                                |
++-------------+-----------------+----------------------------------------------------+
+| type        | string          | ``mac`` or ``vlan``.                               |
++-------------+-----------------+----------------------------------------------------+
+| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
++-------------+-----------------+----------------------------------------------------+
+| mac_address | string          | mac address.                                       |
++-------------+-----------------+----------------------------------------------------+
+| port        | string          | port id.                                           |
++-------------+-----------------+----------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "action": "add",
+      "type": "mac",
+      "mac_address": "FA:16:3E:7D:CC:35",
+      "port": "ring:0"
+    }
+
+.. code-block:: yaml
+
+    {
+      "action": "del",
+      "type": "vlan",
+      "vlan": 475,
+      "mac_address": "FA:16:3E:7D:CC:35",
+      "port": "ring:0"
+    }
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+type is ``mac``
+
+.. code-block:: console
+
+    classifier_table {action} mac {mac_address} {port}
+
+type is ``vlan``
+
+.. code-block:: console
+
+    classifier_table {action} vlan {vlan} {mac_address} {port}
+
+
+API for spp_nfv/spp_vm
+======================
+
+GET /v1/nfvs/{client_id}
+------------------------
+
+Get the information of the ``spp_nfv`` or ``spp_vm`` process.
+
+* Normal response codes: 200
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Response
+^^^^^^^^
+
++-----------+---------+-------------------------------------------+
+| Name      | Type    | Description                               |
++===========+=========+===========================================+
+| client-id | integer | client id.                                |
++-----------+---------+-------------------------------------------+
+| status    | string  | ``Running`` or ``Idle``.                  |
++-----------+---------+-------------------------------------------+
+| ports     | array   | an array of port ids used by the process. |
++-----------+---------+-------------------------------------------+
+| patches   | array   | an array of patches.                      |
++-----------+---------+-------------------------------------------+
+
+patch objest
+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "client-id": 1,
+      "status": "Running",
+      "ports": [
+        "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
+      ],
+      "patches": [
+        {
+          "src": "vhost:0", "dst": "ring:0"
+        },
+        {
+          "src": "ring:1", "dst": "vhost:1"
+        }
+      ]
+    }
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};status
+
+
+PUT /v1/nfvs/{client_id}/forward
+--------------------------------
+
+Start or Stop forwarding.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+------------------------+
+| Name   | Type   | Description            |
++========+========+========================+
+| action | string | ``start`` or ``stop``. |
++--------+--------+------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {"action": "start"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+action is ``start``
+
+.. code-block:: yaml
+
+    sec {client_id};forward
+
+action is ``stop``
+
+.. code-block:: yaml
+
+    sec {client_id};stop
+
+
+PUT /v1/nfvs/{client_id}/ports
+------------------------------
+
+Add or Delete port.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+---------------------------------------------------------------+
+| Name   | Type   | Description                                                   |
++========+========+===============================================================+
+| action | string | ``add`` or ``del``.                                           |
++--------+--------+---------------------------------------------------------------+
+| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
++--------+--------+---------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {"action": "add", "port": "vhost:0"}
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};{action} {interface_type} {interface_id}
+
+
+PUT /v1/nfvs/{client_id}/patches
+--------------------------------
+
+Add a patch.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {"src": "vhost:0", "dst": "ring:0"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};patch {src} {dst}
+
+
+DELETE /v1/nfvs/{client_id}/patches
+-----------------------------------
+
+Reset patches.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``DELETE`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};patch reset
+
+
+API for spp_primary
+===================
+
+GET /v1/primary/status
+----------------------
+
+Show statistical information.
+
+* Normal response codes: 200
+
+Response
+^^^^^^^^
+
+There is no data at the moment. The statistical information will be returned
+when ``spp_primary`` implements it.
+
+
+DELETE /v1/primary/status
+-------------------------
+
+Clear statistical information.
+
+* Normal response codes: 204
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 03/13] docs: add index of spp-ctl
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
  2018-10-05  1:37   ` [spp] [PATCH v3 01/13] docs: add overview of spp-ctl oda
  2018-10-05  1:37   ` [spp] [PATCH v3 02/13] docs: add API reference " oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 04/13] project: add requirements.txt for spp-ctl oda
                     ` (9 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Add index.rst for spp-ctl section.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/index.rst | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 docs/guides/spp-ctl/index.rst

diff --git a/docs/guides/spp-ctl/index.rst b/docs/guides/spp-ctl/index.rst
new file mode 100644
index 0000000..499bcfe
--- /dev/null
+++ b/docs/guides/spp-ctl/index.rst
@@ -0,0 +1,14 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+.. _spp_ctl_index:
+
+spp-ctl
+=======
+
+.. toctree::
+   :maxdepth: 2
+   :numbered:
+
+   overview
+   api-reference
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 04/13] project: add requirements.txt for spp-ctl
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (2 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 03/13] docs: add index " oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 05/13] docs: add spp-ctl to index of doc root oda
                     ` (8 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

`requirements.txt` is for listing python packages installed via `pip`
as below.

  $ pip install -r requirements.txt

It contains a list of packages for `spp-ctl`.

* eventlet
* bottle
* netaddr

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 requirements.txt | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 requirements.txt

diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..7d0b774
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+# spp-ctl
+eventlet
+bottle
+netaddr
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 05/13] docs: add spp-ctl to index of doc root
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (3 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 04/13] project: add requirements.txt for spp-ctl oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 06/13] spp-ctl: add entry point oda
                     ` (7 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

To be referred from index of document root, add spp-ctl to the index.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/index.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/guides/index.rst b/docs/guides/index.rst
index d0242d6..9d62b30 100644
--- a/docs/guides/index.rst
+++ b/docs/guides/index.rst
@@ -39,3 +39,4 @@ SPP documentation
    commands/index
    tools/index
    spp_vf/index
+   spp-ctl/index
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 06/13] spp-ctl: add entry point
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (4 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 05/13] docs: add spp-ctl to index of doc root oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 07/13] spp-ctl: add Controller class oda
                     ` (6 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Add `spp-ctl` launched in a terminal as the entry point. Spp-ctl is
expected to be launched from Python3.

  $ python3 /path/to/spp/src/spp-ctl/spp-ctl

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp-ctl | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 src/spp-ctl/spp-ctl

diff --git a/src/spp-ctl/spp-ctl b/src/spp-ctl/spp-ctl
new file mode 100644
index 0000000..645611b
--- /dev/null
+++ b/src/spp-ctl/spp-ctl
@@ -0,0 +1,11 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import sys
+
+from spp_ctl import main
+
+
+if __name__ == "__main__":
+    sys.exit(main())
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 07/13] spp-ctl: add Controller class
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (5 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 06/13] spp-ctl: add entry point oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 08/13] spp-ctl: add web API handler oda
                     ` (5 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Controller is main class of spp-ctl for setting up connection between
SPP processes and initializing WebServer class for receiving requests.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_ctl.py | 158 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)
 create mode 100644 src/spp-ctl/spp_ctl.py

diff --git a/src/spp-ctl/spp_ctl.py b/src/spp-ctl/spp_ctl.py
new file mode 100644
index 0000000..e168747
--- /dev/null
+++ b/src/spp-ctl/spp_ctl.py
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import eventlet
+eventlet.monkey_patch()
+
+import argparse
+import errno
+import json
+import logging
+import socket
+import subprocess
+
+import spp_proc
+import spp_webapi
+
+
+LOG = logging.getLogger(__name__)
+
+
+MSG_SIZE = 4096
+
+
+class Controller(object):
+
+    def __init__(self, pri_port, sec_port, api_port):
+        self.web_server = spp_webapi.WebServer(self, api_port)
+        self.procs = {}
+        self.init_connection(pri_port, sec_port)
+
+    def start(self):
+        self.web_server.start()
+
+    def init_connection(self, pri_port, sec_port):
+        self.pri_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.pri_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.pri_sock.bind(('127.0.0.1', pri_port))
+        self.pri_sock.listen(1)
+        self.primary_listen_thread = eventlet.greenthread.spawn(
+            self.accept_primary)
+
+        self.sec_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sec_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.sec_sock.bind(('127.0.0.1', sec_port))
+        self.sec_sock.listen(1)
+        self.secondary_listen_thread = eventlet.greenthread.spawn(
+            self.accept_secondary)
+
+    def accept_primary(self):
+        while True:
+            conn, _ = self.pri_sock.accept()
+            proc = self.procs.get(spp_proc.ID_PRIMARY)
+            if proc is not None:
+                LOG.warning("spp_primary reconnect !")
+                with proc.sem:
+                    try:
+                        proc.conn.close()
+                    except Exception:
+                        pass
+                    proc.conn = conn
+                # NOTE: when spp_primary restart, all secondarys must be
+                # restarted. this is out of controle of spp-ctl.
+            else:
+                LOG.info("primary connected.")
+                self.procs[spp_proc.ID_PRIMARY] = spp_proc.PrimaryProc(conn)
+
+    def accept_secondary(self):
+        while True:
+            conn, _ = self.sec_sock.accept()
+            LOG.debug("sec accepted: get process id")
+            proc = self._get_proc(conn)
+            if proc is None:
+                LOG.error("get process id failed")
+                conn.close()
+                continue
+            old_proc = self.procs.get(proc.id)
+            if old_proc:
+                LOG.warning("%s(%d) reconnect !", old_proc.type, old_proc.id)
+                if old_proc.type != proc.type:
+                    LOG.warning("type changed ! new type: %s", proc.type)
+                with old_proc.sem:
+                    try:
+                        old_proc.conn.close()
+                    except Exception:
+                        pass
+            else:
+                LOG.info("%s(%d) connected.", proc.type, proc.id)
+            self.procs[proc.id] = proc
+
+    @staticmethod
+    def _continue_recv(conn):
+        try:
+            # must set non-blocking to recieve remining data not to happen
+            # blocking here.
+            # NOTE: usually MSG_DONTWAIT flag is used for this purpose but
+            # this flag is not supported under eventlet.
+            conn.setblocking(False)
+            data = b""
+            while True:
+                try:
+                    rcv_data = conn.recv(MSG_SIZE)
+                    data += rcv_data
+                    if len(rcv_data) < MSG_SIZE:
+                        break
+                except socket.error as e:
+                    if e.args[0] == errno.EAGAIN:
+                        # OK, no data remining. this happens when recieve data
+                        # length is just multiple of MSG_SIZE.
+                        break
+                    raise e
+            return data
+        finally:
+            conn.setblocking(True)
+
+    @staticmethod
+    def _send_command(conn, command):
+        conn.sendall(command.encode())
+        data = conn.recv(MSG_SIZE)
+        if data and len(data) == MSG_SIZE:
+            # could not receive data at once. recieve remining data.
+            data += self._continue_recv(conn)
+        if data:
+            data = data.decode()
+        return data
+
+    def _get_proc(self, conn):
+        # it is a bit ad hoc. send "_get_clinet_id" command and try to
+        # decode reply for each proc type. if success, that is the type.
+        data = self._send_command(conn, "_get_client_id")
+        for proc in [spp_proc.VfProc, spp_proc.NfvProc]:
+            sec_id = proc._decode_client_id(data)
+            if sec_id is not None:
+                return proc(sec_id, conn)
+
+    def get_processes(self):
+        procs = []
+        for proc in self.procs.values():
+            p = {"type": proc.type}
+            if proc.id != spp_proc.ID_PRIMARY:
+                p["client-id"] = proc.id
+            procs.append(p)
+        return procs
+
+
+def main():
+    parser = argparse.ArgumentParser(description="SPP Controller")
+    parser.add_argument("-p", dest='pri_port', type=int, default=5555,
+                        action='store', help="primary port")
+    parser.add_argument("-s", dest='sec_port', type=int, default=6666,
+                        action='store', help="secondary port")
+    parser.add_argument("-a", dest='api_port', type=int, default=7777,
+                        action='store', help="web api port")
+    args = parser.parse_args()
+
+    logging.basicConfig(level=logging.DEBUG)
+
+    controller = Controller(args.pri_port, args.sec_port, args.api_port)
+    controller.start()
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 08/13] spp-ctl: add web API handler
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (6 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 07/13] spp-ctl: add Controller class oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 09/13] spp-ctl: add spp command interfaces oda
                     ` (4 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Add WebServer class and handler classes to accept the request and route
for each of SPP processes.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_webapi.py | 441 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 441 insertions(+)
 create mode 100644 src/spp-ctl/spp_webapi.py

diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
new file mode 100644
index 0000000..435c4b7
--- /dev/null
+++ b/src/spp-ctl/spp_webapi.py
@@ -0,0 +1,441 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import bottle
+import errno
+import json
+import logging
+import netaddr
+import re
+import socket
+import subprocess
+import sys
+
+import spp_proc
+
+
+LOG = logging.getLogger(__name__)
+
+
+class KeyRequired(bottle.HTTPError):
+
+    def __init__(self, key):
+        msg = "key(%s) required." % key
+        super(KeyRequired, self).__init__(400, msg)
+
+
+class KeyInvalid(bottle.HTTPError):
+
+    def __init__(self, key, value):
+        msg = "invalid key(%s): %s." % (key, value)
+        super(KeyRequired, self).__init__(400, msg)
+
+
+class BaseHandler(bottle.Bottle):
+    """Define common methods for each handler."""
+
+    def __init__(self, controller):
+        super(BaseHandler, self).__init__()
+        self.ctrl = controller
+
+        self.default_error_handler = self._error_handler
+        bottle.response.default_status = 404
+
+    def _error_handler(self, res):
+        # use "text/plain" as content_type rather than bottle's default
+        # "html".
+        res.content_type = "text/plain"
+        return res.body
+
+    def _validate_port(self, port):
+        try:
+            if_type, if_num = port.split(":")
+            if if_type not in ["phy", "vhost", "ring"]:
+                raise
+            int(if_num)
+        except:
+            raise KeyInvalid('port', port)
+
+    def log_url(self):
+        LOG.info("%s %s called", bottle.request.method, bottle.request.path)
+
+    def log_response(self):
+        LOG.info("response: %s", bottle.response.status)
+
+    # following three decorators do common works for each API.
+    # each handler 'install' appropriate decorators.
+    #
+    def get_body(self, func):
+        """Get body and set it to method argument.
+        content-type is OK whether application/json or plain text.
+        """
+        def wrapper(*args, **kwargs):
+            req = bottle.request
+            if req.method in ["POST", "PUT"]:
+                if req.get_header('Content-Type') == "application/json":
+                    body = req.json
+                else:
+                    body = json.loads(req.body.read().decode())
+                kwargs['body'] = body
+                LOG.info("body: %s", body)
+            return func(*args, **kwargs)
+        return wrapper
+
+    def check_sec_id(self, func):
+        """Get and check proc and set it to method argument."""
+        def wrapper(*args, **kwargs):
+            sec_id = kwargs.pop('sec_id', None)
+            if sec_id is not None:
+                proc = self.ctrl.procs.get(sec_id)
+                if proc is None or proc.type != self.type:
+                    raise bottle.HTTPError(404,
+                                           "sec_id %d not found." % sec_id)
+                kwargs['proc'] = proc
+            return func(*args, **kwargs)
+        return wrapper
+
+    def make_response(self, func):
+        """Convert plain response to bottle.HTTPResponse."""
+        def wrapper(*args, **kwargs):
+            ret = func(*args, **kwargs)
+            if ret is None:
+                return bottle.HTTPResponse(status=204)
+            else:
+                r = bottle.HTTPResponse(status=200, body=json.dumps(ret))
+                r.content_type = "application/json"
+                return r
+        return wrapper
+
+
+class WebServer(BaseHandler):
+    """Top level handler.
+
+    handlers are hierarchized using 'mount' as follows:
+    /          WebServer
+    /v1          V1Handler
+       /vfs        V1VFHandler
+       /nfvs       V1NFVHandler
+       /primary    V1PrimaryHandler
+    """
+
+    def __init__(self, controller, api_port):
+        super(WebServer, self).__init__(controller)
+        self.api_port = api_port
+
+        self.mount("/v1", V1Handler(controller))
+
+        # request and response logging.
+        self.add_hook("before_request", self.log_url)
+        self.add_hook("after_request", self.log_response)
+
+    def start(self):
+        self.run(server='eventlet', host='localhost', port=self.api_port,
+                 quiet=True)
+
+
+class V1Handler(BaseHandler):
+    def __init__(self, controller):
+        super(V1Handler, self).__init__(controller)
+
+        self.set_route()
+
+        self.mount("/vfs", V1VFHandler(controller))
+        self.mount("/nfvs", V1NFVHandler(controller))
+        self.mount("/primary", V1PrimaryHandler(controller))
+
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/processes', 'GET', callback=self.get_processes)
+
+    def get_processes(self):
+        LOG.info("get processes called.")
+        return self.ctrl.get_processes()
+
+
+class V1VFHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1VFHandler, self).__init__(controller)
+        self.type = spp_proc.TYPE_VF
+
+        self.set_route()
+
+        self.install(self.check_sec_id)
+        self.install(self.get_body)
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/<sec_id:int>', 'GET', callback=self.vf_get)
+        self.route('/<sec_id:int>/components', 'POST',
+                   callback=self.vf_comp_start)
+        self.route('/<sec_id:int>/components/<name>', 'DELETE',
+                   callback=self.vf_comp_stop)
+        self.route('/<sec_id:int>/components/<name>', 'PUT',
+                   callback=self.vf_comp_port)
+        self.route('/<sec_id:int>/classifier_table', 'PUT',
+                   callback=self.vf_classifier)
+
+    def convert_vf_info(self, data):
+        info = data["info"]
+        vf = {}
+        vf["client-id"] = info["client-id"]
+        vf["ports"] = []
+        for key in ["phy", "vhost", "ring"]:
+            for idx in info[key]:
+                vf["ports"].append(key + ":" + str(idx))
+        vf["components"] = info["core"]
+        vf["classifier_table"] = info["classifier_table"]
+
+        return vf
+
+    def vf_get(self, proc):
+        return self.convert_vf_info(proc.get_status())
+
+    def _validate_vf_comp_start(self, body):
+        for key in ['name', 'core', 'type']:
+            if key not in body:
+                raise KeyRequired(key)
+        if not isinstance(body['name'], str):
+            raise KeyInvalid('name', body['name'])
+        if not isinstance(body['core'], int):
+            raise KeyInvalid('core', body['core'])
+        if body['type'] not in ["forward", "merge", "classifier_mac"]:
+            raise KeyInvalid('type', body['type'])
+
+    def vf_comp_start(self, proc, body):
+        self._validate_vf_comp_start(body)
+        proc.start_component(body['name'], body['core'], body['type'])
+
+    def vf_comp_stop(self, proc, name):
+        proc.stop_component(name)
+
+    def _validate_vf_comp_port(self, body):
+        for key in ['action', 'port', 'dir']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["attach", "detach"]:
+            raise KeyInvalid('action', body['action'])
+        if body['dir'] not in ["rx", "tx"]:
+            raise KeyInvalid('dir', body['dir'])
+        self._validate_port(body['port'])
+
+        if body['action'] == "attach":
+            vlan = body.get('vlan')
+            if vlan:
+                try:
+                    if vlan['operation'] not in ["none", "add", "del"]:
+                        raise
+                    if vlan['operation'] == "add":
+                        int(vlan['id'])
+                        int(vlan['pcp'])
+                except:
+                    raise KeyInvalid('vlan', vlan)
+
+    def vf_comp_port(self, proc, name, body):
+        self._validate_vf_comp_port(body)
+
+        if body['action'] == "attach":
+            op = "none"
+            vlan_id = 0
+            pcp = 0
+            vlan = body.get('vlan')
+            if vlan:
+                if vlan['operation'] == "add":
+                    op = "add_vlantag"
+                    vlan_id = vlan['id']
+                    pcp = vlan['pcp']
+                elif vlan['operation'] == "del":
+                    op = "del_vlantag"
+            proc.port_add(body['port'], body['dir'],
+                          name, op, vlan_id, pcp)
+        else:
+            proc.port_del(body['port'], body['dir'], name)
+
+    def _validate_mac(self, mac_address):
+        try:
+            netaddr.EUI(mac_address)
+        except:
+            raise KeyInvalid('mac_address', mac_address)
+
+    def _validate_vf_classifier(self, body):
+        for key in ['action', 'type', 'port', 'mac_address']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["add", "del"]:
+            raise KeyInvalid('action', body['action'])
+        if body['type'] not in ["mac", "vlan"]:
+            raise KeyInvalid('type', body['type'])
+        self._validate_port(body['port'])
+        self._validate_mac(body['mac_address'])
+
+        if body['type'] == "vlan":
+            try:
+                int(body['vlan'])
+            except:
+                raise KeyInvalid('vlan', body.get('vlan'))
+
+    def vf_classifier(self, proc, body):
+        self._validate_vf_classifier(body)
+
+        port = body['port']
+        mac_address = body['mac_address']
+
+        if body['action'] == "add":
+            if body['type'] == "mac":
+                proc.set_classifier_table(mac_address, port)
+            else:
+                proc.set_classifier_table_with_vlan(
+                    mac_address, port, body['vlan'])
+        else:
+            if body['type'] == "mac":
+                proc.clear_classifier_table(mac_address, port)
+            else:
+                proc.clear_classifier_table_with_vlan(
+                    mac_address, port, body['vlan'])
+
+
+class V1NFVHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1NFVHandler, self).__init__(controller)
+        self.type = spp_proc.TYPE_NFV
+
+        self.set_route()
+
+        self.install(self.check_sec_id)
+        self.install(self.get_body)
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/<sec_id:int>', 'GET', callback=self.nfv_get)
+        self.route('/<sec_id:int>/forward', 'PUT',
+                   callback=self.nfv_forward)
+        self.route('/<sec_id:int>/ports', 'PUT',
+                   callback=self.nfv_port)
+        self.route('/<sec_id:int>/patches', 'PUT',
+                   callback=self.nfv_patch_add)
+        self.route('/<sec_id:int>/patches', 'DELETE',
+                   callback=self.nfv_patch_del)
+
+    def convert_nfv_info(self, data):
+        nfv = {}
+        lines = data.splitlines()
+        if len(lines) < 3:
+            return {}
+        p = re.compile("Client ID (\d+) (\w+)")
+        m = p.match(lines[0])
+        if m:
+            nfv['client_id'] = int(m.group(1))
+            nfv['status'] = m.group(2)
+
+        ports = {}
+        outs = []
+        for line in lines[2:]:
+            if not line.startswith("port_id"):
+                break
+            arg1, _, arg2, arg3 = line.split(",")
+            _, port_id = arg1.split(":")
+            if arg2 == "PHY":
+                port = "phy:" + port_id
+            else:
+                if_type, rest = arg2.split("(")
+                if_num = rest.rstrip(")")
+                if if_type == "RING":
+                    port = "ring:" + if_num
+                elif if_type == "VHOST":
+                    port = "vhost:" + if_num
+                else:
+                    port = if_type + ":" + if_num
+            ports[port_id] = port
+            _, out_id = arg3.split(":")
+            if out_id != "none":
+                outs.append((port_id, out_id))
+        nfv['ports'] = list(ports.values())
+        patches = []
+        if outs:
+            for src_id, dst_id in outs:
+                patches.append({"src": ports[src_id], "dst": ports[dst_id]})
+        nfv['patches'] = patches
+
+        return nfv
+
+    def nfv_get(self, proc):
+        return self.convert_nfv_info(proc.get_status())
+
+    def _validate_nfv_forward(self, body):
+        if 'action' not in body:
+            raise KeyRequired('action')
+        if body['action'] not in ["start", "stop"]:
+            raise KeyInvalid('action', body['action'])
+
+    def nfv_forward(self, proc, body):
+        if body['action'] == "start":
+            proc.forward()
+        else:
+            proc.stop()
+
+    def _validate_nfv_port(self, body):
+        for key in ['action', 'port']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["add", "del"]:
+            raise KeyInvalid('action', body['action'])
+        self._validate_port(body['port'])
+
+    def nfv_port(self, proc, body):
+        self._validate_nfv_port(body)
+
+        if_type, if_num = body['port'].split(":")
+        if body['action'] == "add":
+            proc.port_add(if_type, if_num)
+        else:
+            proc.port_del(if_type, if_num)
+
+    def _validate_nfv_patch(self, body):
+        for key in ['src', 'dst']:
+            if key not in body:
+                raise KeyRequired(key)
+        self._validate_port(body['src'])
+        self._validate_port(body['dst'])
+
+    def nfv_patch_add(self, proc, body):
+        self._validate_nfv_patch(body)
+        proc.patch_add(body['src'], body['dst'])
+
+    def nfv_patch_del(self, proc):
+        proc.patch_reset()
+
+
+class V1PrimaryHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1PrimaryHandler, self).__init__(controller)
+
+        self.set_route()
+
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/status', 'GET', callback=self.get_status)
+        self.route('/status', 'DELETE', callback=self.clear_status)
+
+    def _get_proc(self):
+        proc = self.ctrl.procs.get(spp_proc.ID_PRIMARY)
+        if proc is None:
+            raise bottle.HTTPError(404, "primary not found.")
+        return proc
+
+    def convert_status(self, data):
+        # no data returned at the moment.
+        # some data will be returned when the primary becomes to
+        # return statistical information.
+        return {}
+
+    def get_status(self):
+        proc = self._get_proc()
+        return self.convert_status(proc.status())
+
+    def clear_status(self):
+        proc = self._get_proc()
+        proc.clear()
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 09/13] spp-ctl: add spp command interfaces
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (7 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 08/13] spp-ctl: add web API handler oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 10/13] spp-ctl: update parsing spp_nfv status oda
                     ` (3 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Itsuro Oda <oda@valinux.co.jp>

Add API classes for each of SPP processes.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_proc.py | 187 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 187 insertions(+)
 create mode 100644 src/spp-ctl/spp_proc.py

diff --git a/src/spp-ctl/spp_proc.py b/src/spp-ctl/spp_proc.py
new file mode 100644
index 0000000..aa83b76
--- /dev/null
+++ b/src/spp-ctl/spp_proc.py
@@ -0,0 +1,187 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import bottle
+import eventlet
+import json
+import logging
+
+import spp_ctl
+
+
+LOG = logging.getLogger(__name__)
+
+ID_PRIMARY = 0
+TYPE_PRIMARY = "primary"
+TYPE_VF = "vf"
+TYPE_NFV = "nfv"
+
+
+def exec_command(func):
+    """Decorator for Sending command and receiving reply.
+
+    Define the common function for sending command and receiving reply
+    as a decorator. Each of methods for executing command has only to
+    return command string.
+
+    exp)
+    @exec_command
+    def some_command(self, ...):
+        return "command string of some_command"
+    """
+    def wrapper(self, *args, **kwargs):
+        with self.sem:
+            command = func(self, *args, **kwargs)
+            LOG.info("%s(%d) command executed: %s", self.type, self.id,
+                     command)
+            data = spp_ctl.Controller._send_command(self.conn, command)
+            if data is None:
+                raise RuntimeError("%s(%d): %s: no-data returned" %
+                                   (self.type, self.id, command))
+            LOG.debug("reply: %s", data)
+            return self._decode_reply(data)
+    return wrapper
+
+
+class SppProc(object):
+    def __init__(self, proc_type, id, conn):
+        self.id = id
+        self.type = proc_type
+        # NOTE: executing command is serialized by using a semaphore
+        # for each process.
+        self.sem = eventlet.semaphore.Semaphore(value=1)
+        self.conn = conn
+
+
+class VfProc(SppProc):
+
+    def __init__(self, id, conn):
+        super(VfProc, self).__init__(TYPE_VF, id, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        data = json.loads(data)
+        result = data["results"][0]
+        if result["result"] == "error":
+            msg = result["error_details"]["message"]
+            raise bottle.HTTPError(400, "command error: %s" % msg)
+        return data
+
+    @staticmethod
+    def _decode_client_id(data):
+        try:
+            data = VfProc._decode_reply(data)
+            return data["client_id"]
+        except:
+            return None
+
+    @exec_command
+    def get_status(self):
+        return "status"
+
+    @exec_command
+    def start_component(self, comp_name, core_id, comp_type):
+        return ("component start {comp_name} {core_id} {comp_type}"
+                .format(**locals()))
+
+    @exec_command
+    def stop_component(self, comp_name):
+        return "component stop {comp_name}".format(**locals())
+
+    @exec_command
+    def port_add(self, port, direction, comp_name, op, vlan_id, pcp):
+        command = "port add {port} {direction} {comp_name}".format(**locals())
+        if op != "none":
+            command += " %s" % op
+            if op == "add_vlantag":
+                command += " %d %d" % (vlan_id, pcp)
+        return command
+
+    @exec_command
+    def port_del(self, port, direction, comp_name):
+        return "port del {port} {direction} {comp_name}".format(**locals())
+
+    @exec_command
+    def set_classifier_table(self, mac_address, port):
+        return ("classifier_table add mac {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def clear_classifier_table(self, mac_address, port):
+        return ("classifier_table del mac {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def set_classifier_table_with_vlan(self, mac_address, port,
+                                       vlan_id):
+        return ("classifier_table add vlan {vlan_id} {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def clear_classifier_table_with_vlan(self, mac_address, port,
+                                         vlan_id):
+        return ("classifier_table del vlan {vlan_id} {mac_address} {port}"
+                .format(**locals()))
+
+
+class NfvProc(SppProc):
+
+    def __init__(self, id, conn):
+        super(NfvProc, self).__init__(TYPE_NFV, id, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        return data.strip('\0')
+
+    @staticmethod
+    def _decode_client_id(data):
+        try:
+            return int(NfvProc._decode_reply(data))
+        except:
+            return None
+
+    @exec_command
+    def get_status(self):
+        return "status"
+
+    @exec_command
+    def port_add(self, if_type, if_num):
+        return "add {if_type} {if_num}".format(**locals())
+
+    @exec_command
+    def port_del(self, if_type, if_num):
+        return "del {if_type} {if_num}".format(**locals())
+
+    @exec_command
+    def patch_add(self, src_port, dst_port):
+        return "patch {src_port} {dst_port}".format(**locals())
+
+    @exec_command
+    def patch_reset(self):
+        return "patch reset"
+
+    @exec_command
+    def forward(self):
+        return "forward"
+
+    @exec_command
+    def stop(self):
+        return "stop"
+
+
+class PrimaryProc(SppProc):
+
+    def __init__(self, conn):
+        super(PrimaryProc, self).__init__(TYPE_PRIMARY, ID_PRIMARY, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        return data.strip('\0')
+
+    @exec_command
+    def status(self):
+        return "status"
+
+    @exec_command
+    def clear(self):
+        return "clear"
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 10/13] spp-ctl: update parsing spp_nfv status
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (8 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 09/13] spp-ctl: add spp command interfaces oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 11/13] docs: add request examples of spp-ctl oda
                     ` (2 subsequent siblings)
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Update for parsing the result of status of spp_nfv.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_webapi.py | 66 +++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 38 deletions(-)

diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
index 435c4b7..ee47f2f 100644
--- a/src/spp-ctl/spp_webapi.py
+++ b/src/spp-ctl/spp_webapi.py
@@ -318,50 +318,40 @@ class V1NFVHandler(BaseHandler):
         self.route('/<sec_id:int>/patches', 'DELETE',
                    callback=self.nfv_patch_del)
 
-    def convert_nfv_info(self, data):
+    def convert_nfv_info(self, sec_id, data):
         nfv = {}
-        lines = data.splitlines()
-        if len(lines) < 3:
+
+        # spp_nfv returns status info in two lines. First line is
+        # status of running or idling, and second is patch info.
+        # 'null' means that it has no dst port.
+        #   "status: idling\nports: 'phy:0-phy:1,phy:1-null'\x00\x00.."
+        entries = data.split('\n')
+        if len(entries) != 2:
             return {}
-        p = re.compile("Client ID (\d+) (\w+)")
-        m = p.match(lines[0])
-        if m:
-            nfv['client_id'] = int(m.group(1))
-            nfv['status'] = m.group(2)
-
-        ports = {}
-        outs = []
-        for line in lines[2:]:
-            if not line.startswith("port_id"):
-                break
-            arg1, _, arg2, arg3 = line.split(",")
-            _, port_id = arg1.split(":")
-            if arg2 == "PHY":
-                port = "phy:" + port_id
-            else:
-                if_type, rest = arg2.split("(")
-                if_num = rest.rstrip(")")
-                if if_type == "RING":
-                    port = "ring:" + if_num
-                elif if_type == "VHOST":
-                    port = "vhost:" + if_num
-                else:
-                    port = if_type + ":" + if_num
-            ports[port_id] = port
-            _, out_id = arg3.split(":")
-            if out_id != "none":
-                outs.append((port_id, out_id))
-        nfv['ports'] = list(ports.values())
-        patches = []
-        if outs:
-            for src_id, dst_id in outs:
-                patches.append({"src": ports[src_id], "dst": ports[dst_id]})
-        nfv['patches'] = patches
+
+        nfv['client_id'] = int(sec_id)
+        nfv['status'] = entries[0].split()[1]
+
+        patch_list = entries[1].split()[1].replace("'", '')
+
+        ports = []
+        nfv['patches'] = []
+
+        for port_cmb in patch_list.split(','):
+            p_src, p_dst = port_cmb.split('-')
+            if p_src != 'null' and p_dst != 'null':
+                nfv['patches'].append({'src': p_src, 'dst': p_dst})
+
+            for port in [p_src, p_dst]:
+                if port != 'null':
+                    ports.append(port)
+
+        nfv['ports'] = list(set(ports))
 
         return nfv
 
     def nfv_get(self, proc):
-        return self.convert_nfv_info(proc.get_status())
+        return self.convert_nfv_info(proc.id, proc.get_status())
 
     def _validate_nfv_forward(self, body):
         if 'action' not in body:
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 11/13] docs: add request examples of spp-ctl
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (9 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 10/13] spp-ctl: update parsing spp_nfv status oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 12/13] docs: correct directives " oda
  2018-10-05  1:37   ` [spp] [PATCH v3 13/13] docs: add labels and captions for tables oda
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Add request examples of spp-ctl using curl command.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 887 ++++++++++++++------------
 1 file changed, 465 insertions(+), 422 deletions(-)

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
index 4d2a641..285526e 100644
--- a/docs/guides/spp-ctl/api-reference.rst
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -51,6 +51,14 @@ Show the SPP processes connected to the ``spp-ctl``.
 
 * Normarl response codes: 200
 
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/processes
+
 Response
 ^^^^^^^^
 
@@ -86,13 +94,59 @@ Response example
     ]
 
 
-API for spp_vf
-==============
+API for spp_primary
+===================
 
-GET /v1/vfs/{client_id}
------------------------
+GET /v1/primary/status
+----------------------
 
-Get the information of the ``spp_vf`` process.
+Show statistical information.
+
+* Normal response codes: 200
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/primary/status
+
+Response
+^^^^^^^^
+
+There is no data at the moment. The statistical information will be returned
+when ``spp_primary`` implements it.
+
+
+DELETE /v1/primary/status
+-------------------------
+
+Clear statistical information.
+
+* Normal response codes: 204
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X DELETE -H 'application/json' \
+    http://127.0.0.1:7777/v1/primary/status
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+
+API for spp_nfv/spp_vm
+======================
+
+GET /v1/nfvs/{client_id}
+------------------------
+
+Get the information of the ``spp_nfv`` or ``spp_vm`` process.
 
 * Normal response codes: 200
 * Error response codes: 400, 404
@@ -106,70 +160,38 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-Response
-^^^^^^^^
-
-+------------------+---------+-----------------------------------------------+
-| Name             | Type    | Description                                   |
-+==================+=========+===============================================+
-| client-id        | integer | client id.                                    |
-+------------------+---------+-----------------------------------------------+
-| ports            | array   | an array of port ids used by the process.     |
-+------------------+---------+-----------------------------------------------+
-| components       | array   | an array of component objects in the process. |
-+------------------+---------+-----------------------------------------------+
-| classifier_table | array   | an array of classifier tables in the process. |
-+------------------+---------+-----------------------------------------------+
-
-component object:
-
-+---------+---------+---------------------------------------------------------------------+
-| Name    | Type    | Description                                                         |
-+=========+=========+=====================================================================+
-| core    | integer | core id running on the component                                    |
-+---------+---------+---------------------------------------------------------------------+
-| name    | string  | an array of port ids used by the process.                           |
-+---------+---------+---------------------------------------------------------------------+
-| type    | string  | an array of component objects in the process.                       |
-+---------+---------+---------------------------------------------------------------------+
-| rx_port | array   | an array of port objects connected to the rx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
-| tx_port | array   | an array of port objects connected to the tx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
+Request example
+^^^^^^^^^^^^^^^
 
-port object:
+.. code-block:: console
 
-+---------+---------+---------------------------------------------------------------+
-| Name    | Type    | Description                                                   |
-+=========+=========+===============================================================+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
-+---------+---------+---------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port.                  |
-+---------+---------+---------------------------------------------------------------+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/nfvs/1
 
-vlan object:
+Response
+^^^^^^^^
 
-+-----------+---------+-------------------------------+
-| Name      | Type    | Description                   |
-+===========+=========+===============================+
-| operation | string  | ``add``, ``del`` or ``none``. |
-+-----------+---------+-------------------------------+
-| id        | integer | vlan id.                      |
-+-----------+---------+-------------------------------+
-| pcp       | integer | vlan pcp.                     |
-+-----------+---------+-------------------------------+
++-----------+---------+-------------------------------------------+
+| Name      | Type    | Description                               |
++===========+=========+===========================================+
+| client-id | integer | client id.                                |
++-----------+---------+-------------------------------------------+
+| status    | string  | ``Running`` or ``Idle``.                  |
++-----------+---------+-------------------------------------------+
+| ports     | array   | an array of port ids used by the process. |
++-----------+---------+-------------------------------------------+
+| patches   | array   | an array of patches.                      |
++-----------+---------+-------------------------------------------+
 
-classifier table:
+patch objest
 
-+-----------+--------+------------------------------------------------------------+
-| Name      | Type   | Description                                                |
-+===========+========+============================================================+
-| type      | string | ``mac`` or ``vlan``.                                       |
-+-----------+--------+------------------------------------------------------------+
-| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
-+-----------+--------+------------------------------------------------------------+
-| port      | string | port id applied to classify.                               |
-+-----------+--------+------------------------------------------------------------+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -178,106 +200,20 @@ Response example
 
     {
       "client-id": 1,
+      "status": "Running",
       "ports": [
         "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
       ],
-      "components": [
-        {
-          "core": 2,
-          "name": "forward_0_tx",
-          "type": "forward",
-          "rx_port": [
-            {
-            "port": "ring:0",
-            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "vhost:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
-        },
-        {
-          "core": 3,
-          "type": "unuse"
-        },
-        {
-          "core": 4,
-          "type": "unuse"
-        },
-        {
-          "core": 5,
-          "name": "forward_1_rx",
-          "type": "forward",
-          "rx_port": [
-            {
-            "port": "vhost:1",
-            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "ring:3",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
-        },
-        {
-          "core": 6,
-          "name": "classifier",
-          "type": "classifier_mac",
-          "rx_port": [
-            {
-              "port": "phy:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "ring:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            },
-            {
-              "port": "ring:2",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
-        },
+      "patches": [
         {
-          "core": 7,
-          "name": "merger",
-          "type": "merge",
-          "rx_port": [
-            {
-              "port": "ring:1",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            },
-            {
-              "port": "ring:3",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "phy:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
+          "src": "vhost:0", "dst": "ring:0"
         },
-      ],
-      "classifier_table": [
         {
-          "type": "mac",
-          "value": "FA:16:3E:7D:CC:35",
-          "port": "ring:0"
+          "src": "ring:1", "dst": "vhost:1"
         }
       ]
     }
 
-The component which type is ``unused`` is to indicate unused core.
-
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
@@ -286,10 +222,10 @@ Equivalent CLI command
     sec {client_id};status
 
 
-POST /v1/vfs/{client_id}/components
------------------------------------
+PUT /v1/nfvs/{client_id}/forward
+--------------------------------
 
-Start the component.
+Start or Stop forwarding.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -303,80 +239,99 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-
-Request(body)
-^^^^^^^^^^^^^
-
-+-----------+---------+----------------------------------------------------------------------+
-| Name      | Type    | Description                                                          |
-+===========+=========+======================================================================+
-| name      | string  | component name. must be unique in the process.                       |
-+-----------+---------+----------------------------------------------------------------------+
-| core      | integer | core id.                                                             |
-+-----------+---------+----------------------------------------------------------------------+
-| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
-+-----------+---------+----------------------------------------------------------------------+
-
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: console
 
-    {
-      "name": "forwarder1",
-      "core": 12,
-      "type": "forward"
-    }
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "start"}' \
+    http://127.0.0.1:7777/v1/nfvs/1/forward
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+------------------------+
+| Name   | Type   | Description            |
++========+========+========================+
+| action | string | ``start`` or ``stop``. |
++--------+--------+------------------------+
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``POST`` request.
+There is no body content for the response of a successful ``PUT`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+action is ``start``
 
-    sec {client_id};component start {name} {core} {type}
+.. code-block:: yaml
 
+    sec {client_id};forward
 
-DELETE /v1/vfs/{sec id}/components/{name}
------------------------------------------
+action is ``stop``
 
-Stop the component.
+.. code-block:: yaml
+
+    sec {client_id};stop
+
+
+PUT /v1/nfvs/{client_id}/ports
+------------------------------
+
+Add or Delete port.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
 
-Request(path)
-^^^^^^^^^^^^^
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+---------------------------------------------------------------+
+| Name   | Type   | Description                                                   |
++========+========+===============================================================+
+| action | string | ``add`` or ``del``.                                           |
++--------+--------+---------------------------------------------------------------+
+| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
++--------+--------+---------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "add", "port": "ring:0"}' \
+    http://127.0.0.1:7777/v1/nfvs/1/ports
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``POST`` request.
+There is no body content for the response of a successful ``PUT`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-    sec {client_id};component stop {name}
+    sec {client_id};{action} {interface_type} {interface_id}
 
 
-PUT /v1/vfs/{client_id}/components/{name}/ports
------------------------------------------------
+PUT /v1/nfvs/{client_id}/patches
+--------------------------------
 
-Add or Delete port to the component.
+Add a patch.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -384,92 +339,49 @@ Add or Delete port to the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+---------+---------+-----------------------------------------------------------------+
-| Name    | Type    | Description                                                     |
-+=========+=========+=================================================================+
-| action  | string  | ``attach`` or ``detach``.                                       |
-+---------+---------+-----------------------------------------------------------------+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
-+---------+---------+-----------------------------------------------------------------+
-| dir     | string  | ``rx`` or ``tx``.                                               |
-+---------+---------+-----------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
-+---------+---------+-----------------------------------------------------------------+
-
-vlan object:
-
-+-----------+---------+----------------------------------------------------------+
-| Name      | Type    | Description                                              |
-+===========+=========+==========================================================+
-| operation | string  | ``add``, ``del`` or ``none``.                            |
-+-----------+---------+----------------------------------------------------------+
-| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
-+-----------+---------+----------------------------------------------------------+
-| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
-+-----------+---------+----------------------------------------------------------+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
-
-    {
-      "action": "attach",
-      "port": "vhost:1",
-      "dir": "rx",
-      "vlan": {
-        "operation": "add",
-        "id": 677,
-        "pcp": 0
-      }
-    }
-
-.. code-block:: yaml
-
-    {
-      "action": "detach",
-      "port": "vhost:0",
-      "dir": "tx"
-    }
+.. code-block:: console
 
+    curl -X PUT -H 'application/json' \
+    -d '{"src": "ring:0", "dst": "ring:1"}' \
+    http://127.0.0.1:7777/v1/nfvs/1/patches
 
 Response
 ^^^^^^^^
 
 There is no body content for the response of a successful ``PUT`` request.
 
-
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-action is ``attach``
-
-.. code-block:: console
-
-    sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
-
-action is ``detach``
-
 .. code-block:: console
 
-    sec {client_id};port del {port} {dir} {name}
+    sec {client_id};patch {src} {dst}
 
 
-PUT /v1/vfs/{sec id}/classifier_table
--------------------------------------
+DELETE /v1/nfvs/{client_id}/patches
+-----------------------------------
 
-Set or Unset classifier table.
+Reset patches.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -483,74 +395,34 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-Request(body)
-^^^^^^^^^^^^^
-
-+-------------+-----------------+----------------------------------------------------+
-| Name        | Type            | Description                                        |
-+=============+=================+====================================================+
-| action      | string          | ``add`` or ``del``.                                |
-+-------------+-----------------+----------------------------------------------------+
-| type        | string          | ``mac`` or ``vlan``.                               |
-+-------------+-----------------+----------------------------------------------------+
-| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
-+-------------+-----------------+----------------------------------------------------+
-| mac_address | string          | mac address.                                       |
-+-------------+-----------------+----------------------------------------------------+
-| port        | string          | port id.                                           |
-+-------------+-----------------+----------------------------------------------------+
-
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
-
-    {
-      "action": "add",
-      "type": "mac",
-      "mac_address": "FA:16:3E:7D:CC:35",
-      "port": "ring:0"
-    }
-
-.. code-block:: yaml
-
-    {
-      "action": "del",
-      "type": "vlan",
-      "vlan": 475,
-      "mac_address": "FA:16:3E:7D:CC:35",
-      "port": "ring:0"
-    }
+.. code-block:: console
 
+    curl -X DELETE -H 'application/json' \
+    http://127.0.0.1:7777/v1/nfvs/1/patches
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``PUT`` request.
+There is no body content for the response of a successful ``DELETE`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-type is ``mac``
-
-.. code-block:: console
-
-    classifier_table {action} mac {mac_address} {port}
-
-type is ``vlan``
-
 .. code-block:: console
 
-    classifier_table {action} vlan {vlan} {mac_address} {port}
+    sec {client_id};patch reset
 
 
-API for spp_nfv/spp_vm
-======================
+API for spp_vf
+==============
 
-GET /v1/nfvs/{client_id}
-------------------------
+GET /v1/vfs/{client_id}
+-----------------------
 
-Get the information of the ``spp_nfv`` or ``spp_vm`` process.
+Get the information of the ``spp_vf`` process.
 
 * Normal response codes: 200
 * Error response codes: 400, 404
@@ -564,30 +436,78 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/vfs/1
+
 Response
 ^^^^^^^^
 
-+-----------+---------+-------------------------------------------+
-| Name      | Type    | Description                               |
-+===========+=========+===========================================+
-| client-id | integer | client id.                                |
-+-----------+---------+-------------------------------------------+
-| status    | string  | ``Running`` or ``Idle``.                  |
-+-----------+---------+-------------------------------------------+
-| ports     | array   | an array of port ids used by the process. |
-+-----------+---------+-------------------------------------------+
-| patches   | array   | an array of patches.                      |
-+-----------+---------+-------------------------------------------+
++------------------+---------+-----------------------------------------------+
+| Name             | Type    | Description                                   |
++==================+=========+===============================================+
+| client-id        | integer | client id.                                    |
++------------------+---------+-----------------------------------------------+
+| ports            | array   | an array of port ids used by the process.     |
++------------------+---------+-----------------------------------------------+
+| components       | array   | an array of component objects in the process. |
++------------------+---------+-----------------------------------------------+
+| classifier_table | array   | an array of classifier tables in the process. |
++------------------+---------+-----------------------------------------------+
 
-patch objest
+component object:
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| core    | integer | core id running on the component                                    |
++---------+---------+---------------------------------------------------------------------+
+| name    | string  | an array of port ids used by the process.                           |
++---------+---------+---------------------------------------------------------------------+
+| type    | string  | an array of component objects in the process.                       |
++---------+---------+---------------------------------------------------------------------+
+| rx_port | array   | an array of port objects connected to the rx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+| tx_port | array   | an array of port objects connected to the tx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+
+port object:
+
++---------+---------+---------------------------------------------------------------+
+| Name    | Type    | Description                                                   |
++=========+=========+===============================================================+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
++---------+---------+---------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port.                  |
++---------+---------+---------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+-------------------------------+
+| Name      | Type    | Description                   |
++===========+=========+===============================+
+| operation | string  | ``add``, ``del`` or ``none``. |
++-----------+---------+-------------------------------+
+| id        | integer | vlan id.                      |
++-----------+---------+-------------------------------+
+| pcp       | integer | vlan pcp.                     |
++-----------+---------+-------------------------------+
+
+classifier table:
+
++-----------+--------+------------------------------------------------------------+
+| Name      | Type   | Description                                                |
++===========+========+============================================================+
+| type      | string | ``mac`` or ``vlan``.                                       |
++-----------+--------+------------------------------------------------------------+
+| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
++-----------+--------+------------------------------------------------------------+
+| port      | string | port id applied to classify.                               |
++-----------+--------+------------------------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -596,20 +516,106 @@ Response example
 
     {
       "client-id": 1,
-      "status": "Running",
       "ports": [
         "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
       ],
-      "patches": [
+      "components": [
+        {
+          "core": 2,
+          "name": "forward_0_tx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "ring:0",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "vhost:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 3,
+          "type": "unuse"
+        },
+        {
+          "core": 4,
+          "type": "unuse"
+        },
+        {
+          "core": 5,
+          "name": "forward_1_rx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "vhost:1",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
         {
-          "src": "vhost:0", "dst": "ring:0"
+          "core": 6,
+          "name": "classifier",
+          "type": "classifier_mac",
+          "rx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:2",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
         },
         {
-          "src": "ring:1", "dst": "vhost:1"
+          "core": 7,
+          "name": "merger",
+          "type": "merge",
+          "rx_port": [
+            {
+              "port": "ring:1",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+      ],
+      "classifier_table": [
+        {
+          "type": "mac",
+          "value": "FA:16:3E:7D:CC:35",
+          "port": "ring:0"
         }
       ]
     }
 
+The component which type is ``unused`` is to indicate unused core.
+
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
@@ -618,10 +624,10 @@ Equivalent CLI command
     sec {client_id};status
 
 
-PUT /v1/nfvs/{client_id}/forward
---------------------------------
+POST /v1/vfs/{client_id}/components
+-----------------------------------
 
-Start or Stop forwarding.
+Start the component.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -635,47 +641,46 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
+
 Request(body)
 ^^^^^^^^^^^^^
 
-+--------+--------+------------------------+
-| Name   | Type   | Description            |
-+========+========+========================+
-| action | string | ``start`` or ``stop``. |
-+--------+--------+------------------------+
++-----------+---------+----------------------------------------------------------------------+
+| Name      | Type    | Description                                                          |
++===========+=========+======================================================================+
+| name      | string  | component name. must be unique in the process.                       |
++-----------+---------+----------------------------------------------------------------------+
+| core      | integer | core id.                                                             |
++-----------+---------+----------------------------------------------------------------------+
+| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
++-----------+---------+----------------------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: console
 
-    {"action": "start"}
+    curl -X POST -H 'application/json' \
+    -d '{"name": "forwarder1", "core": 12, "type": "forward"}' \
+    http://127.0.0.1:7777/v1/vfs/1/components
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``PUT`` request.
+There is no body content for the response of a successful ``POST`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-action is ``start``
-
-.. code-block:: yaml
-
-    sec {client_id};forward
-
-action is ``stop``
-
-.. code-block:: yaml
+.. code-block:: console
 
-    sec {client_id};stop
+    sec {client_id};component start {name} {core} {type}
 
 
-PUT /v1/nfvs/{client_id}/ports
-------------------------------
+DELETE /v1/vfs/{sec id}/components/{name}
+-----------------------------------------
 
-Add or Delete port.
+Stop the component.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -683,48 +688,39 @@ Add or Delete port.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
-
-Request(body)
-^^^^^^^^^^^^^
-
-+--------+--------+---------------------------------------------------------------+
-| Name   | Type   | Description                                                   |
-+========+========+===============================================================+
-| action | string | ``add`` or ``del``.                                           |
-+--------+--------+---------------------------------------------------------------+
-| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
-+--------+--------+---------------------------------------------------------------+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
-
-    {"action": "add", "port": "vhost:0"}
+.. code-block:: console
 
+    curl -X DELETE -H 'application/json' \
+    http://127.0.0.1:7777/v1/vfs/1/components/forwarder1
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``PUT`` request.
+There is no body content for the response of a successful ``POST`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-    sec {client_id};{action} {interface_type} {interface_id}
+    sec {client_id};component stop {name}
 
 
-PUT /v1/nfvs/{client_id}/patches
---------------------------------
+PUT /v1/vfs/{client_id}/components/{name}/ports
+-----------------------------------------------
 
-Add a patch.
+Add or Delete port to the component.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -732,29 +728,56 @@ Add a patch.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
++---------+---------+-----------------------------------------------------------------+
+| Name    | Type    | Description                                                     |
++=========+=========+=================================================================+
+| action  | string  | ``attach`` or ``detach``.                                       |
++---------+---------+-----------------------------------------------------------------+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
++---------+---------+-----------------------------------------------------------------+
+| dir     | string  | ``rx`` or ``tx``.                                               |
++---------+---------+-----------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
++---------+---------+-----------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+----------------------------------------------------------+
+| Name      | Type    | Description                                              |
++===========+=========+==========================================================+
+| operation | string  | ``add``, ``del`` or ``none``.                            |
++-----------+---------+----------------------------------------------------------+
+| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
++-----------+---------+----------------------------------------------------------+
+| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
++-----------+---------+----------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: console
+
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "attach", "port": "vhost:1", "dir": "rx", \
+         "vlan": {"operation": "add", "id": 677, "pcp": 0}}' \
+    http://127.0.0.1:7777/v1/vfs/1/components/forwarder1/ports
+
+.. code-block:: console
 
-    {"src": "vhost:0", "dst": "ring:0"}
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "detach", "port": "vhost:0", "dir": "tx"} \
+    http://127.0.0.1:7777/v1/vfs/1/components/forwarder1/ports
 
 Response
 ^^^^^^^^
@@ -764,15 +787,23 @@ There is no body content for the response of a successful ``PUT`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
+action is ``attach``
+
 .. code-block:: console
 
-    sec {client_id};patch {src} {dst}
+    sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
 
+action is ``detach``
 
-DELETE /v1/nfvs/{client_id}/patches
------------------------------------
+.. code-block:: console
 
-Reset patches.
+    sec {client_id};port del {port} {dir} {name}
+
+
+PUT /v1/vfs/{sec id}/classifier_table
+-------------------------------------
+
+Set or Unset classifier table.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -786,44 +817,56 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-Response
-^^^^^^^^
+Request(body)
+^^^^^^^^^^^^^
 
-There is no body content for the response of a successful ``DELETE`` request.
++-------------+-----------------+----------------------------------------------------+
+| Name        | Type            | Description                                        |
++=============+=================+====================================================+
+| action      | string          | ``add`` or ``del``.                                |
++-------------+-----------------+----------------------------------------------------+
+| type        | string          | ``mac`` or ``vlan``.                               |
++-------------+-----------------+----------------------------------------------------+
+| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
++-------------+-----------------+----------------------------------------------------+
+| mac_address | string          | mac address.                                       |
++-------------+-----------------+----------------------------------------------------+
+| port        | string          | port id.                                           |
++-------------+-----------------+----------------------------------------------------+
 
-Equivalent CLI command
-^^^^^^^^^^^^^^^^^^^^^^
+Request example
+^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-    sec {client_id};patch reset
-
-
-API for spp_primary
-===================
-
-GET /v1/primary/status
-----------------------
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "add", "type": "mac", "mac_address": "FA:16:3E:7D:CC:35", \
+       "port": "ring:0"}' \
+    http://127.0.0.1:7777/v1/vfs/1/classifier_table
 
-Show statistical information.
+.. code-block:: console
 
-* Normal response codes: 200
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "del", "type": "vlan", "vlan": 475, \
+       "mac_address": "FA:16:3E:7D:CC:35", "port": "ring:0"}' \
+    http://127.0.0.1:7777/v1/vfs/1/classifier_table
 
 Response
 ^^^^^^^^
 
-There is no data at the moment. The statistical information will be returned
-when ``spp_primary`` implements it.
+There is no body content for the response of a successful ``PUT`` request.
 
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
 
-DELETE /v1/primary/status
--------------------------
+type is ``mac``
 
-Clear statistical information.
+.. code-block:: console
 
-* Normal response codes: 204
+    classifier_table {action} mac {mac_address} {port}
 
-Response
-^^^^^^^^
+type is ``vlan``
 
-There is no body content for the response of a successful ``PUT`` request.
+.. code-block:: console
+
+    classifier_table {action} vlan {vlan} {mac_address} {port}
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 12/13] docs: correct directives of spp-ctl
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (10 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 11/13] docs: add request examples of spp-ctl oda
@ 2018-10-05  1:37   ` oda
  2018-10-05  1:37   ` [spp] [PATCH v3 13/13] docs: add labels and captions for tables oda
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Some of code-block directives are incorrect, and section headers are not
applied with the documentation guidelines of DPDK. This patch is to
correct following directives.

* Mistaken code-block directives.

* Section headers.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 74 +++++++++++++--------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
index 285526e..3263934 100644
--- a/docs/guides/spp-ctl/api-reference.rst
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -3,17 +3,16 @@
 
 .. _spp_ctl_api_ref:
 
-=============
 API Reference
 =============
 
 Overview
-========
+--------
 
 ``spp-ctl`` provides simple REST like API. It supports http only, not https.
 
 Request and Response
---------------------
+~~~~~~~~~~~~~~~~~~~~
 
 Request body is JSON format.
 It is accepted both ``text/plain`` and ``application/json``
@@ -26,7 +25,8 @@ If a request fails, the response is a text which shows error reason
 and the content-type is ``text/plain``.
 
 Error code
-----------
+~~~~~~~~~~
+
 
 ``spp-ctl`` does basic syntax and lexical check of a request.
 
@@ -42,10 +42,10 @@ Error code
 
 
 API independent of the process type
-===================================
+-----------------------------------
 
 GET /v1/processes
------------------
+~~~~~~~~~~~~~~~~~
 
 Show the SPP processes connected to the ``spp-ctl``.
 
@@ -77,7 +77,7 @@ process object:
 Response example
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: json
 
     [
       {
@@ -95,10 +95,10 @@ Response example
 
 
 API for spp_primary
-===================
+-------------------
 
 GET /v1/primary/status
-----------------------
+~~~~~~~~~~~~~~~~~~~~~~
 
 Show statistical information.
 
@@ -120,7 +120,7 @@ when ``spp_primary`` implements it.
 
 
 DELETE /v1/primary/status
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Clear statistical information.
 
@@ -141,10 +141,10 @@ There is no body content for the response of a successful ``PUT`` request.
 
 
 API for spp_nfv/spp_vm
-======================
+----------------------
 
 GET /v1/nfvs/{client_id}
-------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~
 
 Get the information of the ``spp_nfv`` or ``spp_vm`` process.
 
@@ -196,7 +196,7 @@ patch objest
 Response example
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: json
 
     {
       "client-id": 1,
@@ -217,13 +217,13 @@ Response example
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};status
 
 
 PUT /v1/nfvs/{client_id}/forward
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Start or Stop forwarding.
 
@@ -267,19 +267,19 @@ Equivalent CLI command
 
 action is ``start``
 
-.. code-block:: yaml
+.. code-block:: none
 
     sec {client_id};forward
 
 action is ``stop``
 
-.. code-block:: yaml
+.. code-block:: none
 
     sec {client_id};stop
 
 
 PUT /v1/nfvs/{client_id}/ports
-------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Add or Delete port.
 
@@ -323,13 +323,13 @@ There is no body content for the response of a successful ``PUT`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};{action} {interface_type} {interface_id}
 
 
 PUT /v1/nfvs/{client_id}/patches
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Add a patch.
 
@@ -373,13 +373,13 @@ There is no body content for the response of a successful ``PUT`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};patch {src} {dst}
 
 
 DELETE /v1/nfvs/{client_id}/patches
------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Reset patches.
 
@@ -411,16 +411,16 @@ There is no body content for the response of a successful ``DELETE`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};patch reset
 
 
 API for spp_vf
-==============
+--------------
 
 GET /v1/vfs/{client_id}
------------------------
+~~~~~~~~~~~~~~~~~~~~~~~
 
 Get the information of the ``spp_vf`` process.
 
@@ -512,7 +512,7 @@ classifier table:
 Response example
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: json
 
     {
       "client-id": 1,
@@ -619,13 +619,13 @@ The component which type is ``unused`` is to indicate unused core.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};status
 
 
 POST /v1/vfs/{client_id}/components
------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Start the component.
 
@@ -672,13 +672,13 @@ There is no body content for the response of a successful ``POST`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};component start {name} {core} {type}
 
 
 DELETE /v1/vfs/{sec id}/components/{name}
------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Stop the component.
 
@@ -712,13 +712,13 @@ There is no body content for the response of a successful ``POST`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};component stop {name}
 
 
 PUT /v1/vfs/{client_id}/components/{name}/ports
------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Add or Delete port to the component.
 
@@ -789,19 +789,19 @@ Equivalent CLI command
 
 action is ``attach``
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
 
 action is ``detach``
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};port del {port} {dir} {name}
 
 
 PUT /v1/vfs/{sec id}/classifier_table
--------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Set or Unset classifier table.
 
@@ -861,12 +861,12 @@ Equivalent CLI command
 
 type is ``mac``
 
-.. code-block:: console
+.. code-block:: none
 
     classifier_table {action} mac {mac_address} {port}
 
 type is ``vlan``
 
-.. code-block:: console
+.. code-block:: none
 
     classifier_table {action} vlan {vlan} {mac_address} {port}
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v3 13/13] docs: add labels and captions for tables
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
                     ` (11 preceding siblings ...)
  2018-10-05  1:37   ` [spp] [PATCH v3 12/13] docs: correct directives " oda
@ 2018-10-05  1:37   ` oda
  12 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  1:37 UTC (permalink / raw)
  To: spp

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

For documentation, DPDK's documentation Guidelines[1] requires that
all of tables have labels and captions.

[1] https://doc.dpdk.org/guides/contributing/documentation.html

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 525 ++++++++++++++++----------
 1 file changed, 327 insertions(+), 198 deletions(-)

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
index 3263934..23bc8db 100644
--- a/docs/guides/spp-ctl/api-reference.rst
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -30,15 +30,20 @@ Error code
 
 ``spp-ctl`` does basic syntax and lexical check of a request.
 
-+------+----------------------------------------------------------------+
-| Error| Description                                                    |
-+======+================================================================+
-| 400  | Syntax or lexical error, or SPP returns error for the request. |
-+------+----------------------------------------------------------------+
-| 404  | URL is not supported, or no SPP process of client-id in a URL. |
-+------+----------------------------------------------------------------+
-| 500  | System error occured in ``spp-ctl``.                           |
-+------+----------------------------------------------------------------+
+.. _table_spp_ctl_error_codes:
+
+.. table:: Error codes in spp-ctl.
+
+    +-------+----------------------------------------------------------------+
+    | Error | Description                                                    |
+    |       |                                                                |
+    +=======+================================================================+
+    | 400   | Syntax or lexical error, or SPP returns error for the request. |
+    +-------+----------------------------------------------------------------+
+    | 404   | URL is not supported, or no SPP process of client-id in a URL. |
+    +-------+----------------------------------------------------------------+
+    | 500   | System error occured in ``spp-ctl``.                           |
+    +-------+----------------------------------------------------------------+
 
 
 API independent of the process type
@@ -66,13 +71,18 @@ An array of process objects.
 
 process object:
 
-+-----------+---------+---------------------------------------------------------------+
-| Name      | Type    | Description                                                   |
-+===========+=========+===============================================================+
-| type      | string  | process type. one of ``primary``, ``vf`` or ``nfv``.          |
-+-----------+---------+---------------------------------------------------------------+
-| client-id | integer | client id. if type is ``primary`` this member does not exist. |
-+-----------+---------+---------------------------------------------------------------+
+.. _table_spp_ctl_processes:
+
+.. table:: Response params of getting processes info.
+
+    +-----------+---------+-----------------------------------------------------------------+
+    | Name      | Type    | Description                                                     |
+    |           |         |                                                                 |
+    +===========+=========+=================================================================+
+    | type      | string  | process type. one of ``primary``, ``nfv`` or ``vf``.            |
+    +-----------+---------+-----------------------------------------------------------------+
+    | client-id | integer | client id. if type is ``primary`` this member does not exist.   |
+    +-----------+---------+-----------------------------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -154,11 +164,16 @@ Get the information of the ``spp_nfv`` or ``spp_vm`` process.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_nfvs_get:
+
+.. table:: Request parameter for getting spp_nfv or spp_vm info.
+
+    +-----------+---------+-------------------------------------+
+    | Name      | Type    | Description                         |
+    |           |         |                                     |
+    +===========+=========+=====================================+
+    | client_id | integer | client id.                          |
+    +-----------+---------+-------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -171,27 +186,37 @@ Request example
 Response
 ^^^^^^^^
 
-+-----------+---------+-------------------------------------------+
-| Name      | Type    | Description                               |
-+===========+=========+===========================================+
-| client-id | integer | client id.                                |
-+-----------+---------+-------------------------------------------+
-| status    | string  | ``Running`` or ``Idle``.                  |
-+-----------+---------+-------------------------------------------+
-| ports     | array   | an array of port ids used by the process. |
-+-----------+---------+-------------------------------------------+
-| patches   | array   | an array of patches.                      |
-+-----------+---------+-------------------------------------------+
+.. _table_spp_ctl_spp_nfv_res:
+
+.. table:: Response params of getting spp_nfv or spp_vm info.
+
+    +-----------+---------+---------------------------------------------+
+    | Name      | Type    | Description                                 |
+    |           |         |                                             |
+    +===========+=========+=============================================+
+    | client-id | integer | client id.                                  |
+    +-----------+---------+---------------------------------------------+
+    | status    | string  | ``Running`` or ``Idle``.                    |
+    +-----------+---------+---------------------------------------------+
+    | ports     | array   | an array of port ids used by the process.   |
+    +-----------+---------+---------------------------------------------+
+    | patches   | array   | an array of patches.                        |
+    +-----------+---------+---------------------------------------------+
 
 patch objest
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
+.. _table_spp_ctl_patch_spp_nfv:
+
+.. table:: Attributes of patch of spp_nfv or spp_vm.
+
+    +------+--------+----------------------------------------------+
+    | Name | Type   | Description                                  |
+    |      |        |                                              |
+    +======+========+==============================================+
+    | src  | string | source port id.                              |
+    +------+--------+----------------------------------------------+
+    | dst  | string | destination port id.                         |
+    +------+--------+----------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -233,11 +258,16 @@ Start or Stop forwarding.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_forward_get:
+
+.. table:: Request params of forward of spp_nfv or spp_vm.
+
+    +-----------+---------+---------------------------------+
+    | Name      | Type    | Description                     |
+    |           |         |                                 |
+    +===========+=========+=================================+
+    | client_id | integer | client id.                      |
+    +-----------+---------+---------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -251,11 +281,16 @@ Request example
 Request(body)
 ^^^^^^^^^^^^^
 
-+--------+--------+------------------------+
-| Name   | Type   | Description            |
-+========+========+========================+
-| action | string | ``start`` or ``stop``. |
-+--------+--------+------------------------+
+.. _table_spp_ctl_spp_nfv_forward_get_body:
+
+.. table:: Request body params of forward of spp_nfv or spp_vm.
+
+    +--------+--------+-------------------------------------+
+    | Name   | Type   | Description                         |
+    |        |        |                                     |
+    +========+========+=====================================+
+    | action | string | ``start`` or ``stop``.              |
+    +--------+--------+-------------------------------------+
 
 Response
 ^^^^^^^^
@@ -289,22 +324,32 @@ Add or Delete port.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_ports_get:
+
+.. table:: Request params of ports of spp_nfv or spp_vm.
+
+    +-----------+---------+--------------------------------+
+    | Name      | Type    | Description                    |
+    |           |         |                                |
+    +===========+=========+================================+
+    | client_id | integer | client id.                     |
+    +-----------+---------+--------------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+--------+--------+---------------------------------------------------------------+
-| Name   | Type   | Description                                                   |
-+========+========+===============================================================+
-| action | string | ``add`` or ``del``.                                           |
-+--------+--------+---------------------------------------------------------------+
-| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
-+--------+--------+---------------------------------------------------------------+
+.. _table_spp_ctl_spp_nfv_ports_get_body:
+
+.. table:: Request body params of ports of spp_nfv or spp_vm.
+
+    +--------+--------+---------------------------------------------------------------+
+    | Name   | Type   | Description                                                   |
+    |        |        |                                                               |
+    +========+========+===============================================================+
+    | action | string | ``add`` or ``del``.                                           |
+    +--------+--------+---------------------------------------------------------------+
+    | port   | string | port id. port id is the form {interface_type}:{interface_id}. |
+    +--------+--------+---------------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -339,22 +384,32 @@ Add a patch.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_patches_get:
+
+.. table:: Request params of patches of spp_nfv or spp_vm.
+
+    +-----------+---------+---------------------------------+
+    | Name      | Type    | Description                     |
+    |           |         |                                 |
+    +===========+=========+=================================+
+    | client_id | integer | client id.                      |
+    +-----------+---------+---------------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
+.. _table_spp_ctl_spp_nfv_ports_patches_body:
+
+.. table:: Request body params of patches of spp_nfv or spp_vm.
+
+    +------+--------+------------------------------------+
+    | Name | Type   | Description                        |
+    |      |        |                                    |
+    +======+========+====================================+
+    | src  | string | source port id.                    |
+    +------+--------+------------------------------------+
+    | dst  | string | destination port id.               |
+    +------+--------+------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -389,11 +444,16 @@ Reset patches.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_del_patches:
+
+.. table:: Request params of deleting patches of spp_nfv or spp_vm.
+
+    +-----------+---------+---------------------------------------+
+    | Name      | Type    | Description                           |
+    |           |         |                                       |
+    +===========+=========+=======================================+
+    | client_id | integer | client id.                            |
+    +-----------+---------+---------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -430,11 +490,16 @@ Get the information of the ``spp_vf`` process.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_vfs_get:
+
+.. table:: Request parameter for getting spp_vf.
+
+    +-----------+---------+--------------------------+
+    | Name      | Type    | Description              |
+    |           |         |                          |
+    +===========+=========+==========================+
+    | client_id | integer | client id.               |
+    +-----------+---------+--------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -447,67 +512,92 @@ Request example
 Response
 ^^^^^^^^
 
-+------------------+---------+-----------------------------------------------+
-| Name             | Type    | Description                                   |
-+==================+=========+===============================================+
-| client-id        | integer | client id.                                    |
-+------------------+---------+-----------------------------------------------+
-| ports            | array   | an array of port ids used by the process.     |
-+------------------+---------+-----------------------------------------------+
-| components       | array   | an array of component objects in the process. |
-+------------------+---------+-----------------------------------------------+
-| classifier_table | array   | an array of classifier tables in the process. |
-+------------------+---------+-----------------------------------------------+
+.. _table_spp_ctl_spp_vf_res:
+
+.. table:: Response params of getting spp_vf.
+
+    +------------------+---------+-----------------------------------------------+
+    | Name             | Type    | Description                                   |
+    |                  |         |                                               |
+    +==================+=========+===============================================+
+    | client-id        | integer | client id.                                    |
+    +------------------+---------+-----------------------------------------------+
+    | ports            | array   | an array of port ids used by the process.     |
+    +------------------+---------+-----------------------------------------------+
+    | components       | array   | an array of component objects in the process. |
+    +------------------+---------+-----------------------------------------------+
+    | classifier_table | array   | an array of classifier tables in the process. |
+    +------------------+---------+-----------------------------------------------+
 
 component object:
 
-+---------+---------+---------------------------------------------------------------------+
-| Name    | Type    | Description                                                         |
-+=========+=========+=====================================================================+
-| core    | integer | core id running on the component                                    |
-+---------+---------+---------------------------------------------------------------------+
-| name    | string  | an array of port ids used by the process.                           |
-+---------+---------+---------------------------------------------------------------------+
-| type    | string  | an array of component objects in the process.                       |
-+---------+---------+---------------------------------------------------------------------+
-| rx_port | array   | an array of port objects connected to the rx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
-| tx_port | array   | an array of port objects connected to the tx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_res_comp:
+
+.. table:: Component objects of getting spp_vf.
+
+    +---------+---------+---------------------------------------------------------------------+
+    | Name    | Type    | Description                                                         |
+    |         |         |                                                                     |
+    +=========+=========+=====================================================================+
+    | core    | integer | core id running on the component                                    |
+    +---------+---------+---------------------------------------------------------------------+
+    | name    | string  | an array of port ids used by the process.                           |
+    +---------+---------+---------------------------------------------------------------------+
+    | type    | string  | an array of component objects in the process.                       |
+    +---------+---------+---------------------------------------------------------------------+
+    | rx_port | array   | an array of port objects connected to the rx side of the component. |
+    +---------+---------+---------------------------------------------------------------------+
+    | tx_port | array   | an array of port objects connected to the tx side of the component. |
+    +---------+---------+---------------------------------------------------------------------+
 
 port object:
 
-+---------+---------+---------------------------------------------------------------+
-| Name    | Type    | Description                                                   |
-+=========+=========+===============================================================+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
-+---------+---------+---------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port.                  |
-+---------+---------+---------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_res_port:
+
+.. table:: Port objects of getting spp_vf.
+
+    +---------+---------+---------------------------------------------------------------+
+    | Name    | Type    | Description                                                   |
+    |         |         |                                                               |
+    +=========+=========+===============================================================+
+    | port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
+    +---------+---------+---------------------------------------------------------------+
+    | vlan    | object  | vlan operation which is applied to the port.                  |
+    +---------+---------+---------------------------------------------------------------+
 
 vlan object:
 
-+-----------+---------+-------------------------------+
-| Name      | Type    | Description                   |
-+===========+=========+===============================+
-| operation | string  | ``add``, ``del`` or ``none``. |
-+-----------+---------+-------------------------------+
-| id        | integer | vlan id.                      |
-+-----------+---------+-------------------------------+
-| pcp       | integer | vlan pcp.                     |
-+-----------+---------+-------------------------------+
+.. _table_spp_ctl_spp_vf_res_vlan:
+
+.. table:: Vlan objects of getting spp_vf.
+
+    +-----------+---------+-------------------------------+
+    | Name      | Type    | Description                   |
+    |           |         |                               |
+    +===========+=========+===============================+
+    | operation | string  | ``add``, ``del`` or ``none``. |
+    +-----------+---------+-------------------------------+
+    | id        | integer | vlan id.                      |
+    +-----------+---------+-------------------------------+
+    | pcp       | integer | vlan pcp.                     |
+    +-----------+---------+-------------------------------+
 
 classifier table:
 
-+-----------+--------+------------------------------------------------------------+
-| Name      | Type   | Description                                                |
-+===========+========+============================================================+
-| type      | string | ``mac`` or ``vlan``.                                       |
-+-----------+--------+------------------------------------------------------------+
-| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
-+-----------+--------+------------------------------------------------------------+
-| port      | string | port id applied to classify.                               |
-+-----------+--------+------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_res_vlan:
+
+.. table:: Vlan objects of getting spp_vf.
+
+    +-----------+--------+------------------------------------------------------------+
+    | Name      | Type   | Description                                                |
+    |           |        |                                                            |
+    +===========+========+============================================================+
+    | type      | string | ``mac`` or ``vlan``.                                       |
+    +-----------+--------+------------------------------------------------------------+
+    | value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
+    +-----------+--------+------------------------------------------------------------+
+    | port      | string | port id applied to classify.                               |
+    +-----------+--------+------------------------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -635,25 +725,34 @@ Start the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_vf_components:
+
+.. table:: Request params of components of spp_vf.
+
+    +-----------+---------+-------------+
+    | Name      | Type    | Description |
+    +===========+=========+=============+
+    | client_id | integer | client id.  |
+    +-----------+---------+-------------+
 
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+-----------+---------+----------------------------------------------------------------------+
-| Name      | Type    | Description                                                          |
-+===========+=========+======================================================================+
-| name      | string  | component name. must be unique in the process.                       |
-+-----------+---------+----------------------------------------------------------------------+
-| core      | integer | core id.                                                             |
-+-----------+---------+----------------------------------------------------------------------+
-| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
-+-----------+---------+----------------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_components_res:
+
+.. table:: Response params of components of spp_vf.
+
+    +-----------+---------+----------------------------------------------------------------------+
+    | Name      | Type    | Description                                                          |
+    |           |         |                                                                      |
+    +===========+=========+======================================================================+
+    | name      | string  | component name. must be unique in the process.                       |
+    +-----------+---------+----------------------------------------------------------------------+
+    | core      | integer | core id.                                                             |
+    +-----------+---------+----------------------------------------------------------------------+
+    | type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
+    +-----------+---------+----------------------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -688,13 +787,18 @@ Stop the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
+.. _table_spp_ctl_spp_vf_del:
+
+.. table:: Request params of deleting component of spp_vf.
+
+    +-----------+---------+---------------------------------+
+    | Name      | Type    | Description                     |
+    |           |         |                                 |
+    +===========+=========+=================================+
+    | client_id | integer | client id.                      |
+    +-----------+---------+---------------------------------+
+    | name      | string  | component name.                 |
+    +-----------+---------+---------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -728,40 +832,55 @@ Add or Delete port to the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
+.. _table_spp_ctl_spp_vf_comp_port:
+
+.. table:: Request params for ports of component of spp_vf.
+
+    +-----------+---------+---------------------------+
+    | Name      | Type    | Description               |
+    |           |         |                           |
+    +===========+=========+===========================+
+    | client_id | integer | client id.                |
+    +-----------+---------+---------------------------+
+    | name      | string  | component name.           |
+    +-----------+---------+---------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+---------+---------+-----------------------------------------------------------------+
-| Name    | Type    | Description                                                     |
-+=========+=========+=================================================================+
-| action  | string  | ``attach`` or ``detach``.                                       |
-+---------+---------+-----------------------------------------------------------------+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
-+---------+---------+-----------------------------------------------------------------+
-| dir     | string  | ``rx`` or ``tx``.                                               |
-+---------+---------+-----------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
-+---------+---------+-----------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_comp_port_body:
+
+.. table:: Request body params for ports of component of spp_vf.
+
+    +---------+---------+-----------------------------------------------------------------+
+    | Name    | Type    | Description                                                     |
+    |         |         |                                                                 |
+    +=========+=========+=================================================================+
+    | action  | string  | ``attach`` or ``detach``.                                       |
+    +---------+---------+-----------------------------------------------------------------+
+    | port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
+    +---------+---------+-----------------------------------------------------------------+
+    | dir     | string  | ``rx`` or ``tx``.                                               |
+    +---------+---------+-----------------------------------------------------------------+
+    | vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
+    +---------+---------+-----------------------------------------------------------------+
 
 vlan object:
 
-+-----------+---------+----------------------------------------------------------+
-| Name      | Type    | Description                                              |
-+===========+=========+==========================================================+
-| operation | string  | ``add``, ``del`` or ``none``.                            |
-+-----------+---------+----------------------------------------------------------+
-| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
-+-----------+---------+----------------------------------------------------------+
-| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
-+-----------+---------+----------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_comp_port_body_vlan:
+
+.. table:: Request body params for vlan ports of component of spp_vf.
+
+    +-----------+---------+----------------------------------------------------------+
+    | Name      | Type    | Description                                              |
+    |           |         |                                                          |
+    +===========+=========+==========================================================+
+    | operation | string  | ``add``, ``del`` or ``none``.                            |
+    +-----------+---------+----------------------------------------------------------+
+    | id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
+    +-----------+---------+----------------------------------------------------------+
+    | pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
+    +-----------+---------+----------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -811,28 +930,38 @@ Set or Unset classifier table.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_vf_cls_table:
+
+.. table:: Request params for classifier_table of spp_vf.
+
+    +-----------+---------+---------------------------+
+    | Name      | Type    | Description               |
+    |           |         |                           |
+    +===========+=========+===========================+
+    | client_id | integer | client id.                |
+    +-----------+---------+---------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+-------------+-----------------+----------------------------------------------------+
-| Name        | Type            | Description                                        |
-+=============+=================+====================================================+
-| action      | string          | ``add`` or ``del``.                                |
-+-------------+-----------------+----------------------------------------------------+
-| type        | string          | ``mac`` or ``vlan``.                               |
-+-------------+-----------------+----------------------------------------------------+
-| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
-+-------------+-----------------+----------------------------------------------------+
-| mac_address | string          | mac address.                                       |
-+-------------+-----------------+----------------------------------------------------+
-| port        | string          | port id.                                           |
-+-------------+-----------------+----------------------------------------------------+
+.. _table_spp_ctl_spp_vf_cls_table_body:
+
+.. table:: Request body params for classifier_table of spp_vf.
+
+    +-------------+-----------------+----------------------------------------------------+
+    | Name        | Type            | Description                                        |
+    |             |                 |                                                    |
+    +=============+=================+====================================================+
+    | action      | string          | ``add`` or ``del``.                                |
+    +-------------+-----------------+----------------------------------------------------+
+    | type        | string          | ``mac`` or ``vlan``.                               |
+    +-------------+-----------------+----------------------------------------------------+
+    | vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
+    +-------------+-----------------+----------------------------------------------------+
+    | mac_address | string          | mac address.                                       |
+    +-------------+-----------------+----------------------------------------------------+
+    | port        | string          | port id.                                           |
+    +-------------+-----------------+----------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API
  2018-09-12 23:25 [spp] [PATCH] spp-ctl: SPP controller with Web API Itsuro ODA
  2018-09-18 10:00 ` Yasufumi Ogawa
  2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
@ 2018-10-05  3:57 ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 01/14] docs: add overview of spp-ctl oda
                     ` (14 more replies)
  2 siblings, 15 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

spp-ctl is a SPP controller with a REST like web API.

spp-ctl maintains the connections from the SPP processes and at
the same time exposes the API for the user to request for the
SPP processes.

Background and motivation:

Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
Therefore, spp-agent, a component of networking-spp which make
SPP available on OpenStack environment, implements SPP controller
in itself. (see. https://github.com/openstack/networking-spp )

Either CLI or spp-agent, there is a problem that other people can
not request to SPP processes while using. spp-ctl is invented to
solve this problem.

Both CLI and spp-agent can be used spp-ctl to request SPP
processes instead of owning contoroller itself. In that case,
multiple people can request to SPP processes at the same time.
Note that spp-agent has a plan to change to use spp-ctl.
It is also available not using CLI but requesting spp-ctl
directly.

---
v4:
* fix incorrect URL

v3:
* reflect the points which Yasufumi indicated.

v2:
* divide a patch

Itsuro Oda (10):
  docs: add overview of spp-ctl
  docs: add API reference of spp-ctl
  docs: add index of spp-ctl
  project: add requirements.txt for spp-ctl
  docs: add spp-ctl to index of doc root
  spp-ctl: add entry point
  spp-ctl: add Controller class
  spp-ctl: add web API handler
  spp-ctl: add spp command interfaces
  spp-ctl: fix incorrect URL

Yasufumi Ogawa (4):
  spp-ctl: update parsing spp_nfv status
  docs: add request examples of spp-ctl
  docs: correct directives of spp-ctl
  docs: add labels and captions for tables

 docs/guides/index.rst                 |    1 +
 docs/guides/spp-ctl/api-reference.rst | 1001 +++++++++++++++++++++++++
 docs/guides/spp-ctl/index.rst         |   14 +
 docs/guides/spp-ctl/overview.rst      |  119 +++
 requirements.txt                      |    4 +
 src/spp-ctl/spp-ctl                   |   11 +
 src/spp-ctl/spp_ctl.py                |  158 ++++
 src/spp-ctl/spp_proc.py               |  187 +++++
 src/spp-ctl/spp_webapi.py             |  431 +++++++++++
 9 files changed, 1926 insertions(+)
 create mode 100644 docs/guides/spp-ctl/api-reference.rst
 create mode 100644 docs/guides/spp-ctl/index.rst
 create mode 100644 docs/guides/spp-ctl/overview.rst
 create mode 100644 requirements.txt
 create mode 100644 src/spp-ctl/spp-ctl
 create mode 100644 src/spp-ctl/spp_ctl.py
 create mode 100644 src/spp-ctl/spp_proc.py
 create mode 100644 src/spp-ctl/spp_webapi.py

-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 01/14] docs: add overview of spp-ctl
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 02/14] docs: add API reference " oda
                     ` (13 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Add overview section of spp-ctl.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/overview.rst | 119 +++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)
 create mode 100644 docs/guides/spp-ctl/overview.rst

diff --git a/docs/guides/spp-ctl/overview.rst b/docs/guides/spp-ctl/overview.rst
new file mode 100644
index 0000000..4c7181c
--- /dev/null
+++ b/docs/guides/spp-ctl/overview.rst
@@ -0,0 +1,119 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+.. _spp_ctl_overview:
+
+spp-ctl
+=======
+
+Overview
+--------
+
+``spp-ctl`` is an alternative SPP controller which provides a REST-like
+APIs.
+
+It maintains connections between SPP processes for managing them
+via the REST-like APIs for users or other processes.
+
+Background and Motivation
+-------------------------
+
+``spp-ctl`` is designed for supporting to manage SPP from several
+controllers.
+
+There are some usecases where SPP is managed from other
+process without user inputs.
+For example, you need a intermediate process if you think of using SPP
+from a framework, such as OpenStack.
+`networking-spp
+<https://github.com/openstack/networking-spp>`_
+is a Neutron ML2 plugin for SPP and `spp-agent` works as a
+SPP controller.
+
+It is a problem to prepare several controllers for each of usages for
+CLI, OpenStack or others because it would be hard to maintain for
+updates.
+
+Spp-ctl is designed to solve the problem of several controllers with
+application-independent REST APIs.
+You can manage SPP by requesting via this APIs.
+
+Architecture
+------------
+
+The design goal of spp-ctl is to be as simple as possible.
+It is stateless.
+Basically, spp-ctl only converts API requests into commands of SPP
+processes and throws request, thouth it does syntax and lexical check
+for API requests.
+
+``spp-ctl`` adopts
+`bottle
+<https://bottlepy.org/docs/dev/>`_
+which is simple and well known as a web framework and
+`eventlet
+<http://eventlet.net/>`_
+for parallel processing.
+``spp-ctl`` accepts multiple requests at the same time and serializes them
+internally.
+
+
+Setup
+=====
+
+You are required to install Python3 and packages described in
+``requirements.txt`` via ``pip3`` for launching ``spp-ctl``.
+You might need to run ``pip3`` with ``sudo``.
+
+.. code-block:: console
+
+    $ sudo apt update
+    $ sudo apt install python3
+    $ sudo apt install python3-pip
+    $ pip3 install -r requirements.txt
+
+Usage
+-----
+
+.. code-block:: console
+
+    usage: spp-ctl [-p PRI_PORT] [-s SEC_PORT] [-a API_PORT]
+
+    optional arguments:
+      -p PRI_PORT  primary port. default is 5555.
+      -s SEC_PORT  secondary port. default is 6666.
+      -a API_PORT  web api port. default is 7777.
+
+Using systemd
+-------------
+
+`spp-ctl` is assumed to be launched as a daemon process, or managed
+by `systemd`.
+Here is a simple example of service file for systemd.
+
+::
+
+    [Unit]
+    Description = SPP Controller
+
+    [Service]
+    ExecStart = /usr/bin/python3 /path/to/spp/src/spp-ctl/spp-ctl
+    User = root
+
+
+REST APIs
+=========
+
+You can try to call ``spp-ctl`` APIs with ``curl`` command as following.
+
+.. code-block:: console
+
+    $ curl http://localhost:7777/v1/processes
+    [{"type": "primary"}, ..., {"client-id": 2, "type": "vf"}]
+    $ curl http://localhost:7777/v1/vfs/1
+    ... snip
+    $ curl -X POST http://localhost:7777/v1/vfs/1/components \
+      -d '{"core": 2, "name": "forward_0_tx", "type": "forward"}'
+
+For more details, see
+:ref:`API Reference<spp_ctl_api_ref>`.
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 02/14] docs: add API reference of spp-ctl
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
  2018-10-05  3:57   ` [spp] [PATCH v4 01/14] docs: add overview of spp-ctl oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 03/14] docs: add index " oda
                     ` (12 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Add REST API reference of spp-ctl.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 829 ++++++++++++++++++++++++++
 1 file changed, 829 insertions(+)
 create mode 100644 docs/guides/spp-ctl/api-reference.rst

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
new file mode 100644
index 0000000..4d2a641
--- /dev/null
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -0,0 +1,829 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+.. _spp_ctl_api_ref:
+
+=============
+API Reference
+=============
+
+Overview
+========
+
+``spp-ctl`` provides simple REST like API. It supports http only, not https.
+
+Request and Response
+--------------------
+
+Request body is JSON format.
+It is accepted both ``text/plain`` and ``application/json``
+for the content-type header.
+
+Response of ``GET`` is JSON format and the content-type is
+``application/json`` if the request success.
+
+If a request fails, the response is a text which shows error reason
+and the content-type is ``text/plain``.
+
+Error code
+----------
+
+``spp-ctl`` does basic syntax and lexical check of a request.
+
++------+----------------------------------------------------------------+
+| Error| Description                                                    |
++======+================================================================+
+| 400  | Syntax or lexical error, or SPP returns error for the request. |
++------+----------------------------------------------------------------+
+| 404  | URL is not supported, or no SPP process of client-id in a URL. |
++------+----------------------------------------------------------------+
+| 500  | System error occured in ``spp-ctl``.                           |
++------+----------------------------------------------------------------+
+
+
+API independent of the process type
+===================================
+
+GET /v1/processes
+-----------------
+
+Show the SPP processes connected to the ``spp-ctl``.
+
+* Normarl response codes: 200
+
+Response
+^^^^^^^^
+
+An array of process objects.
+
+process object:
+
++-----------+---------+---------------------------------------------------------------+
+| Name      | Type    | Description                                                   |
++===========+=========+===============================================================+
+| type      | string  | process type. one of ``primary``, ``vf`` or ``nfv``.          |
++-----------+---------+---------------------------------------------------------------+
+| client-id | integer | client id. if type is ``primary`` this member does not exist. |
++-----------+---------+---------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    [
+      {
+        "type": "primary"
+      },
+      {
+        "type": "vf",
+        "client-id": 1
+      },
+      {
+        "type": "nfv",
+        "client-id": 2
+      }
+    ]
+
+
+API for spp_vf
+==============
+
+GET /v1/vfs/{client_id}
+-----------------------
+
+Get the information of the ``spp_vf`` process.
+
+* Normal response codes: 200
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Response
+^^^^^^^^
+
++------------------+---------+-----------------------------------------------+
+| Name             | Type    | Description                                   |
++==================+=========+===============================================+
+| client-id        | integer | client id.                                    |
++------------------+---------+-----------------------------------------------+
+| ports            | array   | an array of port ids used by the process.     |
++------------------+---------+-----------------------------------------------+
+| components       | array   | an array of component objects in the process. |
++------------------+---------+-----------------------------------------------+
+| classifier_table | array   | an array of classifier tables in the process. |
++------------------+---------+-----------------------------------------------+
+
+component object:
+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| core    | integer | core id running on the component                                    |
++---------+---------+---------------------------------------------------------------------+
+| name    | string  | an array of port ids used by the process.                           |
++---------+---------+---------------------------------------------------------------------+
+| type    | string  | an array of component objects in the process.                       |
++---------+---------+---------------------------------------------------------------------+
+| rx_port | array   | an array of port objects connected to the rx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+| tx_port | array   | an array of port objects connected to the tx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+
+port object:
+
++---------+---------+---------------------------------------------------------------+
+| Name    | Type    | Description                                                   |
++=========+=========+===============================================================+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
++---------+---------+---------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port.                  |
++---------+---------+---------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+-------------------------------+
+| Name      | Type    | Description                   |
++===========+=========+===============================+
+| operation | string  | ``add``, ``del`` or ``none``. |
++-----------+---------+-------------------------------+
+| id        | integer | vlan id.                      |
++-----------+---------+-------------------------------+
+| pcp       | integer | vlan pcp.                     |
++-----------+---------+-------------------------------+
+
+classifier table:
+
++-----------+--------+------------------------------------------------------------+
+| Name      | Type   | Description                                                |
++===========+========+============================================================+
+| type      | string | ``mac`` or ``vlan``.                                       |
++-----------+--------+------------------------------------------------------------+
+| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
++-----------+--------+------------------------------------------------------------+
+| port      | string | port id applied to classify.                               |
++-----------+--------+------------------------------------------------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "client-id": 1,
+      "ports": [
+        "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
+      ],
+      "components": [
+        {
+          "core": 2,
+          "name": "forward_0_tx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "ring:0",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "vhost:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 3,
+          "type": "unuse"
+        },
+        {
+          "core": 4,
+          "type": "unuse"
+        },
+        {
+          "core": 5,
+          "name": "forward_1_rx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "vhost:1",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 6,
+          "name": "classifier",
+          "type": "classifier_mac",
+          "rx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:2",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 7,
+          "name": "merger",
+          "type": "merge",
+          "rx_port": [
+            {
+              "port": "ring:1",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+      ],
+      "classifier_table": [
+        {
+          "type": "mac",
+          "value": "FA:16:3E:7D:CC:35",
+          "port": "ring:0"
+        }
+      ]
+    }
+
+The component which type is ``unused`` is to indicate unused core.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};status
+
+
+POST /v1/vfs/{client_id}/components
+-----------------------------------
+
+Start the component.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-----------+---------+----------------------------------------------------------------------+
+| Name      | Type    | Description                                                          |
++===========+=========+======================================================================+
+| name      | string  | component name. must be unique in the process.                       |
++-----------+---------+----------------------------------------------------------------------+
+| core      | integer | core id.                                                             |
++-----------+---------+----------------------------------------------------------------------+
+| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
++-----------+---------+----------------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "name": "forwarder1",
+      "core": 12,
+      "type": "forward"
+    }
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``POST`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};component start {name} {core} {type}
+
+
+DELETE /v1/vfs/{sec id}/components/{name}
+-----------------------------------------
+
+Stop the component.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``POST`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};component stop {name}
+
+
+PUT /v1/vfs/{client_id}/components/{name}/ports
+-----------------------------------------------
+
+Add or Delete port to the component.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++---------+---------+-----------------------------------------------------------------+
+| Name    | Type    | Description                                                     |
++=========+=========+=================================================================+
+| action  | string  | ``attach`` or ``detach``.                                       |
++---------+---------+-----------------------------------------------------------------+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
++---------+---------+-----------------------------------------------------------------+
+| dir     | string  | ``rx`` or ``tx``.                                               |
++---------+---------+-----------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
++---------+---------+-----------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+----------------------------------------------------------+
+| Name      | Type    | Description                                              |
++===========+=========+==========================================================+
+| operation | string  | ``add``, ``del`` or ``none``.                            |
++-----------+---------+----------------------------------------------------------+
+| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
++-----------+---------+----------------------------------------------------------+
+| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
++-----------+---------+----------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "action": "attach",
+      "port": "vhost:1",
+      "dir": "rx",
+      "vlan": {
+        "operation": "add",
+        "id": 677,
+        "pcp": 0
+      }
+    }
+
+.. code-block:: yaml
+
+    {
+      "action": "detach",
+      "port": "vhost:0",
+      "dir": "tx"
+    }
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+action is ``attach``
+
+.. code-block:: console
+
+    sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
+
+action is ``detach``
+
+.. code-block:: console
+
+    sec {client_id};port del {port} {dir} {name}
+
+
+PUT /v1/vfs/{sec id}/classifier_table
+-------------------------------------
+
+Set or Unset classifier table.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++-------------+-----------------+----------------------------------------------------+
+| Name        | Type            | Description                                        |
++=============+=================+====================================================+
+| action      | string          | ``add`` or ``del``.                                |
++-------------+-----------------+----------------------------------------------------+
+| type        | string          | ``mac`` or ``vlan``.                               |
++-------------+-----------------+----------------------------------------------------+
+| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
++-------------+-----------------+----------------------------------------------------+
+| mac_address | string          | mac address.                                       |
++-------------+-----------------+----------------------------------------------------+
+| port        | string          | port id.                                           |
++-------------+-----------------+----------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "action": "add",
+      "type": "mac",
+      "mac_address": "FA:16:3E:7D:CC:35",
+      "port": "ring:0"
+    }
+
+.. code-block:: yaml
+
+    {
+      "action": "del",
+      "type": "vlan",
+      "vlan": 475,
+      "mac_address": "FA:16:3E:7D:CC:35",
+      "port": "ring:0"
+    }
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+type is ``mac``
+
+.. code-block:: console
+
+    classifier_table {action} mac {mac_address} {port}
+
+type is ``vlan``
+
+.. code-block:: console
+
+    classifier_table {action} vlan {vlan} {mac_address} {port}
+
+
+API for spp_nfv/spp_vm
+======================
+
+GET /v1/nfvs/{client_id}
+------------------------
+
+Get the information of the ``spp_nfv`` or ``spp_vm`` process.
+
+* Normal response codes: 200
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Response
+^^^^^^^^
+
++-----------+---------+-------------------------------------------+
+| Name      | Type    | Description                               |
++===========+=========+===========================================+
+| client-id | integer | client id.                                |
++-----------+---------+-------------------------------------------+
+| status    | string  | ``Running`` or ``Idle``.                  |
++-----------+---------+-------------------------------------------+
+| ports     | array   | an array of port ids used by the process. |
++-----------+---------+-------------------------------------------+
+| patches   | array   | an array of patches.                      |
++-----------+---------+-------------------------------------------+
+
+patch objest
+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
+
+Response example
+^^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {
+      "client-id": 1,
+      "status": "Running",
+      "ports": [
+        "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
+      ],
+      "patches": [
+        {
+          "src": "vhost:0", "dst": "ring:0"
+        },
+        {
+          "src": "ring:1", "dst": "vhost:1"
+        }
+      ]
+    }
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};status
+
+
+PUT /v1/nfvs/{client_id}/forward
+--------------------------------
+
+Start or Stop forwarding.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+------------------------+
+| Name   | Type   | Description            |
++========+========+========================+
+| action | string | ``start`` or ``stop``. |
++--------+--------+------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {"action": "start"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+action is ``start``
+
+.. code-block:: yaml
+
+    sec {client_id};forward
+
+action is ``stop``
+
+.. code-block:: yaml
+
+    sec {client_id};stop
+
+
+PUT /v1/nfvs/{client_id}/ports
+------------------------------
+
+Add or Delete port.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+---------------------------------------------------------------+
+| Name   | Type   | Description                                                   |
++========+========+===============================================================+
+| action | string | ``add`` or ``del``.                                           |
++--------+--------+---------------------------------------------------------------+
+| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
++--------+--------+---------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {"action": "add", "port": "vhost:0"}
+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};{action} {interface_type} {interface_id}
+
+
+PUT /v1/nfvs/{client_id}/patches
+--------------------------------
+
+Add a patch.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: yaml
+
+    {"src": "vhost:0", "dst": "ring:0"}
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};patch {src} {dst}
+
+
+DELETE /v1/nfvs/{client_id}/patches
+-----------------------------------
+
+Reset patches.
+
+* Normal response codes: 204
+* Error response codes: 400, 404
+
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``DELETE`` request.
+
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    sec {client_id};patch reset
+
+
+API for spp_primary
+===================
+
+GET /v1/primary/status
+----------------------
+
+Show statistical information.
+
+* Normal response codes: 200
+
+Response
+^^^^^^^^
+
+There is no data at the moment. The statistical information will be returned
+when ``spp_primary`` implements it.
+
+
+DELETE /v1/primary/status
+-------------------------
+
+Clear statistical information.
+
+* Normal response codes: 204
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 03/14] docs: add index of spp-ctl
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
  2018-10-05  3:57   ` [spp] [PATCH v4 01/14] docs: add overview of spp-ctl oda
  2018-10-05  3:57   ` [spp] [PATCH v4 02/14] docs: add API reference " oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 04/14] project: add requirements.txt for spp-ctl oda
                     ` (11 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Add index.rst for spp-ctl section.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/index.rst | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 docs/guides/spp-ctl/index.rst

diff --git a/docs/guides/spp-ctl/index.rst b/docs/guides/spp-ctl/index.rst
new file mode 100644
index 0000000..499bcfe
--- /dev/null
+++ b/docs/guides/spp-ctl/index.rst
@@ -0,0 +1,14 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+.. _spp_ctl_index:
+
+spp-ctl
+=======
+
+.. toctree::
+   :maxdepth: 2
+   :numbered:
+
+   overview
+   api-reference
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 04/14] project: add requirements.txt for spp-ctl
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (2 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 03/14] docs: add index " oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 05/14] docs: add spp-ctl to index of doc root oda
                     ` (10 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

`requirements.txt` is for listing python packages installed via `pip`
as below.

  $ pip install -r requirements.txt

It contains a list of packages for `spp-ctl`.

* eventlet
* bottle
* netaddr

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 requirements.txt | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 requirements.txt

diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..7d0b774
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+# spp-ctl
+eventlet
+bottle
+netaddr
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 05/14] docs: add spp-ctl to index of doc root
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (3 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 04/14] project: add requirements.txt for spp-ctl oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 06/14] spp-ctl: add entry point oda
                     ` (9 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

To be referred from index of document root, add spp-ctl to the index.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/index.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/guides/index.rst b/docs/guides/index.rst
index d0242d6..9d62b30 100644
--- a/docs/guides/index.rst
+++ b/docs/guides/index.rst
@@ -39,3 +39,4 @@ SPP documentation
    commands/index
    tools/index
    spp_vf/index
+   spp-ctl/index
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 06/14] spp-ctl: add entry point
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (4 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 05/14] docs: add spp-ctl to index of doc root oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 07/14] spp-ctl: add Controller class oda
                     ` (8 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Add `spp-ctl` launched in a terminal as the entry point. Spp-ctl is
expected to be launched from Python3.

  $ python3 /path/to/spp/src/spp-ctl/spp-ctl

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp-ctl | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 src/spp-ctl/spp-ctl

diff --git a/src/spp-ctl/spp-ctl b/src/spp-ctl/spp-ctl
new file mode 100644
index 0000000..645611b
--- /dev/null
+++ b/src/spp-ctl/spp-ctl
@@ -0,0 +1,11 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import sys
+
+from spp_ctl import main
+
+
+if __name__ == "__main__":
+    sys.exit(main())
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 07/14] spp-ctl: add Controller class
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (5 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 06/14] spp-ctl: add entry point oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 08/14] spp-ctl: add web API handler oda
                     ` (7 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Controller is main class of spp-ctl for setting up connection between
SPP processes and initializing WebServer class for receiving requests.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_ctl.py | 158 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)
 create mode 100644 src/spp-ctl/spp_ctl.py

diff --git a/src/spp-ctl/spp_ctl.py b/src/spp-ctl/spp_ctl.py
new file mode 100644
index 0000000..e168747
--- /dev/null
+++ b/src/spp-ctl/spp_ctl.py
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import eventlet
+eventlet.monkey_patch()
+
+import argparse
+import errno
+import json
+import logging
+import socket
+import subprocess
+
+import spp_proc
+import spp_webapi
+
+
+LOG = logging.getLogger(__name__)
+
+
+MSG_SIZE = 4096
+
+
+class Controller(object):
+
+    def __init__(self, pri_port, sec_port, api_port):
+        self.web_server = spp_webapi.WebServer(self, api_port)
+        self.procs = {}
+        self.init_connection(pri_port, sec_port)
+
+    def start(self):
+        self.web_server.start()
+
+    def init_connection(self, pri_port, sec_port):
+        self.pri_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.pri_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.pri_sock.bind(('127.0.0.1', pri_port))
+        self.pri_sock.listen(1)
+        self.primary_listen_thread = eventlet.greenthread.spawn(
+            self.accept_primary)
+
+        self.sec_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sec_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.sec_sock.bind(('127.0.0.1', sec_port))
+        self.sec_sock.listen(1)
+        self.secondary_listen_thread = eventlet.greenthread.spawn(
+            self.accept_secondary)
+
+    def accept_primary(self):
+        while True:
+            conn, _ = self.pri_sock.accept()
+            proc = self.procs.get(spp_proc.ID_PRIMARY)
+            if proc is not None:
+                LOG.warning("spp_primary reconnect !")
+                with proc.sem:
+                    try:
+                        proc.conn.close()
+                    except Exception:
+                        pass
+                    proc.conn = conn
+                # NOTE: when spp_primary restart, all secondarys must be
+                # restarted. this is out of controle of spp-ctl.
+            else:
+                LOG.info("primary connected.")
+                self.procs[spp_proc.ID_PRIMARY] = spp_proc.PrimaryProc(conn)
+
+    def accept_secondary(self):
+        while True:
+            conn, _ = self.sec_sock.accept()
+            LOG.debug("sec accepted: get process id")
+            proc = self._get_proc(conn)
+            if proc is None:
+                LOG.error("get process id failed")
+                conn.close()
+                continue
+            old_proc = self.procs.get(proc.id)
+            if old_proc:
+                LOG.warning("%s(%d) reconnect !", old_proc.type, old_proc.id)
+                if old_proc.type != proc.type:
+                    LOG.warning("type changed ! new type: %s", proc.type)
+                with old_proc.sem:
+                    try:
+                        old_proc.conn.close()
+                    except Exception:
+                        pass
+            else:
+                LOG.info("%s(%d) connected.", proc.type, proc.id)
+            self.procs[proc.id] = proc
+
+    @staticmethod
+    def _continue_recv(conn):
+        try:
+            # must set non-blocking to recieve remining data not to happen
+            # blocking here.
+            # NOTE: usually MSG_DONTWAIT flag is used for this purpose but
+            # this flag is not supported under eventlet.
+            conn.setblocking(False)
+            data = b""
+            while True:
+                try:
+                    rcv_data = conn.recv(MSG_SIZE)
+                    data += rcv_data
+                    if len(rcv_data) < MSG_SIZE:
+                        break
+                except socket.error as e:
+                    if e.args[0] == errno.EAGAIN:
+                        # OK, no data remining. this happens when recieve data
+                        # length is just multiple of MSG_SIZE.
+                        break
+                    raise e
+            return data
+        finally:
+            conn.setblocking(True)
+
+    @staticmethod
+    def _send_command(conn, command):
+        conn.sendall(command.encode())
+        data = conn.recv(MSG_SIZE)
+        if data and len(data) == MSG_SIZE:
+            # could not receive data at once. recieve remining data.
+            data += self._continue_recv(conn)
+        if data:
+            data = data.decode()
+        return data
+
+    def _get_proc(self, conn):
+        # it is a bit ad hoc. send "_get_clinet_id" command and try to
+        # decode reply for each proc type. if success, that is the type.
+        data = self._send_command(conn, "_get_client_id")
+        for proc in [spp_proc.VfProc, spp_proc.NfvProc]:
+            sec_id = proc._decode_client_id(data)
+            if sec_id is not None:
+                return proc(sec_id, conn)
+
+    def get_processes(self):
+        procs = []
+        for proc in self.procs.values():
+            p = {"type": proc.type}
+            if proc.id != spp_proc.ID_PRIMARY:
+                p["client-id"] = proc.id
+            procs.append(p)
+        return procs
+
+
+def main():
+    parser = argparse.ArgumentParser(description="SPP Controller")
+    parser.add_argument("-p", dest='pri_port', type=int, default=5555,
+                        action='store', help="primary port")
+    parser.add_argument("-s", dest='sec_port', type=int, default=6666,
+                        action='store', help="secondary port")
+    parser.add_argument("-a", dest='api_port', type=int, default=7777,
+                        action='store', help="web api port")
+    args = parser.parse_args()
+
+    logging.basicConfig(level=logging.DEBUG)
+
+    controller = Controller(args.pri_port, args.sec_port, args.api_port)
+    controller.start()
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 08/14] spp-ctl: add web API handler
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (6 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 07/14] spp-ctl: add Controller class oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 09/14] spp-ctl: add spp command interfaces oda
                     ` (6 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Add WebServer class and handler classes to accept the request and route
for each of SPP processes.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_webapi.py | 441 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 441 insertions(+)
 create mode 100644 src/spp-ctl/spp_webapi.py

diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
new file mode 100644
index 0000000..435c4b7
--- /dev/null
+++ b/src/spp-ctl/spp_webapi.py
@@ -0,0 +1,441 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import bottle
+import errno
+import json
+import logging
+import netaddr
+import re
+import socket
+import subprocess
+import sys
+
+import spp_proc
+
+
+LOG = logging.getLogger(__name__)
+
+
+class KeyRequired(bottle.HTTPError):
+
+    def __init__(self, key):
+        msg = "key(%s) required." % key
+        super(KeyRequired, self).__init__(400, msg)
+
+
+class KeyInvalid(bottle.HTTPError):
+
+    def __init__(self, key, value):
+        msg = "invalid key(%s): %s." % (key, value)
+        super(KeyRequired, self).__init__(400, msg)
+
+
+class BaseHandler(bottle.Bottle):
+    """Define common methods for each handler."""
+
+    def __init__(self, controller):
+        super(BaseHandler, self).__init__()
+        self.ctrl = controller
+
+        self.default_error_handler = self._error_handler
+        bottle.response.default_status = 404
+
+    def _error_handler(self, res):
+        # use "text/plain" as content_type rather than bottle's default
+        # "html".
+        res.content_type = "text/plain"
+        return res.body
+
+    def _validate_port(self, port):
+        try:
+            if_type, if_num = port.split(":")
+            if if_type not in ["phy", "vhost", "ring"]:
+                raise
+            int(if_num)
+        except:
+            raise KeyInvalid('port', port)
+
+    def log_url(self):
+        LOG.info("%s %s called", bottle.request.method, bottle.request.path)
+
+    def log_response(self):
+        LOG.info("response: %s", bottle.response.status)
+
+    # following three decorators do common works for each API.
+    # each handler 'install' appropriate decorators.
+    #
+    def get_body(self, func):
+        """Get body and set it to method argument.
+        content-type is OK whether application/json or plain text.
+        """
+        def wrapper(*args, **kwargs):
+            req = bottle.request
+            if req.method in ["POST", "PUT"]:
+                if req.get_header('Content-Type') == "application/json":
+                    body = req.json
+                else:
+                    body = json.loads(req.body.read().decode())
+                kwargs['body'] = body
+                LOG.info("body: %s", body)
+            return func(*args, **kwargs)
+        return wrapper
+
+    def check_sec_id(self, func):
+        """Get and check proc and set it to method argument."""
+        def wrapper(*args, **kwargs):
+            sec_id = kwargs.pop('sec_id', None)
+            if sec_id is not None:
+                proc = self.ctrl.procs.get(sec_id)
+                if proc is None or proc.type != self.type:
+                    raise bottle.HTTPError(404,
+                                           "sec_id %d not found." % sec_id)
+                kwargs['proc'] = proc
+            return func(*args, **kwargs)
+        return wrapper
+
+    def make_response(self, func):
+        """Convert plain response to bottle.HTTPResponse."""
+        def wrapper(*args, **kwargs):
+            ret = func(*args, **kwargs)
+            if ret is None:
+                return bottle.HTTPResponse(status=204)
+            else:
+                r = bottle.HTTPResponse(status=200, body=json.dumps(ret))
+                r.content_type = "application/json"
+                return r
+        return wrapper
+
+
+class WebServer(BaseHandler):
+    """Top level handler.
+
+    handlers are hierarchized using 'mount' as follows:
+    /          WebServer
+    /v1          V1Handler
+       /vfs        V1VFHandler
+       /nfvs       V1NFVHandler
+       /primary    V1PrimaryHandler
+    """
+
+    def __init__(self, controller, api_port):
+        super(WebServer, self).__init__(controller)
+        self.api_port = api_port
+
+        self.mount("/v1", V1Handler(controller))
+
+        # request and response logging.
+        self.add_hook("before_request", self.log_url)
+        self.add_hook("after_request", self.log_response)
+
+    def start(self):
+        self.run(server='eventlet', host='localhost', port=self.api_port,
+                 quiet=True)
+
+
+class V1Handler(BaseHandler):
+    def __init__(self, controller):
+        super(V1Handler, self).__init__(controller)
+
+        self.set_route()
+
+        self.mount("/vfs", V1VFHandler(controller))
+        self.mount("/nfvs", V1NFVHandler(controller))
+        self.mount("/primary", V1PrimaryHandler(controller))
+
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/processes', 'GET', callback=self.get_processes)
+
+    def get_processes(self):
+        LOG.info("get processes called.")
+        return self.ctrl.get_processes()
+
+
+class V1VFHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1VFHandler, self).__init__(controller)
+        self.type = spp_proc.TYPE_VF
+
+        self.set_route()
+
+        self.install(self.check_sec_id)
+        self.install(self.get_body)
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/<sec_id:int>', 'GET', callback=self.vf_get)
+        self.route('/<sec_id:int>/components', 'POST',
+                   callback=self.vf_comp_start)
+        self.route('/<sec_id:int>/components/<name>', 'DELETE',
+                   callback=self.vf_comp_stop)
+        self.route('/<sec_id:int>/components/<name>', 'PUT',
+                   callback=self.vf_comp_port)
+        self.route('/<sec_id:int>/classifier_table', 'PUT',
+                   callback=self.vf_classifier)
+
+    def convert_vf_info(self, data):
+        info = data["info"]
+        vf = {}
+        vf["client-id"] = info["client-id"]
+        vf["ports"] = []
+        for key in ["phy", "vhost", "ring"]:
+            for idx in info[key]:
+                vf["ports"].append(key + ":" + str(idx))
+        vf["components"] = info["core"]
+        vf["classifier_table"] = info["classifier_table"]
+
+        return vf
+
+    def vf_get(self, proc):
+        return self.convert_vf_info(proc.get_status())
+
+    def _validate_vf_comp_start(self, body):
+        for key in ['name', 'core', 'type']:
+            if key not in body:
+                raise KeyRequired(key)
+        if not isinstance(body['name'], str):
+            raise KeyInvalid('name', body['name'])
+        if not isinstance(body['core'], int):
+            raise KeyInvalid('core', body['core'])
+        if body['type'] not in ["forward", "merge", "classifier_mac"]:
+            raise KeyInvalid('type', body['type'])
+
+    def vf_comp_start(self, proc, body):
+        self._validate_vf_comp_start(body)
+        proc.start_component(body['name'], body['core'], body['type'])
+
+    def vf_comp_stop(self, proc, name):
+        proc.stop_component(name)
+
+    def _validate_vf_comp_port(self, body):
+        for key in ['action', 'port', 'dir']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["attach", "detach"]:
+            raise KeyInvalid('action', body['action'])
+        if body['dir'] not in ["rx", "tx"]:
+            raise KeyInvalid('dir', body['dir'])
+        self._validate_port(body['port'])
+
+        if body['action'] == "attach":
+            vlan = body.get('vlan')
+            if vlan:
+                try:
+                    if vlan['operation'] not in ["none", "add", "del"]:
+                        raise
+                    if vlan['operation'] == "add":
+                        int(vlan['id'])
+                        int(vlan['pcp'])
+                except:
+                    raise KeyInvalid('vlan', vlan)
+
+    def vf_comp_port(self, proc, name, body):
+        self._validate_vf_comp_port(body)
+
+        if body['action'] == "attach":
+            op = "none"
+            vlan_id = 0
+            pcp = 0
+            vlan = body.get('vlan')
+            if vlan:
+                if vlan['operation'] == "add":
+                    op = "add_vlantag"
+                    vlan_id = vlan['id']
+                    pcp = vlan['pcp']
+                elif vlan['operation'] == "del":
+                    op = "del_vlantag"
+            proc.port_add(body['port'], body['dir'],
+                          name, op, vlan_id, pcp)
+        else:
+            proc.port_del(body['port'], body['dir'], name)
+
+    def _validate_mac(self, mac_address):
+        try:
+            netaddr.EUI(mac_address)
+        except:
+            raise KeyInvalid('mac_address', mac_address)
+
+    def _validate_vf_classifier(self, body):
+        for key in ['action', 'type', 'port', 'mac_address']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["add", "del"]:
+            raise KeyInvalid('action', body['action'])
+        if body['type'] not in ["mac", "vlan"]:
+            raise KeyInvalid('type', body['type'])
+        self._validate_port(body['port'])
+        self._validate_mac(body['mac_address'])
+
+        if body['type'] == "vlan":
+            try:
+                int(body['vlan'])
+            except:
+                raise KeyInvalid('vlan', body.get('vlan'))
+
+    def vf_classifier(self, proc, body):
+        self._validate_vf_classifier(body)
+
+        port = body['port']
+        mac_address = body['mac_address']
+
+        if body['action'] == "add":
+            if body['type'] == "mac":
+                proc.set_classifier_table(mac_address, port)
+            else:
+                proc.set_classifier_table_with_vlan(
+                    mac_address, port, body['vlan'])
+        else:
+            if body['type'] == "mac":
+                proc.clear_classifier_table(mac_address, port)
+            else:
+                proc.clear_classifier_table_with_vlan(
+                    mac_address, port, body['vlan'])
+
+
+class V1NFVHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1NFVHandler, self).__init__(controller)
+        self.type = spp_proc.TYPE_NFV
+
+        self.set_route()
+
+        self.install(self.check_sec_id)
+        self.install(self.get_body)
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/<sec_id:int>', 'GET', callback=self.nfv_get)
+        self.route('/<sec_id:int>/forward', 'PUT',
+                   callback=self.nfv_forward)
+        self.route('/<sec_id:int>/ports', 'PUT',
+                   callback=self.nfv_port)
+        self.route('/<sec_id:int>/patches', 'PUT',
+                   callback=self.nfv_patch_add)
+        self.route('/<sec_id:int>/patches', 'DELETE',
+                   callback=self.nfv_patch_del)
+
+    def convert_nfv_info(self, data):
+        nfv = {}
+        lines = data.splitlines()
+        if len(lines) < 3:
+            return {}
+        p = re.compile("Client ID (\d+) (\w+)")
+        m = p.match(lines[0])
+        if m:
+            nfv['client_id'] = int(m.group(1))
+            nfv['status'] = m.group(2)
+
+        ports = {}
+        outs = []
+        for line in lines[2:]:
+            if not line.startswith("port_id"):
+                break
+            arg1, _, arg2, arg3 = line.split(",")
+            _, port_id = arg1.split(":")
+            if arg2 == "PHY":
+                port = "phy:" + port_id
+            else:
+                if_type, rest = arg2.split("(")
+                if_num = rest.rstrip(")")
+                if if_type == "RING":
+                    port = "ring:" + if_num
+                elif if_type == "VHOST":
+                    port = "vhost:" + if_num
+                else:
+                    port = if_type + ":" + if_num
+            ports[port_id] = port
+            _, out_id = arg3.split(":")
+            if out_id != "none":
+                outs.append((port_id, out_id))
+        nfv['ports'] = list(ports.values())
+        patches = []
+        if outs:
+            for src_id, dst_id in outs:
+                patches.append({"src": ports[src_id], "dst": ports[dst_id]})
+        nfv['patches'] = patches
+
+        return nfv
+
+    def nfv_get(self, proc):
+        return self.convert_nfv_info(proc.get_status())
+
+    def _validate_nfv_forward(self, body):
+        if 'action' not in body:
+            raise KeyRequired('action')
+        if body['action'] not in ["start", "stop"]:
+            raise KeyInvalid('action', body['action'])
+
+    def nfv_forward(self, proc, body):
+        if body['action'] == "start":
+            proc.forward()
+        else:
+            proc.stop()
+
+    def _validate_nfv_port(self, body):
+        for key in ['action', 'port']:
+            if key not in body:
+                raise KeyRequired(key)
+        if body['action'] not in ["add", "del"]:
+            raise KeyInvalid('action', body['action'])
+        self._validate_port(body['port'])
+
+    def nfv_port(self, proc, body):
+        self._validate_nfv_port(body)
+
+        if_type, if_num = body['port'].split(":")
+        if body['action'] == "add":
+            proc.port_add(if_type, if_num)
+        else:
+            proc.port_del(if_type, if_num)
+
+    def _validate_nfv_patch(self, body):
+        for key in ['src', 'dst']:
+            if key not in body:
+                raise KeyRequired(key)
+        self._validate_port(body['src'])
+        self._validate_port(body['dst'])
+
+    def nfv_patch_add(self, proc, body):
+        self._validate_nfv_patch(body)
+        proc.patch_add(body['src'], body['dst'])
+
+    def nfv_patch_del(self, proc):
+        proc.patch_reset()
+
+
+class V1PrimaryHandler(BaseHandler):
+
+    def __init__(self, controller):
+        super(V1PrimaryHandler, self).__init__(controller)
+
+        self.set_route()
+
+        self.install(self.make_response)
+
+    def set_route(self):
+        self.route('/status', 'GET', callback=self.get_status)
+        self.route('/status', 'DELETE', callback=self.clear_status)
+
+    def _get_proc(self):
+        proc = self.ctrl.procs.get(spp_proc.ID_PRIMARY)
+        if proc is None:
+            raise bottle.HTTPError(404, "primary not found.")
+        return proc
+
+    def convert_status(self, data):
+        # no data returned at the moment.
+        # some data will be returned when the primary becomes to
+        # return statistical information.
+        return {}
+
+    def get_status(self):
+        proc = self._get_proc()
+        return self.convert_status(proc.status())
+
+    def clear_status(self):
+        proc = self._get_proc()
+        proc.clear()
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 09/14] spp-ctl: add spp command interfaces
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (7 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 08/14] spp-ctl: add web API handler oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 10/14] spp-ctl: update parsing spp_nfv status oda
                     ` (5 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

Add API classes for each of SPP processes.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_proc.py | 187 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 187 insertions(+)
 create mode 100644 src/spp-ctl/spp_proc.py

diff --git a/src/spp-ctl/spp_proc.py b/src/spp-ctl/spp_proc.py
new file mode 100644
index 0000000..aa83b76
--- /dev/null
+++ b/src/spp-ctl/spp_proc.py
@@ -0,0 +1,187 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+import bottle
+import eventlet
+import json
+import logging
+
+import spp_ctl
+
+
+LOG = logging.getLogger(__name__)
+
+ID_PRIMARY = 0
+TYPE_PRIMARY = "primary"
+TYPE_VF = "vf"
+TYPE_NFV = "nfv"
+
+
+def exec_command(func):
+    """Decorator for Sending command and receiving reply.
+
+    Define the common function for sending command and receiving reply
+    as a decorator. Each of methods for executing command has only to
+    return command string.
+
+    exp)
+    @exec_command
+    def some_command(self, ...):
+        return "command string of some_command"
+    """
+    def wrapper(self, *args, **kwargs):
+        with self.sem:
+            command = func(self, *args, **kwargs)
+            LOG.info("%s(%d) command executed: %s", self.type, self.id,
+                     command)
+            data = spp_ctl.Controller._send_command(self.conn, command)
+            if data is None:
+                raise RuntimeError("%s(%d): %s: no-data returned" %
+                                   (self.type, self.id, command))
+            LOG.debug("reply: %s", data)
+            return self._decode_reply(data)
+    return wrapper
+
+
+class SppProc(object):
+    def __init__(self, proc_type, id, conn):
+        self.id = id
+        self.type = proc_type
+        # NOTE: executing command is serialized by using a semaphore
+        # for each process.
+        self.sem = eventlet.semaphore.Semaphore(value=1)
+        self.conn = conn
+
+
+class VfProc(SppProc):
+
+    def __init__(self, id, conn):
+        super(VfProc, self).__init__(TYPE_VF, id, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        data = json.loads(data)
+        result = data["results"][0]
+        if result["result"] == "error":
+            msg = result["error_details"]["message"]
+            raise bottle.HTTPError(400, "command error: %s" % msg)
+        return data
+
+    @staticmethod
+    def _decode_client_id(data):
+        try:
+            data = VfProc._decode_reply(data)
+            return data["client_id"]
+        except:
+            return None
+
+    @exec_command
+    def get_status(self):
+        return "status"
+
+    @exec_command
+    def start_component(self, comp_name, core_id, comp_type):
+        return ("component start {comp_name} {core_id} {comp_type}"
+                .format(**locals()))
+
+    @exec_command
+    def stop_component(self, comp_name):
+        return "component stop {comp_name}".format(**locals())
+
+    @exec_command
+    def port_add(self, port, direction, comp_name, op, vlan_id, pcp):
+        command = "port add {port} {direction} {comp_name}".format(**locals())
+        if op != "none":
+            command += " %s" % op
+            if op == "add_vlantag":
+                command += " %d %d" % (vlan_id, pcp)
+        return command
+
+    @exec_command
+    def port_del(self, port, direction, comp_name):
+        return "port del {port} {direction} {comp_name}".format(**locals())
+
+    @exec_command
+    def set_classifier_table(self, mac_address, port):
+        return ("classifier_table add mac {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def clear_classifier_table(self, mac_address, port):
+        return ("classifier_table del mac {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def set_classifier_table_with_vlan(self, mac_address, port,
+                                       vlan_id):
+        return ("classifier_table add vlan {vlan_id} {mac_address} {port}"
+                .format(**locals()))
+
+    @exec_command
+    def clear_classifier_table_with_vlan(self, mac_address, port,
+                                         vlan_id):
+        return ("classifier_table del vlan {vlan_id} {mac_address} {port}"
+                .format(**locals()))
+
+
+class NfvProc(SppProc):
+
+    def __init__(self, id, conn):
+        super(NfvProc, self).__init__(TYPE_NFV, id, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        return data.strip('\0')
+
+    @staticmethod
+    def _decode_client_id(data):
+        try:
+            return int(NfvProc._decode_reply(data))
+        except:
+            return None
+
+    @exec_command
+    def get_status(self):
+        return "status"
+
+    @exec_command
+    def port_add(self, if_type, if_num):
+        return "add {if_type} {if_num}".format(**locals())
+
+    @exec_command
+    def port_del(self, if_type, if_num):
+        return "del {if_type} {if_num}".format(**locals())
+
+    @exec_command
+    def patch_add(self, src_port, dst_port):
+        return "patch {src_port} {dst_port}".format(**locals())
+
+    @exec_command
+    def patch_reset(self):
+        return "patch reset"
+
+    @exec_command
+    def forward(self):
+        return "forward"
+
+    @exec_command
+    def stop(self):
+        return "stop"
+
+
+class PrimaryProc(SppProc):
+
+    def __init__(self, conn):
+        super(PrimaryProc, self).__init__(TYPE_PRIMARY, ID_PRIMARY, conn)
+
+    @staticmethod
+    def _decode_reply(data):
+        return data.strip('\0')
+
+    @exec_command
+    def status(self):
+        return "status"
+
+    @exec_command
+    def clear(self):
+        return "clear"
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 10/14] spp-ctl: update parsing spp_nfv status
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (8 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 09/14] spp-ctl: add spp command interfaces oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 11/14] docs: add request examples of spp-ctl oda
                     ` (4 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Update for parsing the result of status of spp_nfv.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_webapi.py | 66 +++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 38 deletions(-)

diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
index 435c4b7..ee47f2f 100644
--- a/src/spp-ctl/spp_webapi.py
+++ b/src/spp-ctl/spp_webapi.py
@@ -318,50 +318,40 @@ class V1NFVHandler(BaseHandler):
         self.route('/<sec_id:int>/patches', 'DELETE',
                    callback=self.nfv_patch_del)
 
-    def convert_nfv_info(self, data):
+    def convert_nfv_info(self, sec_id, data):
         nfv = {}
-        lines = data.splitlines()
-        if len(lines) < 3:
+
+        # spp_nfv returns status info in two lines. First line is
+        # status of running or idling, and second is patch info.
+        # 'null' means that it has no dst port.
+        #   "status: idling\nports: 'phy:0-phy:1,phy:1-null'\x00\x00.."
+        entries = data.split('\n')
+        if len(entries) != 2:
             return {}
-        p = re.compile("Client ID (\d+) (\w+)")
-        m = p.match(lines[0])
-        if m:
-            nfv['client_id'] = int(m.group(1))
-            nfv['status'] = m.group(2)
-
-        ports = {}
-        outs = []
-        for line in lines[2:]:
-            if not line.startswith("port_id"):
-                break
-            arg1, _, arg2, arg3 = line.split(",")
-            _, port_id = arg1.split(":")
-            if arg2 == "PHY":
-                port = "phy:" + port_id
-            else:
-                if_type, rest = arg2.split("(")
-                if_num = rest.rstrip(")")
-                if if_type == "RING":
-                    port = "ring:" + if_num
-                elif if_type == "VHOST":
-                    port = "vhost:" + if_num
-                else:
-                    port = if_type + ":" + if_num
-            ports[port_id] = port
-            _, out_id = arg3.split(":")
-            if out_id != "none":
-                outs.append((port_id, out_id))
-        nfv['ports'] = list(ports.values())
-        patches = []
-        if outs:
-            for src_id, dst_id in outs:
-                patches.append({"src": ports[src_id], "dst": ports[dst_id]})
-        nfv['patches'] = patches
+
+        nfv['client_id'] = int(sec_id)
+        nfv['status'] = entries[0].split()[1]
+
+        patch_list = entries[1].split()[1].replace("'", '')
+
+        ports = []
+        nfv['patches'] = []
+
+        for port_cmb in patch_list.split(','):
+            p_src, p_dst = port_cmb.split('-')
+            if p_src != 'null' and p_dst != 'null':
+                nfv['patches'].append({'src': p_src, 'dst': p_dst})
+
+            for port in [p_src, p_dst]:
+                if port != 'null':
+                    ports.append(port)
+
+        nfv['ports'] = list(set(ports))
 
         return nfv
 
     def nfv_get(self, proc):
-        return self.convert_nfv_info(proc.get_status())
+        return self.convert_nfv_info(proc.id, proc.get_status())
 
     def _validate_nfv_forward(self, body):
         if 'action' not in body:
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 11/14] docs: add request examples of spp-ctl
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (9 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 10/14] spp-ctl: update parsing spp_nfv status oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 12/14] docs: correct directives " oda
                     ` (3 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Add request examples of spp-ctl using curl command.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 887 ++++++++++++++------------
 1 file changed, 465 insertions(+), 422 deletions(-)

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
index 4d2a641..285526e 100644
--- a/docs/guides/spp-ctl/api-reference.rst
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -51,6 +51,14 @@ Show the SPP processes connected to the ``spp-ctl``.
 
 * Normarl response codes: 200
 
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/processes
+
 Response
 ^^^^^^^^
 
@@ -86,13 +94,59 @@ Response example
     ]
 
 
-API for spp_vf
-==============
+API for spp_primary
+===================
 
-GET /v1/vfs/{client_id}
------------------------
+GET /v1/primary/status
+----------------------
 
-Get the information of the ``spp_vf`` process.
+Show statistical information.
+
+* Normal response codes: 200
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/primary/status
+
+Response
+^^^^^^^^
+
+There is no data at the moment. The statistical information will be returned
+when ``spp_primary`` implements it.
+
+
+DELETE /v1/primary/status
+-------------------------
+
+Clear statistical information.
+
+* Normal response codes: 204
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X DELETE -H 'application/json' \
+    http://127.0.0.1:7777/v1/primary/status
+
+Response
+^^^^^^^^
+
+There is no body content for the response of a successful ``PUT`` request.
+
+
+API for spp_nfv/spp_vm
+======================
+
+GET /v1/nfvs/{client_id}
+------------------------
+
+Get the information of the ``spp_nfv`` or ``spp_vm`` process.
 
 * Normal response codes: 200
 * Error response codes: 400, 404
@@ -106,70 +160,38 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-Response
-^^^^^^^^
-
-+------------------+---------+-----------------------------------------------+
-| Name             | Type    | Description                                   |
-+==================+=========+===============================================+
-| client-id        | integer | client id.                                    |
-+------------------+---------+-----------------------------------------------+
-| ports            | array   | an array of port ids used by the process.     |
-+------------------+---------+-----------------------------------------------+
-| components       | array   | an array of component objects in the process. |
-+------------------+---------+-----------------------------------------------+
-| classifier_table | array   | an array of classifier tables in the process. |
-+------------------+---------+-----------------------------------------------+
-
-component object:
-
-+---------+---------+---------------------------------------------------------------------+
-| Name    | Type    | Description                                                         |
-+=========+=========+=====================================================================+
-| core    | integer | core id running on the component                                    |
-+---------+---------+---------------------------------------------------------------------+
-| name    | string  | an array of port ids used by the process.                           |
-+---------+---------+---------------------------------------------------------------------+
-| type    | string  | an array of component objects in the process.                       |
-+---------+---------+---------------------------------------------------------------------+
-| rx_port | array   | an array of port objects connected to the rx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
-| tx_port | array   | an array of port objects connected to the tx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
+Request example
+^^^^^^^^^^^^^^^
 
-port object:
+.. code-block:: console
 
-+---------+---------+---------------------------------------------------------------+
-| Name    | Type    | Description                                                   |
-+=========+=========+===============================================================+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
-+---------+---------+---------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port.                  |
-+---------+---------+---------------------------------------------------------------+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/nfvs/1
 
-vlan object:
+Response
+^^^^^^^^
 
-+-----------+---------+-------------------------------+
-| Name      | Type    | Description                   |
-+===========+=========+===============================+
-| operation | string  | ``add``, ``del`` or ``none``. |
-+-----------+---------+-------------------------------+
-| id        | integer | vlan id.                      |
-+-----------+---------+-------------------------------+
-| pcp       | integer | vlan pcp.                     |
-+-----------+---------+-------------------------------+
++-----------+---------+-------------------------------------------+
+| Name      | Type    | Description                               |
++===========+=========+===========================================+
+| client-id | integer | client id.                                |
++-----------+---------+-------------------------------------------+
+| status    | string  | ``Running`` or ``Idle``.                  |
++-----------+---------+-------------------------------------------+
+| ports     | array   | an array of port ids used by the process. |
++-----------+---------+-------------------------------------------+
+| patches   | array   | an array of patches.                      |
++-----------+---------+-------------------------------------------+
 
-classifier table:
+patch objest
 
-+-----------+--------+------------------------------------------------------------+
-| Name      | Type   | Description                                                |
-+===========+========+============================================================+
-| type      | string | ``mac`` or ``vlan``.                                       |
-+-----------+--------+------------------------------------------------------------+
-| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
-+-----------+--------+------------------------------------------------------------+
-| port      | string | port id applied to classify.                               |
-+-----------+--------+------------------------------------------------------------+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -178,106 +200,20 @@ Response example
 
     {
       "client-id": 1,
+      "status": "Running",
       "ports": [
         "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
       ],
-      "components": [
-        {
-          "core": 2,
-          "name": "forward_0_tx",
-          "type": "forward",
-          "rx_port": [
-            {
-            "port": "ring:0",
-            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "vhost:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
-        },
-        {
-          "core": 3,
-          "type": "unuse"
-        },
-        {
-          "core": 4,
-          "type": "unuse"
-        },
-        {
-          "core": 5,
-          "name": "forward_1_rx",
-          "type": "forward",
-          "rx_port": [
-            {
-            "port": "vhost:1",
-            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "ring:3",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
-        },
-        {
-          "core": 6,
-          "name": "classifier",
-          "type": "classifier_mac",
-          "rx_port": [
-            {
-              "port": "phy:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "ring:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            },
-            {
-              "port": "ring:2",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
-        },
+      "patches": [
         {
-          "core": 7,
-          "name": "merger",
-          "type": "merge",
-          "rx_port": [
-            {
-              "port": "ring:1",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            },
-            {
-              "port": "ring:3",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ],
-          "tx_port": [
-            {
-              "port": "phy:0",
-              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
-            }
-          ]
+          "src": "vhost:0", "dst": "ring:0"
         },
-      ],
-      "classifier_table": [
         {
-          "type": "mac",
-          "value": "FA:16:3E:7D:CC:35",
-          "port": "ring:0"
+          "src": "ring:1", "dst": "vhost:1"
         }
       ]
     }
 
-The component which type is ``unused`` is to indicate unused core.
-
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
@@ -286,10 +222,10 @@ Equivalent CLI command
     sec {client_id};status
 
 
-POST /v1/vfs/{client_id}/components
------------------------------------
+PUT /v1/nfvs/{client_id}/forward
+--------------------------------
 
-Start the component.
+Start or Stop forwarding.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -303,80 +239,99 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-
-Request(body)
-^^^^^^^^^^^^^
-
-+-----------+---------+----------------------------------------------------------------------+
-| Name      | Type    | Description                                                          |
-+===========+=========+======================================================================+
-| name      | string  | component name. must be unique in the process.                       |
-+-----------+---------+----------------------------------------------------------------------+
-| core      | integer | core id.                                                             |
-+-----------+---------+----------------------------------------------------------------------+
-| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
-+-----------+---------+----------------------------------------------------------------------+
-
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: console
 
-    {
-      "name": "forwarder1",
-      "core": 12,
-      "type": "forward"
-    }
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "start"}' \
+    http://127.0.0.1:7777/v1/nfvs/1/forward
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+------------------------+
+| Name   | Type   | Description            |
++========+========+========================+
+| action | string | ``start`` or ``stop``. |
++--------+--------+------------------------+
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``POST`` request.
+There is no body content for the response of a successful ``PUT`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+action is ``start``
 
-    sec {client_id};component start {name} {core} {type}
+.. code-block:: yaml
 
+    sec {client_id};forward
 
-DELETE /v1/vfs/{sec id}/components/{name}
------------------------------------------
+action is ``stop``
 
-Stop the component.
+.. code-block:: yaml
+
+    sec {client_id};stop
+
+
+PUT /v1/nfvs/{client_id}/ports
+------------------------------
+
+Add or Delete port.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
 
-Request(path)
-^^^^^^^^^^^^^
+Request(path)
+^^^^^^^^^^^^^
+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
+
+Request(body)
+^^^^^^^^^^^^^
+
++--------+--------+---------------------------------------------------------------+
+| Name   | Type   | Description                                                   |
++========+========+===============================================================+
+| action | string | ``add`` or ``del``.                                           |
++--------+--------+---------------------------------------------------------------+
+| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
++--------+--------+---------------------------------------------------------------+
+
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "add", "port": "ring:0"}' \
+    http://127.0.0.1:7777/v1/nfvs/1/ports
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``POST`` request.
+There is no body content for the response of a successful ``PUT`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-    sec {client_id};component stop {name}
+    sec {client_id};{action} {interface_type} {interface_id}
 
 
-PUT /v1/vfs/{client_id}/components/{name}/ports
------------------------------------------------
+PUT /v1/nfvs/{client_id}/patches
+--------------------------------
 
-Add or Delete port to the component.
+Add a patch.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -384,92 +339,49 @@ Add or Delete port to the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
++-----------+---------+-------------+
+| Name      | Type    | Description |
++===========+=========+=============+
+| client_id | integer | client id.  |
++-----------+---------+-------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+---------+---------+-----------------------------------------------------------------+
-| Name    | Type    | Description                                                     |
-+=========+=========+=================================================================+
-| action  | string  | ``attach`` or ``detach``.                                       |
-+---------+---------+-----------------------------------------------------------------+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
-+---------+---------+-----------------------------------------------------------------+
-| dir     | string  | ``rx`` or ``tx``.                                               |
-+---------+---------+-----------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
-+---------+---------+-----------------------------------------------------------------+
-
-vlan object:
-
-+-----------+---------+----------------------------------------------------------+
-| Name      | Type    | Description                                              |
-+===========+=========+==========================================================+
-| operation | string  | ``add``, ``del`` or ``none``.                            |
-+-----------+---------+----------------------------------------------------------+
-| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
-+-----------+---------+----------------------------------------------------------+
-| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
-+-----------+---------+----------------------------------------------------------+
++------+--------+----------------------+
+| Name | Type   | Description          |
++======+========+======================+
+| src  | string | source port id.      |
++------+--------+----------------------+
+| dst  | string | destination port id. |
++------+--------+----------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
-
-    {
-      "action": "attach",
-      "port": "vhost:1",
-      "dir": "rx",
-      "vlan": {
-        "operation": "add",
-        "id": 677,
-        "pcp": 0
-      }
-    }
-
-.. code-block:: yaml
-
-    {
-      "action": "detach",
-      "port": "vhost:0",
-      "dir": "tx"
-    }
+.. code-block:: console
 
+    curl -X PUT -H 'application/json' \
+    -d '{"src": "ring:0", "dst": "ring:1"}' \
+    http://127.0.0.1:7777/v1/nfvs/1/patches
 
 Response
 ^^^^^^^^
 
 There is no body content for the response of a successful ``PUT`` request.
 
-
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-action is ``attach``
-
-.. code-block:: console
-
-    sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
-
-action is ``detach``
-
 .. code-block:: console
 
-    sec {client_id};port del {port} {dir} {name}
+    sec {client_id};patch {src} {dst}
 
 
-PUT /v1/vfs/{sec id}/classifier_table
--------------------------------------
+DELETE /v1/nfvs/{client_id}/patches
+-----------------------------------
 
-Set or Unset classifier table.
+Reset patches.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -483,74 +395,34 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-Request(body)
-^^^^^^^^^^^^^
-
-+-------------+-----------------+----------------------------------------------------+
-| Name        | Type            | Description                                        |
-+=============+=================+====================================================+
-| action      | string          | ``add`` or ``del``.                                |
-+-------------+-----------------+----------------------------------------------------+
-| type        | string          | ``mac`` or ``vlan``.                               |
-+-------------+-----------------+----------------------------------------------------+
-| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
-+-------------+-----------------+----------------------------------------------------+
-| mac_address | string          | mac address.                                       |
-+-------------+-----------------+----------------------------------------------------+
-| port        | string          | port id.                                           |
-+-------------+-----------------+----------------------------------------------------+
-
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
-
-    {
-      "action": "add",
-      "type": "mac",
-      "mac_address": "FA:16:3E:7D:CC:35",
-      "port": "ring:0"
-    }
-
-.. code-block:: yaml
-
-    {
-      "action": "del",
-      "type": "vlan",
-      "vlan": 475,
-      "mac_address": "FA:16:3E:7D:CC:35",
-      "port": "ring:0"
-    }
+.. code-block:: console
 
+    curl -X DELETE -H 'application/json' \
+    http://127.0.0.1:7777/v1/nfvs/1/patches
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``PUT`` request.
+There is no body content for the response of a successful ``DELETE`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-type is ``mac``
-
-.. code-block:: console
-
-    classifier_table {action} mac {mac_address} {port}
-
-type is ``vlan``
-
 .. code-block:: console
 
-    classifier_table {action} vlan {vlan} {mac_address} {port}
+    sec {client_id};patch reset
 
 
-API for spp_nfv/spp_vm
-======================
+API for spp_vf
+==============
 
-GET /v1/nfvs/{client_id}
-------------------------
+GET /v1/vfs/{client_id}
+-----------------------
 
-Get the information of the ``spp_nfv`` or ``spp_vm`` process.
+Get the information of the ``spp_vf`` process.
 
 * Normal response codes: 200
 * Error response codes: 400, 404
@@ -564,30 +436,78 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
+Request example
+^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+    curl -X GET -H 'application/json' \
+    http://127.0.0.1:7777/v1/vfs/1
+
 Response
 ^^^^^^^^
 
-+-----------+---------+-------------------------------------------+
-| Name      | Type    | Description                               |
-+===========+=========+===========================================+
-| client-id | integer | client id.                                |
-+-----------+---------+-------------------------------------------+
-| status    | string  | ``Running`` or ``Idle``.                  |
-+-----------+---------+-------------------------------------------+
-| ports     | array   | an array of port ids used by the process. |
-+-----------+---------+-------------------------------------------+
-| patches   | array   | an array of patches.                      |
-+-----------+---------+-------------------------------------------+
++------------------+---------+-----------------------------------------------+
+| Name             | Type    | Description                                   |
++==================+=========+===============================================+
+| client-id        | integer | client id.                                    |
++------------------+---------+-----------------------------------------------+
+| ports            | array   | an array of port ids used by the process.     |
++------------------+---------+-----------------------------------------------+
+| components       | array   | an array of component objects in the process. |
++------------------+---------+-----------------------------------------------+
+| classifier_table | array   | an array of classifier tables in the process. |
++------------------+---------+-----------------------------------------------+
 
-patch objest
+component object:
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
++---------+---------+---------------------------------------------------------------------+
+| Name    | Type    | Description                                                         |
++=========+=========+=====================================================================+
+| core    | integer | core id running on the component                                    |
++---------+---------+---------------------------------------------------------------------+
+| name    | string  | an array of port ids used by the process.                           |
++---------+---------+---------------------------------------------------------------------+
+| type    | string  | an array of component objects in the process.                       |
++---------+---------+---------------------------------------------------------------------+
+| rx_port | array   | an array of port objects connected to the rx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+| tx_port | array   | an array of port objects connected to the tx side of the component. |
++---------+---------+---------------------------------------------------------------------+
+
+port object:
+
++---------+---------+---------------------------------------------------------------+
+| Name    | Type    | Description                                                   |
++=========+=========+===============================================================+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
++---------+---------+---------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port.                  |
++---------+---------+---------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+-------------------------------+
+| Name      | Type    | Description                   |
++===========+=========+===============================+
+| operation | string  | ``add``, ``del`` or ``none``. |
++-----------+---------+-------------------------------+
+| id        | integer | vlan id.                      |
++-----------+---------+-------------------------------+
+| pcp       | integer | vlan pcp.                     |
++-----------+---------+-------------------------------+
+
+classifier table:
+
++-----------+--------+------------------------------------------------------------+
+| Name      | Type   | Description                                                |
++===========+========+============================================================+
+| type      | string | ``mac`` or ``vlan``.                                       |
++-----------+--------+------------------------------------------------------------+
+| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
++-----------+--------+------------------------------------------------------------+
+| port      | string | port id applied to classify.                               |
++-----------+--------+------------------------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -596,20 +516,106 @@ Response example
 
     {
       "client-id": 1,
-      "status": "Running",
       "ports": [
         "phy:0", "phy:1", "vhost:0", "vhost:1", "ring:0", "ring:1", "ring:2", "ring:3"
       ],
-      "patches": [
+      "components": [
+        {
+          "core": 2,
+          "name": "forward_0_tx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "ring:0",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "vhost:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+        {
+          "core": 3,
+          "type": "unuse"
+        },
+        {
+          "core": 4,
+          "type": "unuse"
+        },
+        {
+          "core": 5,
+          "name": "forward_1_rx",
+          "type": "forward",
+          "rx_port": [
+            {
+            "port": "vhost:1",
+            "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
         {
-          "src": "vhost:0", "dst": "ring:0"
+          "core": 6,
+          "name": "classifier",
+          "type": "classifier_mac",
+          "rx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "ring:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:2",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
         },
         {
-          "src": "ring:1", "dst": "vhost:1"
+          "core": 7,
+          "name": "merger",
+          "type": "merge",
+          "rx_port": [
+            {
+              "port": "ring:1",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            },
+            {
+              "port": "ring:3",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ],
+          "tx_port": [
+            {
+              "port": "phy:0",
+              "vlan": { "operation": "none", "id": 0, "pcp": 0 }
+            }
+          ]
+        },
+      ],
+      "classifier_table": [
+        {
+          "type": "mac",
+          "value": "FA:16:3E:7D:CC:35",
+          "port": "ring:0"
         }
       ]
     }
 
+The component which type is ``unused`` is to indicate unused core.
+
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
@@ -618,10 +624,10 @@ Equivalent CLI command
     sec {client_id};status
 
 
-PUT /v1/nfvs/{client_id}/forward
---------------------------------
+POST /v1/vfs/{client_id}/components
+-----------------------------------
 
-Start or Stop forwarding.
+Start the component.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -635,47 +641,46 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
+
 Request(body)
 ^^^^^^^^^^^^^
 
-+--------+--------+------------------------+
-| Name   | Type   | Description            |
-+========+========+========================+
-| action | string | ``start`` or ``stop``. |
-+--------+--------+------------------------+
++-----------+---------+----------------------------------------------------------------------+
+| Name      | Type    | Description                                                          |
++===========+=========+======================================================================+
+| name      | string  | component name. must be unique in the process.                       |
++-----------+---------+----------------------------------------------------------------------+
+| core      | integer | core id.                                                             |
++-----------+---------+----------------------------------------------------------------------+
+| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
++-----------+---------+----------------------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: console
 
-    {"action": "start"}
+    curl -X POST -H 'application/json' \
+    -d '{"name": "forwarder1", "core": 12, "type": "forward"}' \
+    http://127.0.0.1:7777/v1/vfs/1/components
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``PUT`` request.
+There is no body content for the response of a successful ``POST`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-action is ``start``
-
-.. code-block:: yaml
-
-    sec {client_id};forward
-
-action is ``stop``
-
-.. code-block:: yaml
+.. code-block:: console
 
-    sec {client_id};stop
+    sec {client_id};component start {name} {core} {type}
 
 
-PUT /v1/nfvs/{client_id}/ports
-------------------------------
+DELETE /v1/vfs/{sec id}/components/{name}
+-----------------------------------------
 
-Add or Delete port.
+Stop the component.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -683,48 +688,39 @@ Add or Delete port.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
-
-Request(body)
-^^^^^^^^^^^^^
-
-+--------+--------+---------------------------------------------------------------+
-| Name   | Type   | Description                                                   |
-+========+========+===============================================================+
-| action | string | ``add`` or ``del``.                                           |
-+--------+--------+---------------------------------------------------------------+
-| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
-+--------+--------+---------------------------------------------------------------+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
-
-    {"action": "add", "port": "vhost:0"}
+.. code-block:: console
 
+    curl -X DELETE -H 'application/json' \
+    http://127.0.0.1:7777/v1/vfs/1/components/forwarder1
 
 Response
 ^^^^^^^^
 
-There is no body content for the response of a successful ``PUT`` request.
+There is no body content for the response of a successful ``POST`` request.
 
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-    sec {client_id};{action} {interface_type} {interface_id}
+    sec {client_id};component stop {name}
 
 
-PUT /v1/nfvs/{client_id}/patches
---------------------------------
+PUT /v1/vfs/{client_id}/components/{name}/ports
+-----------------------------------------------
 
-Add a patch.
+Add or Delete port to the component.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -732,29 +728,56 @@ Add a patch.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
++-----------+---------+-----------------+
+| Name      | Type    | Description     |
++===========+=========+=================+
+| client_id | integer | client id.      |
++-----------+---------+-----------------+
+| name      | string  | component name. |
++-----------+---------+-----------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
++---------+---------+-----------------------------------------------------------------+
+| Name    | Type    | Description                                                     |
++=========+=========+=================================================================+
+| action  | string  | ``attach`` or ``detach``.                                       |
++---------+---------+-----------------------------------------------------------------+
+| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
++---------+---------+-----------------------------------------------------------------+
+| dir     | string  | ``rx`` or ``tx``.                                               |
++---------+---------+-----------------------------------------------------------------+
+| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
++---------+---------+-----------------------------------------------------------------+
+
+vlan object:
+
++-----------+---------+----------------------------------------------------------+
+| Name      | Type    | Description                                              |
++===========+=========+==========================================================+
+| operation | string  | ``add``, ``del`` or ``none``.                            |
++-----------+---------+----------------------------------------------------------+
+| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
++-----------+---------+----------------------------------------------------------+
+| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
++-----------+---------+----------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: console
+
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "attach", "port": "vhost:1", "dir": "rx", \
+         "vlan": {"operation": "add", "id": 677, "pcp": 0}}' \
+    http://127.0.0.1:7777/v1/vfs/1/components/forwarder1/ports
+
+.. code-block:: console
 
-    {"src": "vhost:0", "dst": "ring:0"}
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "detach", "port": "vhost:0", "dir": "tx"} \
+    http://127.0.0.1:7777/v1/vfs/1/components/forwarder1/ports
 
 Response
 ^^^^^^^^
@@ -764,15 +787,23 @@ There is no body content for the response of a successful ``PUT`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
+action is ``attach``
+
 .. code-block:: console
 
-    sec {client_id};patch {src} {dst}
+    sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
 
+action is ``detach``
 
-DELETE /v1/nfvs/{client_id}/patches
------------------------------------
+.. code-block:: console
 
-Reset patches.
+    sec {client_id};port del {port} {dir} {name}
+
+
+PUT /v1/vfs/{sec id}/classifier_table
+-------------------------------------
+
+Set or Unset classifier table.
 
 * Normal response codes: 204
 * Error response codes: 400, 404
@@ -786,44 +817,56 @@ Request(path)
 | client_id | integer | client id.  |
 +-----------+---------+-------------+
 
-Response
-^^^^^^^^
+Request(body)
+^^^^^^^^^^^^^
 
-There is no body content for the response of a successful ``DELETE`` request.
++-------------+-----------------+----------------------------------------------------+
+| Name        | Type            | Description                                        |
++=============+=================+====================================================+
+| action      | string          | ``add`` or ``del``.                                |
++-------------+-----------------+----------------------------------------------------+
+| type        | string          | ``mac`` or ``vlan``.                               |
++-------------+-----------------+----------------------------------------------------+
+| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
++-------------+-----------------+----------------------------------------------------+
+| mac_address | string          | mac address.                                       |
++-------------+-----------------+----------------------------------------------------+
+| port        | string          | port id.                                           |
++-------------+-----------------+----------------------------------------------------+
 
-Equivalent CLI command
-^^^^^^^^^^^^^^^^^^^^^^
+Request example
+^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-    sec {client_id};patch reset
-
-
-API for spp_primary
-===================
-
-GET /v1/primary/status
-----------------------
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "add", "type": "mac", "mac_address": "FA:16:3E:7D:CC:35", \
+       "port": "ring:0"}' \
+    http://127.0.0.1:7777/v1/vfs/1/classifier_table
 
-Show statistical information.
+.. code-block:: console
 
-* Normal response codes: 200
+    curl -X PUT -H 'application/json' \
+    -d '{"action": "del", "type": "vlan", "vlan": 475, \
+       "mac_address": "FA:16:3E:7D:CC:35", "port": "ring:0"}' \
+    http://127.0.0.1:7777/v1/vfs/1/classifier_table
 
 Response
 ^^^^^^^^
 
-There is no data at the moment. The statistical information will be returned
-when ``spp_primary`` implements it.
+There is no body content for the response of a successful ``PUT`` request.
 
+Equivalent CLI command
+^^^^^^^^^^^^^^^^^^^^^^
 
-DELETE /v1/primary/status
--------------------------
+type is ``mac``
 
-Clear statistical information.
+.. code-block:: console
 
-* Normal response codes: 204
+    classifier_table {action} mac {mac_address} {port}
 
-Response
-^^^^^^^^
+type is ``vlan``
 
-There is no body content for the response of a successful ``PUT`` request.
+.. code-block:: console
+
+    classifier_table {action} vlan {vlan} {mac_address} {port}
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 12/14] docs: correct directives of spp-ctl
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (10 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 11/14] docs: add request examples of spp-ctl oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 13/14] docs: add labels and captions for tables oda
                     ` (2 subsequent siblings)
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Some of code-block directives are incorrect, and section headers are not
applied with the documentation guidelines of DPDK. This patch is to
correct following directives.

* Mistaken code-block directives.

* Section headers.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 74 +++++++++++++--------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
index 285526e..3263934 100644
--- a/docs/guides/spp-ctl/api-reference.rst
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -3,17 +3,16 @@
 
 .. _spp_ctl_api_ref:
 
-=============
 API Reference
 =============
 
 Overview
-========
+--------
 
 ``spp-ctl`` provides simple REST like API. It supports http only, not https.
 
 Request and Response
---------------------
+~~~~~~~~~~~~~~~~~~~~
 
 Request body is JSON format.
 It is accepted both ``text/plain`` and ``application/json``
@@ -26,7 +25,8 @@ If a request fails, the response is a text which shows error reason
 and the content-type is ``text/plain``.
 
 Error code
-----------
+~~~~~~~~~~
+
 
 ``spp-ctl`` does basic syntax and lexical check of a request.
 
@@ -42,10 +42,10 @@ Error code
 
 
 API independent of the process type
-===================================
+-----------------------------------
 
 GET /v1/processes
------------------
+~~~~~~~~~~~~~~~~~
 
 Show the SPP processes connected to the ``spp-ctl``.
 
@@ -77,7 +77,7 @@ process object:
 Response example
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: json
 
     [
       {
@@ -95,10 +95,10 @@ Response example
 
 
 API for spp_primary
-===================
+-------------------
 
 GET /v1/primary/status
-----------------------
+~~~~~~~~~~~~~~~~~~~~~~
 
 Show statistical information.
 
@@ -120,7 +120,7 @@ when ``spp_primary`` implements it.
 
 
 DELETE /v1/primary/status
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Clear statistical information.
 
@@ -141,10 +141,10 @@ There is no body content for the response of a successful ``PUT`` request.
 
 
 API for spp_nfv/spp_vm
-======================
+----------------------
 
 GET /v1/nfvs/{client_id}
-------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~
 
 Get the information of the ``spp_nfv`` or ``spp_vm`` process.
 
@@ -196,7 +196,7 @@ patch objest
 Response example
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: json
 
     {
       "client-id": 1,
@@ -217,13 +217,13 @@ Response example
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};status
 
 
 PUT /v1/nfvs/{client_id}/forward
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Start or Stop forwarding.
 
@@ -267,19 +267,19 @@ Equivalent CLI command
 
 action is ``start``
 
-.. code-block:: yaml
+.. code-block:: none
 
     sec {client_id};forward
 
 action is ``stop``
 
-.. code-block:: yaml
+.. code-block:: none
 
     sec {client_id};stop
 
 
 PUT /v1/nfvs/{client_id}/ports
-------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Add or Delete port.
 
@@ -323,13 +323,13 @@ There is no body content for the response of a successful ``PUT`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};{action} {interface_type} {interface_id}
 
 
 PUT /v1/nfvs/{client_id}/patches
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Add a patch.
 
@@ -373,13 +373,13 @@ There is no body content for the response of a successful ``PUT`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};patch {src} {dst}
 
 
 DELETE /v1/nfvs/{client_id}/patches
------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Reset patches.
 
@@ -411,16 +411,16 @@ There is no body content for the response of a successful ``DELETE`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};patch reset
 
 
 API for spp_vf
-==============
+--------------
 
 GET /v1/vfs/{client_id}
------------------------
+~~~~~~~~~~~~~~~~~~~~~~~
 
 Get the information of the ``spp_vf`` process.
 
@@ -512,7 +512,7 @@ classifier table:
 Response example
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: yaml
+.. code-block:: json
 
     {
       "client-id": 1,
@@ -619,13 +619,13 @@ The component which type is ``unused`` is to indicate unused core.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};status
 
 
 POST /v1/vfs/{client_id}/components
------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Start the component.
 
@@ -672,13 +672,13 @@ There is no body content for the response of a successful ``POST`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};component start {name} {core} {type}
 
 
 DELETE /v1/vfs/{sec id}/components/{name}
------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Stop the component.
 
@@ -712,13 +712,13 @@ There is no body content for the response of a successful ``POST`` request.
 Equivalent CLI command
 ^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};component stop {name}
 
 
 PUT /v1/vfs/{client_id}/components/{name}/ports
------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Add or Delete port to the component.
 
@@ -789,19 +789,19 @@ Equivalent CLI command
 
 action is ``attach``
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};port add {port} {dir} {name} [add_vlantag {id} {pcp} | del_vlantag]
 
 action is ``detach``
 
-.. code-block:: console
+.. code-block:: none
 
     sec {client_id};port del {port} {dir} {name}
 
 
 PUT /v1/vfs/{sec id}/classifier_table
--------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Set or Unset classifier table.
 
@@ -861,12 +861,12 @@ Equivalent CLI command
 
 type is ``mac``
 
-.. code-block:: console
+.. code-block:: none
 
     classifier_table {action} mac {mac_address} {port}
 
 type is ``vlan``
 
-.. code-block:: console
+.. code-block:: none
 
     classifier_table {action} vlan {vlan} {mac_address} {port}
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 13/14] docs: add labels and captions for tables
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (11 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 12/14] docs: correct directives " oda
@ 2018-10-05  3:57   ` oda
  2018-10-05  3:57   ` [spp] [PATCH v4 14/14] spp-ctl: fix incorrect URL oda
  2018-10-09  2:01   ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API Yasufumi Ogawa
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

For documentation, DPDK's documentation Guidelines[1] requires that
all of tables have labels and captions.

[1] https://doc.dpdk.org/guides/contributing/documentation.html

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 docs/guides/spp-ctl/api-reference.rst | 525 ++++++++++++++++----------
 1 file changed, 327 insertions(+), 198 deletions(-)

diff --git a/docs/guides/spp-ctl/api-reference.rst b/docs/guides/spp-ctl/api-reference.rst
index 3263934..23bc8db 100644
--- a/docs/guides/spp-ctl/api-reference.rst
+++ b/docs/guides/spp-ctl/api-reference.rst
@@ -30,15 +30,20 @@ Error code
 
 ``spp-ctl`` does basic syntax and lexical check of a request.
 
-+------+----------------------------------------------------------------+
-| Error| Description                                                    |
-+======+================================================================+
-| 400  | Syntax or lexical error, or SPP returns error for the request. |
-+------+----------------------------------------------------------------+
-| 404  | URL is not supported, or no SPP process of client-id in a URL. |
-+------+----------------------------------------------------------------+
-| 500  | System error occured in ``spp-ctl``.                           |
-+------+----------------------------------------------------------------+
+.. _table_spp_ctl_error_codes:
+
+.. table:: Error codes in spp-ctl.
+
+    +-------+----------------------------------------------------------------+
+    | Error | Description                                                    |
+    |       |                                                                |
+    +=======+================================================================+
+    | 400   | Syntax or lexical error, or SPP returns error for the request. |
+    +-------+----------------------------------------------------------------+
+    | 404   | URL is not supported, or no SPP process of client-id in a URL. |
+    +-------+----------------------------------------------------------------+
+    | 500   | System error occured in ``spp-ctl``.                           |
+    +-------+----------------------------------------------------------------+
 
 
 API independent of the process type
@@ -66,13 +71,18 @@ An array of process objects.
 
 process object:
 
-+-----------+---------+---------------------------------------------------------------+
-| Name      | Type    | Description                                                   |
-+===========+=========+===============================================================+
-| type      | string  | process type. one of ``primary``, ``vf`` or ``nfv``.          |
-+-----------+---------+---------------------------------------------------------------+
-| client-id | integer | client id. if type is ``primary`` this member does not exist. |
-+-----------+---------+---------------------------------------------------------------+
+.. _table_spp_ctl_processes:
+
+.. table:: Response params of getting processes info.
+
+    +-----------+---------+-----------------------------------------------------------------+
+    | Name      | Type    | Description                                                     |
+    |           |         |                                                                 |
+    +===========+=========+=================================================================+
+    | type      | string  | process type. one of ``primary``, ``nfv`` or ``vf``.            |
+    +-----------+---------+-----------------------------------------------------------------+
+    | client-id | integer | client id. if type is ``primary`` this member does not exist.   |
+    +-----------+---------+-----------------------------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -154,11 +164,16 @@ Get the information of the ``spp_nfv`` or ``spp_vm`` process.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_nfvs_get:
+
+.. table:: Request parameter for getting spp_nfv or spp_vm info.
+
+    +-----------+---------+-------------------------------------+
+    | Name      | Type    | Description                         |
+    |           |         |                                     |
+    +===========+=========+=====================================+
+    | client_id | integer | client id.                          |
+    +-----------+---------+-------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -171,27 +186,37 @@ Request example
 Response
 ^^^^^^^^
 
-+-----------+---------+-------------------------------------------+
-| Name      | Type    | Description                               |
-+===========+=========+===========================================+
-| client-id | integer | client id.                                |
-+-----------+---------+-------------------------------------------+
-| status    | string  | ``Running`` or ``Idle``.                  |
-+-----------+---------+-------------------------------------------+
-| ports     | array   | an array of port ids used by the process. |
-+-----------+---------+-------------------------------------------+
-| patches   | array   | an array of patches.                      |
-+-----------+---------+-------------------------------------------+
+.. _table_spp_ctl_spp_nfv_res:
+
+.. table:: Response params of getting spp_nfv or spp_vm info.
+
+    +-----------+---------+---------------------------------------------+
+    | Name      | Type    | Description                                 |
+    |           |         |                                             |
+    +===========+=========+=============================================+
+    | client-id | integer | client id.                                  |
+    +-----------+---------+---------------------------------------------+
+    | status    | string  | ``Running`` or ``Idle``.                    |
+    +-----------+---------+---------------------------------------------+
+    | ports     | array   | an array of port ids used by the process.   |
+    +-----------+---------+---------------------------------------------+
+    | patches   | array   | an array of patches.                        |
+    +-----------+---------+---------------------------------------------+
 
 patch objest
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
+.. _table_spp_ctl_patch_spp_nfv:
+
+.. table:: Attributes of patch of spp_nfv or spp_vm.
+
+    +------+--------+----------------------------------------------+
+    | Name | Type   | Description                                  |
+    |      |        |                                              |
+    +======+========+==============================================+
+    | src  | string | source port id.                              |
+    +------+--------+----------------------------------------------+
+    | dst  | string | destination port id.                         |
+    +------+--------+----------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -233,11 +258,16 @@ Start or Stop forwarding.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_forward_get:
+
+.. table:: Request params of forward of spp_nfv or spp_vm.
+
+    +-----------+---------+---------------------------------+
+    | Name      | Type    | Description                     |
+    |           |         |                                 |
+    +===========+=========+=================================+
+    | client_id | integer | client id.                      |
+    +-----------+---------+---------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -251,11 +281,16 @@ Request example
 Request(body)
 ^^^^^^^^^^^^^
 
-+--------+--------+------------------------+
-| Name   | Type   | Description            |
-+========+========+========================+
-| action | string | ``start`` or ``stop``. |
-+--------+--------+------------------------+
+.. _table_spp_ctl_spp_nfv_forward_get_body:
+
+.. table:: Request body params of forward of spp_nfv or spp_vm.
+
+    +--------+--------+-------------------------------------+
+    | Name   | Type   | Description                         |
+    |        |        |                                     |
+    +========+========+=====================================+
+    | action | string | ``start`` or ``stop``.              |
+    +--------+--------+-------------------------------------+
 
 Response
 ^^^^^^^^
@@ -289,22 +324,32 @@ Add or Delete port.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_ports_get:
+
+.. table:: Request params of ports of spp_nfv or spp_vm.
+
+    +-----------+---------+--------------------------------+
+    | Name      | Type    | Description                    |
+    |           |         |                                |
+    +===========+=========+================================+
+    | client_id | integer | client id.                     |
+    +-----------+---------+--------------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+--------+--------+---------------------------------------------------------------+
-| Name   | Type   | Description                                                   |
-+========+========+===============================================================+
-| action | string | ``add`` or ``del``.                                           |
-+--------+--------+---------------------------------------------------------------+
-| port   | string | port id. port id is the form {interface_type}:{interface_id}. |
-+--------+--------+---------------------------------------------------------------+
+.. _table_spp_ctl_spp_nfv_ports_get_body:
+
+.. table:: Request body params of ports of spp_nfv or spp_vm.
+
+    +--------+--------+---------------------------------------------------------------+
+    | Name   | Type   | Description                                                   |
+    |        |        |                                                               |
+    +========+========+===============================================================+
+    | action | string | ``add`` or ``del``.                                           |
+    +--------+--------+---------------------------------------------------------------+
+    | port   | string | port id. port id is the form {interface_type}:{interface_id}. |
+    +--------+--------+---------------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -339,22 +384,32 @@ Add a patch.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_patches_get:
+
+.. table:: Request params of patches of spp_nfv or spp_vm.
+
+    +-----------+---------+---------------------------------+
+    | Name      | Type    | Description                     |
+    |           |         |                                 |
+    +===========+=========+=================================+
+    | client_id | integer | client id.                      |
+    +-----------+---------+---------------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+------+--------+----------------------+
-| Name | Type   | Description          |
-+======+========+======================+
-| src  | string | source port id.      |
-+------+--------+----------------------+
-| dst  | string | destination port id. |
-+------+--------+----------------------+
+.. _table_spp_ctl_spp_nfv_ports_patches_body:
+
+.. table:: Request body params of patches of spp_nfv or spp_vm.
+
+    +------+--------+------------------------------------+
+    | Name | Type   | Description                        |
+    |      |        |                                    |
+    +======+========+====================================+
+    | src  | string | source port id.                    |
+    +------+--------+------------------------------------+
+    | dst  | string | destination port id.               |
+    +------+--------+------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -389,11 +444,16 @@ Reset patches.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_nfv_del_patches:
+
+.. table:: Request params of deleting patches of spp_nfv or spp_vm.
+
+    +-----------+---------+---------------------------------------+
+    | Name      | Type    | Description                           |
+    |           |         |                                       |
+    +===========+=========+=======================================+
+    | client_id | integer | client id.                            |
+    +-----------+---------+---------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -430,11 +490,16 @@ Get the information of the ``spp_vf`` process.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_vfs_get:
+
+.. table:: Request parameter for getting spp_vf.
+
+    +-----------+---------+--------------------------+
+    | Name      | Type    | Description              |
+    |           |         |                          |
+    +===========+=========+==========================+
+    | client_id | integer | client id.               |
+    +-----------+---------+--------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -447,67 +512,92 @@ Request example
 Response
 ^^^^^^^^
 
-+------------------+---------+-----------------------------------------------+
-| Name             | Type    | Description                                   |
-+==================+=========+===============================================+
-| client-id        | integer | client id.                                    |
-+------------------+---------+-----------------------------------------------+
-| ports            | array   | an array of port ids used by the process.     |
-+------------------+---------+-----------------------------------------------+
-| components       | array   | an array of component objects in the process. |
-+------------------+---------+-----------------------------------------------+
-| classifier_table | array   | an array of classifier tables in the process. |
-+------------------+---------+-----------------------------------------------+
+.. _table_spp_ctl_spp_vf_res:
+
+.. table:: Response params of getting spp_vf.
+
+    +------------------+---------+-----------------------------------------------+
+    | Name             | Type    | Description                                   |
+    |                  |         |                                               |
+    +==================+=========+===============================================+
+    | client-id        | integer | client id.                                    |
+    +------------------+---------+-----------------------------------------------+
+    | ports            | array   | an array of port ids used by the process.     |
+    +------------------+---------+-----------------------------------------------+
+    | components       | array   | an array of component objects in the process. |
+    +------------------+---------+-----------------------------------------------+
+    | classifier_table | array   | an array of classifier tables in the process. |
+    +------------------+---------+-----------------------------------------------+
 
 component object:
 
-+---------+---------+---------------------------------------------------------------------+
-| Name    | Type    | Description                                                         |
-+=========+=========+=====================================================================+
-| core    | integer | core id running on the component                                    |
-+---------+---------+---------------------------------------------------------------------+
-| name    | string  | an array of port ids used by the process.                           |
-+---------+---------+---------------------------------------------------------------------+
-| type    | string  | an array of component objects in the process.                       |
-+---------+---------+---------------------------------------------------------------------+
-| rx_port | array   | an array of port objects connected to the rx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
-| tx_port | array   | an array of port objects connected to the tx side of the component. |
-+---------+---------+---------------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_res_comp:
+
+.. table:: Component objects of getting spp_vf.
+
+    +---------+---------+---------------------------------------------------------------------+
+    | Name    | Type    | Description                                                         |
+    |         |         |                                                                     |
+    +=========+=========+=====================================================================+
+    | core    | integer | core id running on the component                                    |
+    +---------+---------+---------------------------------------------------------------------+
+    | name    | string  | an array of port ids used by the process.                           |
+    +---------+---------+---------------------------------------------------------------------+
+    | type    | string  | an array of component objects in the process.                       |
+    +---------+---------+---------------------------------------------------------------------+
+    | rx_port | array   | an array of port objects connected to the rx side of the component. |
+    +---------+---------+---------------------------------------------------------------------+
+    | tx_port | array   | an array of port objects connected to the tx side of the component. |
+    +---------+---------+---------------------------------------------------------------------+
 
 port object:
 
-+---------+---------+---------------------------------------------------------------+
-| Name    | Type    | Description                                                   |
-+=========+=========+===============================================================+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
-+---------+---------+---------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port.                  |
-+---------+---------+---------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_res_port:
+
+.. table:: Port objects of getting spp_vf.
+
+    +---------+---------+---------------------------------------------------------------+
+    | Name    | Type    | Description                                                   |
+    |         |         |                                                               |
+    +=========+=========+===============================================================+
+    | port    | string  | port id. port id is the form {interface_type}:{interface_id}. |
+    +---------+---------+---------------------------------------------------------------+
+    | vlan    | object  | vlan operation which is applied to the port.                  |
+    +---------+---------+---------------------------------------------------------------+
 
 vlan object:
 
-+-----------+---------+-------------------------------+
-| Name      | Type    | Description                   |
-+===========+=========+===============================+
-| operation | string  | ``add``, ``del`` or ``none``. |
-+-----------+---------+-------------------------------+
-| id        | integer | vlan id.                      |
-+-----------+---------+-------------------------------+
-| pcp       | integer | vlan pcp.                     |
-+-----------+---------+-------------------------------+
+.. _table_spp_ctl_spp_vf_res_vlan:
+
+.. table:: Vlan objects of getting spp_vf.
+
+    +-----------+---------+-------------------------------+
+    | Name      | Type    | Description                   |
+    |           |         |                               |
+    +===========+=========+===============================+
+    | operation | string  | ``add``, ``del`` or ``none``. |
+    +-----------+---------+-------------------------------+
+    | id        | integer | vlan id.                      |
+    +-----------+---------+-------------------------------+
+    | pcp       | integer | vlan pcp.                     |
+    +-----------+---------+-------------------------------+
 
 classifier table:
 
-+-----------+--------+------------------------------------------------------------+
-| Name      | Type   | Description                                                |
-+===========+========+============================================================+
-| type      | string | ``mac`` or ``vlan``.                                       |
-+-----------+--------+------------------------------------------------------------+
-| value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
-+-----------+--------+------------------------------------------------------------+
-| port      | string | port id applied to classify.                               |
-+-----------+--------+------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_res_vlan:
+
+.. table:: Vlan objects of getting spp_vf.
+
+    +-----------+--------+------------------------------------------------------------+
+    | Name      | Type   | Description                                                |
+    |           |        |                                                            |
+    +===========+========+============================================================+
+    | type      | string | ``mac`` or ``vlan``.                                       |
+    +-----------+--------+------------------------------------------------------------+
+    | value     | string | mac_address for ``mac``, vlan_id/mac_address for ``vlan``. |
+    +-----------+--------+------------------------------------------------------------+
+    | port      | string | port id applied to classify.                               |
+    +-----------+--------+------------------------------------------------------------+
 
 Response example
 ^^^^^^^^^^^^^^^^
@@ -635,25 +725,34 @@ Start the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_vf_components:
+
+.. table:: Request params of components of spp_vf.
+
+    +-----------+---------+-------------+
+    | Name      | Type    | Description |
+    +===========+=========+=============+
+    | client_id | integer | client id.  |
+    +-----------+---------+-------------+
 
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+-----------+---------+----------------------------------------------------------------------+
-| Name      | Type    | Description                                                          |
-+===========+=========+======================================================================+
-| name      | string  | component name. must be unique in the process.                       |
-+-----------+---------+----------------------------------------------------------------------+
-| core      | integer | core id.                                                             |
-+-----------+---------+----------------------------------------------------------------------+
-| type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
-+-----------+---------+----------------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_components_res:
+
+.. table:: Response params of components of spp_vf.
+
+    +-----------+---------+----------------------------------------------------------------------+
+    | Name      | Type    | Description                                                          |
+    |           |         |                                                                      |
+    +===========+=========+======================================================================+
+    | name      | string  | component name. must be unique in the process.                       |
+    +-----------+---------+----------------------------------------------------------------------+
+    | core      | integer | core id.                                                             |
+    +-----------+---------+----------------------------------------------------------------------+
+    | type      | string  | component type. one of ``forward``, ``merge`` or ``classifier_mac``. |
+    +-----------+---------+----------------------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -688,13 +787,18 @@ Stop the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
+.. _table_spp_ctl_spp_vf_del:
+
+.. table:: Request params of deleting component of spp_vf.
+
+    +-----------+---------+---------------------------------+
+    | Name      | Type    | Description                     |
+    |           |         |                                 |
+    +===========+=========+=================================+
+    | client_id | integer | client id.                      |
+    +-----------+---------+---------------------------------+
+    | name      | string  | component name.                 |
+    +-----------+---------+---------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -728,40 +832,55 @@ Add or Delete port to the component.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-----------------+
-| Name      | Type    | Description     |
-+===========+=========+=================+
-| client_id | integer | client id.      |
-+-----------+---------+-----------------+
-| name      | string  | component name. |
-+-----------+---------+-----------------+
+.. _table_spp_ctl_spp_vf_comp_port:
+
+.. table:: Request params for ports of component of spp_vf.
+
+    +-----------+---------+---------------------------+
+    | Name      | Type    | Description               |
+    |           |         |                           |
+    +===========+=========+===========================+
+    | client_id | integer | client id.                |
+    +-----------+---------+---------------------------+
+    | name      | string  | component name.           |
+    +-----------+---------+---------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+---------+---------+-----------------------------------------------------------------+
-| Name    | Type    | Description                                                     |
-+=========+=========+=================================================================+
-| action  | string  | ``attach`` or ``detach``.                                       |
-+---------+---------+-----------------------------------------------------------------+
-| port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
-+---------+---------+-----------------------------------------------------------------+
-| dir     | string  | ``rx`` or ``tx``.                                               |
-+---------+---------+-----------------------------------------------------------------+
-| vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
-+---------+---------+-----------------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_comp_port_body:
+
+.. table:: Request body params for ports of component of spp_vf.
+
+    +---------+---------+-----------------------------------------------------------------+
+    | Name    | Type    | Description                                                     |
+    |         |         |                                                                 |
+    +=========+=========+=================================================================+
+    | action  | string  | ``attach`` or ``detach``.                                       |
+    +---------+---------+-----------------------------------------------------------------+
+    | port    | string  | port id. port id is the form {interface_type}:{interface_id}.   |
+    +---------+---------+-----------------------------------------------------------------+
+    | dir     | string  | ``rx`` or ``tx``.                                               |
+    +---------+---------+-----------------------------------------------------------------+
+    | vlan    | object  | vlan operation which is applied to the port. it can be omitted. |
+    +---------+---------+-----------------------------------------------------------------+
 
 vlan object:
 
-+-----------+---------+----------------------------------------------------------+
-| Name      | Type    | Description                                              |
-+===========+=========+==========================================================+
-| operation | string  | ``add``, ``del`` or ``none``.                            |
-+-----------+---------+----------------------------------------------------------+
-| id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
-+-----------+---------+----------------------------------------------------------+
-| pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
-+-----------+---------+----------------------------------------------------------+
+.. _table_spp_ctl_spp_vf_comp_port_body_vlan:
+
+.. table:: Request body params for vlan ports of component of spp_vf.
+
+    +-----------+---------+----------------------------------------------------------+
+    | Name      | Type    | Description                                              |
+    |           |         |                                                          |
+    +===========+=========+==========================================================+
+    | operation | string  | ``add``, ``del`` or ``none``.                            |
+    +-----------+---------+----------------------------------------------------------+
+    | id        | integer | vlan id. ignored when operation is ``del`` or ``none``.  |
+    +-----------+---------+----------------------------------------------------------+
+    | pcp       | integer | vlan pcp. ignored when operation is ``del`` or ``none``. |
+    +-----------+---------+----------------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
@@ -811,28 +930,38 @@ Set or Unset classifier table.
 Request(path)
 ^^^^^^^^^^^^^
 
-+-----------+---------+-------------+
-| Name      | Type    | Description |
-+===========+=========+=============+
-| client_id | integer | client id.  |
-+-----------+---------+-------------+
+.. _table_spp_ctl_spp_vf_cls_table:
+
+.. table:: Request params for classifier_table of spp_vf.
+
+    +-----------+---------+---------------------------+
+    | Name      | Type    | Description               |
+    |           |         |                           |
+    +===========+=========+===========================+
+    | client_id | integer | client id.                |
+    +-----------+---------+---------------------------+
 
 Request(body)
 ^^^^^^^^^^^^^
 
-+-------------+-----------------+----------------------------------------------------+
-| Name        | Type            | Description                                        |
-+=============+=================+====================================================+
-| action      | string          | ``add`` or ``del``.                                |
-+-------------+-----------------+----------------------------------------------------+
-| type        | string          | ``mac`` or ``vlan``.                               |
-+-------------+-----------------+----------------------------------------------------+
-| vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
-+-------------+-----------------+----------------------------------------------------+
-| mac_address | string          | mac address.                                       |
-+-------------+-----------------+----------------------------------------------------+
-| port        | string          | port id.                                           |
-+-------------+-----------------+----------------------------------------------------+
+.. _table_spp_ctl_spp_vf_cls_table_body:
+
+.. table:: Request body params for classifier_table of spp_vf.
+
+    +-------------+-----------------+----------------------------------------------------+
+    | Name        | Type            | Description                                        |
+    |             |                 |                                                    |
+    +=============+=================+====================================================+
+    | action      | string          | ``add`` or ``del``.                                |
+    +-------------+-----------------+----------------------------------------------------+
+    | type        | string          | ``mac`` or ``vlan``.                               |
+    +-------------+-----------------+----------------------------------------------------+
+    | vlan        | integer or null | vlan id for ``vlan``. null or omitted for ``mac``. |
+    +-------------+-----------------+----------------------------------------------------+
+    | mac_address | string          | mac address.                                       |
+    +-------------+-----------------+----------------------------------------------------+
+    | port        | string          | port id.                                           |
+    +-------------+-----------------+----------------------------------------------------+
 
 Request example
 ^^^^^^^^^^^^^^^
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [spp] [PATCH v4 14/14] spp-ctl: fix incorrect URL
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (12 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 13/14] docs: add labels and captions for tables oda
@ 2018-10-05  3:57   ` oda
  2018-10-09  2:01   ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API Yasufumi Ogawa
  14 siblings, 0 replies; 33+ messages in thread
From: oda @ 2018-10-05  3:57 UTC (permalink / raw)
  To: spp, ferruh.yigit, ogawa.yasufumi

From: Itsuro Oda <oda@valinux.co.jp>

This patch fixes an incorrect URL.

Signed-off-by: Itsuro Oda <oda@valinux.co.jp>
---
 src/spp-ctl/spp_webapi.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
index ee47f2f..ddf6513 100644
--- a/src/spp-ctl/spp_webapi.py
+++ b/src/spp-ctl/spp_webapi.py
@@ -171,7 +171,7 @@ class V1VFHandler(BaseHandler):
                    callback=self.vf_comp_start)
         self.route('/<sec_id:int>/components/<name>', 'DELETE',
                    callback=self.vf_comp_stop)
-        self.route('/<sec_id:int>/components/<name>', 'PUT',
+        self.route('/<sec_id:int>/components/<name>/ports', 'PUT',
                    callback=self.vf_comp_port)
         self.route('/<sec_id:int>/classifier_table', 'PUT',
                    callback=self.vf_classifier)
-- 
2.17.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API
  2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
                     ` (13 preceding siblings ...)
  2018-10-05  3:57   ` [spp] [PATCH v4 14/14] spp-ctl: fix incorrect URL oda
@ 2018-10-09  2:01   ` Yasufumi Ogawa
  14 siblings, 0 replies; 33+ messages in thread
From: Yasufumi Ogawa @ 2018-10-09  2:01 UTC (permalink / raw)
  To: oda, spp; +Cc: ferruh.yigit

> From: Itsuro Oda <oda@valinux.co.jp>
> 
> spp-ctl is a SPP controller with a REST like web API.
> 
> spp-ctl maintains the connections from the SPP processes and at
> the same time exposes the API for the user to request for the
> SPP processes.
> 
> Background and motivation:
> 
> Current CLI (spp.py/spp_vf.py) can be used by intaractive only.
> Therefore, spp-agent, a component of networking-spp which make
> SPP available on OpenStack environment, implements SPP controller
> in itself. (see. https://github.com/openstack/networking-spp )
> 
> Either CLI or spp-agent, there is a problem that other people can
> not request to SPP processes while using. spp-ctl is invented to
> solve this problem.
> 
> Both CLI and spp-agent can be used spp-ctl to request SPP
> processes instead of owning contoroller itself. In that case,
> multiple people can request to SPP processes at the same time.
> Note that spp-agent has a plan to change to use spp-ctl.
> It is also available not using CLI but requesting spp-ctl
> directly.
Thanks a lot!

Acked-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
> 
> ---
> v4:
> * fix incorrect URL
> 
> v3:
> * reflect the points which Yasufumi indicated.
> 
> v2:
> * divide a patch
> 
> Itsuro Oda (10):
>    docs: add overview of spp-ctl
>    docs: add API reference of spp-ctl
>    docs: add index of spp-ctl
>    project: add requirements.txt for spp-ctl
>    docs: add spp-ctl to index of doc root
>    spp-ctl: add entry point
>    spp-ctl: add Controller class
>    spp-ctl: add web API handler
>    spp-ctl: add spp command interfaces
>    spp-ctl: fix incorrect URL
> 
> Yasufumi Ogawa (4):
>    spp-ctl: update parsing spp_nfv status
>    docs: add request examples of spp-ctl
>    docs: correct directives of spp-ctl
>    docs: add labels and captions for tables
> 
>   docs/guides/index.rst                 |    1 +
>   docs/guides/spp-ctl/api-reference.rst | 1001 +++++++++++++++++++++++++
>   docs/guides/spp-ctl/index.rst         |   14 +
>   docs/guides/spp-ctl/overview.rst      |  119 +++
>   requirements.txt                      |    4 +
>   src/spp-ctl/spp-ctl                   |   11 +
>   src/spp-ctl/spp_ctl.py                |  158 ++++
>   src/spp-ctl/spp_proc.py               |  187 +++++
>   src/spp-ctl/spp_webapi.py             |  431 +++++++++++
>   9 files changed, 1926 insertions(+)
>   create mode 100644 docs/guides/spp-ctl/api-reference.rst
>   create mode 100644 docs/guides/spp-ctl/index.rst
>   create mode 100644 docs/guides/spp-ctl/overview.rst
>   create mode 100644 requirements.txt
>   create mode 100644 src/spp-ctl/spp-ctl
>   create mode 100644 src/spp-ctl/spp_ctl.py
>   create mode 100644 src/spp-ctl/spp_proc.py
>   create mode 100644 src/spp-ctl/spp_webapi.py
>

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2018-10-09  2:03 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-12 23:25 [spp] [PATCH] spp-ctl: SPP controller with Web API Itsuro ODA
2018-09-18 10:00 ` Yasufumi Ogawa
2018-09-18 21:40   ` Itsuro ODA
2018-10-05  1:37 ` [spp] [PATCH v3 00/13] " oda
2018-10-05  1:37   ` [spp] [PATCH v3 01/13] docs: add overview of spp-ctl oda
2018-10-05  1:37   ` [spp] [PATCH v3 02/13] docs: add API reference " oda
2018-10-05  1:37   ` [spp] [PATCH v3 03/13] docs: add index " oda
2018-10-05  1:37   ` [spp] [PATCH v3 04/13] project: add requirements.txt for spp-ctl oda
2018-10-05  1:37   ` [spp] [PATCH v3 05/13] docs: add spp-ctl to index of doc root oda
2018-10-05  1:37   ` [spp] [PATCH v3 06/13] spp-ctl: add entry point oda
2018-10-05  1:37   ` [spp] [PATCH v3 07/13] spp-ctl: add Controller class oda
2018-10-05  1:37   ` [spp] [PATCH v3 08/13] spp-ctl: add web API handler oda
2018-10-05  1:37   ` [spp] [PATCH v3 09/13] spp-ctl: add spp command interfaces oda
2018-10-05  1:37   ` [spp] [PATCH v3 10/13] spp-ctl: update parsing spp_nfv status oda
2018-10-05  1:37   ` [spp] [PATCH v3 11/13] docs: add request examples of spp-ctl oda
2018-10-05  1:37   ` [spp] [PATCH v3 12/13] docs: correct directives " oda
2018-10-05  1:37   ` [spp] [PATCH v3 13/13] docs: add labels and captions for tables oda
2018-10-05  3:57 ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API oda
2018-10-05  3:57   ` [spp] [PATCH v4 01/14] docs: add overview of spp-ctl oda
2018-10-05  3:57   ` [spp] [PATCH v4 02/14] docs: add API reference " oda
2018-10-05  3:57   ` [spp] [PATCH v4 03/14] docs: add index " oda
2018-10-05  3:57   ` [spp] [PATCH v4 04/14] project: add requirements.txt for spp-ctl oda
2018-10-05  3:57   ` [spp] [PATCH v4 05/14] docs: add spp-ctl to index of doc root oda
2018-10-05  3:57   ` [spp] [PATCH v4 06/14] spp-ctl: add entry point oda
2018-10-05  3:57   ` [spp] [PATCH v4 07/14] spp-ctl: add Controller class oda
2018-10-05  3:57   ` [spp] [PATCH v4 08/14] spp-ctl: add web API handler oda
2018-10-05  3:57   ` [spp] [PATCH v4 09/14] spp-ctl: add spp command interfaces oda
2018-10-05  3:57   ` [spp] [PATCH v4 10/14] spp-ctl: update parsing spp_nfv status oda
2018-10-05  3:57   ` [spp] [PATCH v4 11/14] docs: add request examples of spp-ctl oda
2018-10-05  3:57   ` [spp] [PATCH v4 12/14] docs: correct directives " oda
2018-10-05  3:57   ` [spp] [PATCH v4 13/14] docs: add labels and captions for tables oda
2018-10-05  3:57   ` [spp] [PATCH v4 14/14] spp-ctl: fix incorrect URL oda
2018-10-09  2:01   ` [spp] [PATCH v4 00/14] spp-ctl: SPP controller with Web API Yasufumi Ogawa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).