Soft Patch Panel
 help / color / mirror / Atom feed
From: x-fn-spp-ml@ntt-tx.co.jp
To: spp@dpdk.org, ferruh.yigit@intel.com, yasufum.o@gmail.com
Subject: [spp] [PATCH v3 16/17] spp-ctl: add APIs for flow rules
Date: Tue, 25 Feb 2020 14:56:38 +0900	[thread overview]
Message-ID: <20200225055639.31616-17-x-fn-spp-ml@ntt-tx.co.jp> (raw)
In-Reply-To: <20200219112155.13964-1-yamashita.hideyuki@ntt-tx.co.jp>

From: Hideyuki Yamashita <yamashita.hideyuki@ntt-tx.co.jp>

This patch implements support of flow APIs in spp-ctl.
  - flow validate
  - flow create
  - flow delete
  - flow flush

Signed-off-by: Hideyuki Yamashita <yamashita.hideyuki@ntt-tx.co.jp>
Signed-off-by: Naoki Takada <ntakada14@gmail.com>
---
 src/spp-ctl/spp_proc.py   |   4 +
 src/spp-ctl/spp_webapi.py | 200 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 194 insertions(+), 10 deletions(-)

diff --git a/src/spp-ctl/spp_proc.py b/src/spp-ctl/spp_proc.py
index 35919fb..d584f7f 100644
--- a/src/spp-ctl/spp_proc.py
+++ b/src/spp-ctl/spp_proc.py
@@ -317,6 +317,10 @@ class PrimaryProc(SppProc):
     def stop(self):
         return "stop"
 
+    @exec_command
+    def flow(self, command):
+        return command
+
     @exec_command
     def do_launch_sec_proc(self, args):
         proc_name = args['proc_name']
diff --git a/src/spp-ctl/spp_webapi.py b/src/spp-ctl/spp_webapi.py
index 31befe2..d8e6e73 100644
--- a/src/spp-ctl/spp_webapi.py
+++ b/src/spp-ctl/spp_webapi.py
@@ -34,6 +34,20 @@ class KeyInvalid(bottle.HTTPError):
         super(KeyRequired, self).__init__(400, msg)
 
 
+class RequestJSONDecodeHTTPError(bottle.HTTPError):
+
+    def __init__(self):
+        msg = "Not in json format."
+        super().__init__(400, msg)
+
+
+class ResponseJSONDecodeHTTPError(bottle.HTTPError):
+
+    def __init__(self):
+        msg = "Internal Server Error"
+        super().__init__(500, msg)
+
+
 class BaseHandler(bottle.Bottle):
     """Define common methods for each handler."""
 
@@ -55,7 +69,12 @@ class BaseHandler(bottle.Bottle):
             if_type, if_num = port.split(":")
             if if_type not in PORT_TYPES:
                 raise
-            int(if_num)
+            if if_type == "phy" and "nq" in if_num:
+                port_num, queue_num = if_num.split("nq")
+                int(port_num)
+                int(queue_num)
+            else:
+                int(if_num)
         except Exception:
             raise KeyInvalid('port', port)
 
@@ -75,12 +94,15 @@ class BaseHandler(bottle.Bottle):
         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())
+                try:
+                    if req.get_header('Content-Type') == "application/json":
+                        body = req.json
+                    else:
+                        body = json.loads(req.body.read().decode())
+                    LOG.info("body: %s", body)
+                except Exception:
+                    raise RequestJSONDecodeHTTPError()
                 kwargs['body'] = body
-                LOG.info("body: %s", body)
             return func(*args, **kwargs)
         return wrapper
 
@@ -103,10 +125,15 @@ class BaseHandler(bottle.Bottle):
             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
+
+            try:
+                body = json.dumps(ret)
+            except Exception:
+                raise ResponseJSONDecodeHTTPError()
+
+            r = bottle.HTTPResponse(status=200, body=body)
+            r.content_type = "application/json"
+            return r
         return wrapper
 
 
@@ -446,12 +473,16 @@ class V1PrimaryHandler(BaseHandler):
 
     def __init__(self, controller):
         super(V1PrimaryHandler, self).__init__(controller)
+        self._initialize()
 
+    def _initialize(self):
         self.set_route()
 
         self.install(self.make_response)
         self.install(self.get_body)
 
+        self.mount("/flow_rules", V1PrimaryFlowHandler(self.ctrl))
+
     def set_route(self):
         self.route('/status', 'GET', callback=self.get_status)
         self.route('/status', 'DELETE', callback=self.clear_status)
@@ -556,6 +587,155 @@ class V1PrimaryHandler(BaseHandler):
         proc.do_exit()
 
 
+class V1PrimaryFlowHandler(V1PrimaryHandler):
+
+    def __init__(self, controller):
+        super().__init__(controller)
+
+    def _initialize(self):
+        self.set_route()
+
+        self.install(self.make_response)
+        self.install(self.get_body)
+
+    def set_route(self):
+        self.route('/port_id/<port_id:int>/validate',
+                   'POST', callback=self.post_flow_validate)
+        self.route('/port_id/<port_id:int>',
+                   'POST', callback=self.post_flow_create)
+        self.route('/port_id/<port_id:int>',
+                   'DELETE', callback=self.delete_flow_all_destroy)
+        self.route('/<rule_id:int>/port_id/<port_id:int>',
+                   'DELETE', callback=self.delete_flow_destroy)
+
+    def post_flow_validate(self, port_id, body):
+        self._check_request_body(body)
+        command = self._create_flow_rule_command(
+            port_id, body.get("rule"), "validate")
+
+        proc = self._get_proc()
+        return proc.flow(command)
+
+    def post_flow_create(self, port_id, body):
+        self._check_request_body(body)
+        command = self._create_flow_rule_command(
+            port_id, body.get("rule"), "create")
+
+        proc = self._get_proc()
+        return proc.flow(command)
+
+    def delete_flow_all_destroy(self, port_id):
+        command = self._gen_flow_destroy(port_id)
+
+        proc = self._get_proc()
+        return proc.flow(command)
+
+    def delete_flow_destroy(self, rule_id, port_id):
+        command = self._gen_flow_destroy(port_id, rule_id)
+
+        proc = self._get_proc()
+        return proc.flow(command)
+
+    def _create_flow_rule_command(self, port_id, rule, sub_command):
+        attr_data = {}
+        data = {}
+
+        # `group`,` priority`, and `transfer` in` attrs` are optional and
+        # may be omitted
+        attr_command = "{group}{priority}{transfer}{direction}"
+
+        attr_data["direction"] = rule.get("direction")
+
+        if "group" in rule:
+            attr_data["group"] = "group {0} ".format(rule.get("group"))
+        else:
+            attr_data["group"] = ""
+
+        if "priority" in rule:
+            attr_data["priority"] = "priority {0} ".format(
+                rule.get("priority"))
+        else:
+            attr_data["priority"] = ""
+
+        if "transfer" in rule:
+            attr_data["transfer"] = "transfer " if rule.get("transfer") else ""
+        else:
+            attr_data["transfer"] = ""
+
+        attrs = attr_command.format(**attr_data)
+
+        command = "flow {sub_command} {res_uid} {attrs} "
+        command += "pattern {pattern} / end "
+        command += "actions {actions} / end"
+
+        data["sub_command"] = sub_command
+        data["res_uid"] = "phy:{0}".format(port_id)
+        data["attrs"] = attrs
+        data["pattern"] = " / ".join(rule.get("pattern"))
+        data["actions"] = " / ".join(rule.get("actions"))
+
+        return command.format(**data)
+
+    def _gen_flow_destroy(self, port_id, rule_id=None):
+        """Delete a flow of given rule ID, or all flows if the ID is None."""
+        if rule_id is not None:
+            target = int(rule_id)
+        else:
+            target = "ALL"
+        return "flow destroy phy:{0} {1}".format(port_id, target)
+
+    def _check_request_body(self, body):
+        self._check_request_body_required_param(body, "rule", dict)
+        rule = body.get("rule")
+
+        self._check_request_body_optional_param(rule, "group", int)
+        self._check_request_body_optional_param(rule, "priority", int)
+        self._check_request_body_required_param(rule, "direction", str)
+        self._check_request_body_optional_param(rule, "transfer", bool)
+        self._check_request_body_required_param(rule, "pattern", list)
+        self._check_request_body_required_param(rule, "actions", list)
+
+        dir = rule.get("direction")
+        if dir != "ingress" and dir != "egress":
+            raise KeyInvalid("direction", dir)
+
+        pattern = rule.get("pattern")
+        for obj in pattern:
+            if obj is None or type(obj) != str:
+                raise KeyInvalid("pattern", pattern)
+
+        actions = rule.get("actions")
+        for obj in actions:
+            if obj is None or type(obj) != str:
+                raise KeyInvalid("actions", actions)
+
+    def _check_request_body_optional_param(self, target, key_name, obj_type):
+        """Check for optional parameter.
+
+        If key_name exists in dict, checking obj_type.
+        If invalid, raise error class. Return True if valid.
+        """
+        if key_name not in target:
+            return True
+        return self._check_request_body_required_param(
+            target, key_name, obj_type)
+
+    def _check_request_body_required_param(self, target, key_name, obj_type):
+        """Check for required parameter.
+
+        key_name must be present and check obj_type.
+        If invalid, raise error class. Return True if valid.
+        """
+        if key_name not in target:
+            raise KeyRequired(key_name)
+
+        obj = target.get(key_name)
+        if obj is None or type(obj) != obj_type:
+            raise KeyInvalid(key_name, obj)
+
+        return True
+
+
 class V1PcapHandler(BaseHandler):
 
     def __init__(self, controller):
-- 
2.17.1


  parent reply	other threads:[~2020-02-25  5:57 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20200219112155.13964-1-yamashita.hideyuki@ntt-tx.co.jp>
2020-02-25  5:56 ` [spp] [PATCH v3 00/17] Adding Hardware offload capability x-fn-spp-ml
2020-02-25 10:37   ` Yasufumi Ogawa
2020-02-25  5:56 ` [spp] [PATCH v3 01/17] shared: add support of multi-queue x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 02/17] spp_vf: " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 03/17] spp_mirror: " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 04/17] spp_pcap: " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 05/17] spp_primary: " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 06/17] spp_primary: add support of rte_flow x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 07/17] spp_primary: add common function " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 08/17] spp_primary: add attribute " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 09/17] spp_primary: add patterns " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 10/17] spp_primary: add actions " x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 11/17] bin: add parameter for hardrare offload x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 12/17] cli: add support of hardware offload x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 13/17] cli: add support of rte_flow in vf x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 14/17] cli: add support of rte_flow in mirror x-fn-spp-ml
2020-02-25  5:56 ` [spp] [PATCH v3 15/17] cli: add support of rte_flow in nfv x-fn-spp-ml
2020-02-25  5:56 ` x-fn-spp-ml [this message]
2020-02-25  5:56 ` [spp] [PATCH v3 17/17] spp_nfv: add support of multi-queue x-fn-spp-ml

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200225055639.31616-17-x-fn-spp-ml@ntt-tx.co.jp \
    --to=x-fn-spp-ml@ntt-tx.co.jp \
    --cc=ferruh.yigit@intel.com \
    --cc=spp@dpdk.org \
    --cc=yasufum.o@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).