From: x-fn-spp-ml@ntt-tx.co.jp
To: ferruh.yigit@intel.com, yasufum.o@gmail.com
Cc: spp@dpdk.org
Subject: [spp] [PATCH 16/17] spp-ctl: add APIs for flow rules
Date: Tue, 18 Feb 2020 15:37:19 +0900 [thread overview]
Message-ID: <20200218063720.6597-17-x-fn-spp-ml@ntt-tx.co.jp> (raw)
In-Reply-To: <20200218063720.6597-1-x-fn-spp-ml@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: Yasufumi Ogawa <yasufum.o@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
next prev parent reply other threads:[~2020-02-18 6:37 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-18 6:37 [spp] [PATCH 00/17] Adding Hardware offload capability x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 01/17] shared: add support of multi-queue x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 02/17] spp_vf: " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 03/17] spp_mirror: " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 04/17] spp_pcap: " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 05/17] spp_primary: " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 06/17] spp_primary: add support of rte_flow x-fn-spp-ml
2020-02-19 2:24 ` Yasufumi Ogawa
2020-02-19 11:57 ` [spp] (x-fn-spp-ml 118) " Hideyuki Yamashita
2020-02-18 6:37 ` [spp] [PATCH 07/17] spp_primary: add common function " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 08/17] spp_primary: add attribute " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 09/17] spp_primary: add patterns " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 10/17] spp_primary: add actions " x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 11/17] bin: add parameter for hardrare offload x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 12/17] cli: add support of hardware offload x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 13/17] cli: add support of rte_flow in vf x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 14/17] cli: add support of rte_flow in mirror x-fn-spp-ml
2020-02-18 6:37 ` [spp] [PATCH 15/17] cli: add support of rte_flow in nfv x-fn-spp-ml
2020-02-18 6:37 ` x-fn-spp-ml [this message]
2020-02-18 6:37 ` [spp] [PATCH 17/17] spp_nfv: add support of multi-queue x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 00/17] Adding Hardware offload capability x-fn-spp-ml
2020-02-21 8:17 ` Yasufumi Ogawa
2020-02-25 5:49 ` [spp] (x-fn-spp-ml 177) " Hideyuki Yamashita
2020-02-19 11:49 ` [spp] [PATCH v2 01/17] shared: add support of multi-queue x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 02/17] spp_vf: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 03/17] spp_mirror: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 04/17] spp_pcap: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 05/17] spp_primary: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 06/17] spp_primary: add support of rte_flow x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 07/17] spp_primary: add common function " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 08/17] spp_primary: add attribute " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 09/17] spp_primary: add patterns " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 10/17] spp_primary: add actions " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 11/17] bin: add parameter for hardrare offload x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 12/17] cli: add support of hardware offload x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 13/17] cli: add support of rte_flow in vf x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 14/17] cli: add support of rte_flow in mirror x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 15/17] cli: add support of rte_flow in nfv x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 16/17] spp-ctl: add APIs for flow rules x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 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=20200218063720.6597-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).