get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/76828/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 76828,
    "url": "http://patches.dpdk.org/api/patches/76828/?format=api",
    "web_url": "http://patches.dpdk.org/project/dts/patch/20200908032645.11852-4-qimaix.xiao@intel.com/",
    "project": {
        "id": 3,
        "url": "http://patches.dpdk.org/api/projects/3/?format=api",
        "name": "DTS",
        "link_name": "dts",
        "list_id": "dts.dpdk.org",
        "list_email": "dts@dpdk.org",
        "web_url": "",
        "scm_url": "git://dpdk.org/tools/dts",
        "webscm_url": "http://git.dpdk.org/tools/dts/",
        "list_archive_url": "https://inbox.dpdk.org/dts",
        "list_archive_url_format": "https://inbox.dpdk.org/dts/{}",
        "commit_url_format": ""
    },
    "msgid": "<20200908032645.11852-4-qimaix.xiao@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dts/20200908032645.11852-4-qimaix.xiao@intel.com",
    "date": "2020-09-08T03:26:41",
    "name": "[V1,3/7] framework/packet: update packet module for saving sending packet time",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "6027885375ad44ba47020f1d51bd0a2e72ed6e64",
    "submitter": {
        "id": 1303,
        "url": "http://patches.dpdk.org/api/people/1303/?format=api",
        "name": "Xiao, QimaiX",
        "email": "qimaix.xiao@intel.com"
    },
    "delegate": null,
    "mbox": "http://patches.dpdk.org/project/dts/patch/20200908032645.11852-4-qimaix.xiao@intel.com/mbox/",
    "series": [
        {
            "id": 12007,
            "url": "http://patches.dpdk.org/api/series/12007/?format=api",
            "web_url": "http://patches.dpdk.org/project/dts/list/?series=12007",
            "date": "2020-09-08T03:26:38",
            "name": "update packet related modules",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/12007/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/76828/comments/",
    "check": "pending",
    "checks": "http://patches.dpdk.org/api/patches/76828/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dts-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id BD2A7A04AA;\n\tTue,  8 Sep 2020 05:51:34 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id B01911C19B;\n\tTue,  8 Sep 2020 05:51:34 +0200 (CEST)",
            "from mga03.intel.com (mga03.intel.com [134.134.136.65])\n by dpdk.org (Postfix) with ESMTP id 9E54E1C19B\n for <dts@dpdk.org>; Tue,  8 Sep 2020 05:51:33 +0200 (CEST)",
            "from fmsmga001.fm.intel.com ([10.253.24.23])\n by orsmga103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 07 Sep 2020 20:51:32 -0700",
            "from unknown (HELO localhost.localdomain) ([10.240.183.52])\n by fmsmga001.fm.intel.com with ESMTP; 07 Sep 2020 20:51:31 -0700"
        ],
        "IronPort-SDR": [
            "\n JJ9HVxxgiXkQ642SvDqTBBtJTa7DRcfkQUDTwjQSzegOLr7uxigRKrPGXLWJqPY0hGf5LPFPp8\n X3U0xGMXrh/Q==",
            "\n vihbIlF2WkgbxkrM7QRsZBo8dmhR2h13zV75A9y/5Kw8S9qRHkSAl9WqQCFT5I+pKUzqCZ4htL\n o/Y57dLm2Mag=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6000,8403,9737\"; a=\"158088235\"",
            "E=Sophos;i=\"5.76,404,1592895600\"; d=\"scan'208\";a=\"158088235\"",
            "E=Sophos;i=\"5.76,404,1592895600\"; d=\"scan'208\";a=\"407003005\""
        ],
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "From": "Xiao Qimai <qimaix.xiao@intel.com>",
        "To": "dts@dpdk.org",
        "Cc": "Xiao Qimai <qimaix.xiao@intel.com>",
        "Date": "Tue,  8 Sep 2020 03:26:41 +0000",
        "Message-Id": "<20200908032645.11852-4-qimaix.xiao@intel.com>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<20200908032645.11852-1-qimaix.xiao@intel.com>",
        "References": "<20200908032645.11852-1-qimaix.xiao@intel.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dts] [PATCH V1 3/7]framework/packet: update packet module for\n\tsaving sending packet time",
        "X-BeenThere": "dts@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "test suite reviews and discussions <dts.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dts>,\n <mailto:dts-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dts/>",
        "List-Post": "<mailto:dts@dpdk.org>",
        "List-Help": "<mailto:dts-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dts>,\n <mailto:dts-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dts-bounces@dpdk.org",
        "Sender": "\"dts\" <dts-bounces@dpdk.org>"
    },
    "content": "Signed-off-by: Xiao Qimai <qimaix.xiao@intel.com>\n---\n framework/packet.py | 159 +++++++++++++++++++++++---------------------\n 1 file changed, 83 insertions(+), 76 deletions(-)",
    "diff": "diff --git a/framework/packet.py b/framework/packet.py\nindex 603840b4..6a306cba 100644\n--- a/framework/packet.py\n+++ b/framework/packet.py\n@@ -34,35 +34,54 @@ Base on scapy(python program for packet manipulation)\n \"\"\"\n \n from socket import AF_INET6\n+from importlib import import_module\n from scapy.all import *\n-from scapy.layers.sctp import SCTP, SCTPChunkData\n-\n # load extension layers\n exec_file = os.path.realpath(__file__)\n DTS_PATH = exec_file.replace('/framework/packet.py', '')\n # exec_file might be .pyc file, if so, remove 'c'.\n TMP_PATH = DTS_PATH[:-1] + '/output/tmp/pcap/' if exec_file.endswith('.pyc') else DTS_PATH + '/output/tmp/pcap/'\n-\n if not os.path.exists(TMP_PATH):\n     os.system('mkdir -p %s' % TMP_PATH)\n+\n DEP_FOLDER = DTS_PATH + '/dep'\n sys.path.append(DEP_FOLDER)\n-\n-from vxlan import VXLAN\n-from nvgre import NVGRE, IPPROTO_NVGRE\n-from scapy.contrib.gtp import *\n-from lldp import LLDP, LLDPManagementAddress\n-from Dot1BR import Dot1BR\n-from nsh import NSH\n-from mpls import MPLS\n-from igmp import IGMP\n-from pfcp import PFCP\n+sys.path.append(DEP_FOLDER + '/scapy_modules')\n \n from utils import convert_ip2int\n from utils import convert_int2ip\n \n-# for saving command history\n-from utils import get_backtrace_object\n+scapy_modules_required = {'nvgre': ['NVGRE', 'IPPROTO_NVGRE'],\n+                          'gtp': ['GTP_U_Header', 'GTP_PDUSession_ExtensionHeader'],\n+                          'lldp': ['LLDP', 'LLDPManagementAddress'], 'Dot1BR': ['Dot1BR'], 'pfcp': ['PFCP'],\n+                          'nsh': ['NSH'], 'igmp': ['IGMP'], 'mpls': ['MPLS'], 'sctp': ['SCTP', 'SCTPChunkData'], 'vxlan': ['VXLAN']}\n+local_modules = [m[:-3] for m in os.listdir(DEP_FOLDER + '/scapy_modules') if (m.endswith('.py') and not m.startswith('__'))]\n+\n+for m in scapy_modules_required:\n+    if m in local_modules:\n+        module = import_module(m)\n+        for clazz in scapy_modules_required[m]:\n+            locals().update({clazz: getattr(module, clazz)})\n+    else:\n+        if m == 'sctp':\n+            module = import_module(f'scapy.layers.{m}')\n+            for clazz in scapy_modules_required[m]:\n+                locals().update({clazz: getattr(module, clazz)})\n+        else:\n+            module = import_module(f'scapy.contrib.{m}')\n+            for clazz in scapy_modules_required[m]:\n+                locals().update({clazz: getattr(module, clazz)})\n+\n+def get_scapy_module_impcmd():\n+    cmd_li = list()\n+    for m in scapy_modules_required:\n+        if m in local_modules:\n+            cmd_li.append(f'from {m} import {\",\".join(scapy_modules_required[m])}')\n+        else:\n+            cmd_li.append(f'from scapy.contirb.{m} import {\",\".join(scapy_modules_required[m])}')\n+    return ';'.join(cmd_li)\n+\n+SCAPY_IMP_CMD = get_scapy_module_impcmd()\n \n # packet generator type should be configured later\n PACKETGEN = \"scapy\"\n@@ -139,7 +158,7 @@ class scapy(object):\n \n     def __init__(self):\n         self.pkt = None\n-        self.pkts = []\n+        self.pkts = list()\n \n     def append_pkts(self):\n         self.pkts.append(self.pkt)\n@@ -329,7 +348,7 @@ class scapy(object):\n         pkt_layer.vni = vni\n \n     def nsh(self, pkt_layer, ver=0, oam=0, critical=0, reserved=0, len=0, mdtype=1, nextproto=3,\n-            nsp=0x0, nsi=1, npc= 0x0, nsc= 0x0, spc= 0x0, ssc= 0x0):\n+            nsp=0x0, nsi=1, npc=0x0, nsc=0x0, spc=0x0, ssc=0x0):\n         pkt_layer.Ver = ver\n         pkt_layer.OAM = oam\n         pkt_layer.Critical = critical\n@@ -354,7 +373,6 @@ class scapy(object):\n \n \n class Packet(object):\n-\n     \"\"\"\n     Module for config/create packet\n     Based on scapy module\n@@ -469,8 +487,7 @@ class Packet(object):\n         layer_li = [re.sub('\\(.*?\\)', '', i) for i in scapy_str.split('/')]\n         self.pkt_type = '_'.join(layer_li)\n         self._load_pkt_layers()\n-        pkt = eval(scapy_str)\n-        self.pktgen.assign_pkt(pkt)\n+        self.pktgen.assign_pkt(scapy_str)\n \n     def append_pkt(self, args=None, **kwargs):\n         \"\"\"\n@@ -641,67 +658,58 @@ class Packet(object):\n             self.pktgen.pkts.append(i)\n         return p\n \n-    def _send_pkt(self, crb, tx_port='', count=1, send_bg=False, loop=0, inter=0, timeout=15):\n+    def send_pkt_bg_with_pcapfile(self, crb, tx_port='', count=1, loop=0, inter=0):\n         \"\"\"\n-\n+        send packet background with a pcap file, got an advantage in sending a large number of packets\n         :param crb: session or crb object\n         :param tx_port: ether to send packet\n         :param count: send times\n-        :param send_bg: send packet background\n         :param loop: send packet in a loop\n-        :param inter: interval time\n-        :return: None\n+        :param inter: interval time per packet\n+        :return: send session\n         \"\"\"\n-        # save pkts to local pcap file, then copy to remote tester tmp directory\n-\n-        time_stamp = str(time.time())\n-        pcap_file = 'scapy_{}.pcap'.format(tx_port) + time_stamp\n-        self.save_pcapfile(crb, pcap_file)\n-        scapy_cmd = 'scapy_{}.cmd'.format(tx_port) + time_stamp\n-        cmd_str = 'from scapy.all import *\\np=rdpcap(\"%s\")\\nprint(\"packet ready for sending...\")\\nfor i in p:\\n\\tprint(i.command())\\nsendp(p, iface=\"%s\", count=%d, loop=%d, inter=%0.3f,  verbose=False)' % (\n-            crb.tmp_file + pcap_file, tx_port, count, loop, inter)\n-        # write send cmd file to local tmp directory then copy to remote tester tmp folder\n-        with open(TMP_PATH + scapy_cmd, 'w') as f:\n-            f.write(cmd_str)\n-        crb.session.copy_file_to(TMP_PATH + scapy_cmd, crb.tmp_file)\n-\n-        if send_bg:  # if send_bg create a new session to execute send action\n-            session_prefix = 'scapy_bg_session'\n-            scapy_session = crb.create_session(session_prefix + time_stamp)\n-            scapy_session.send_command('python3 %s' % crb.tmp_file + scapy_cmd)\n+        if crb.name != 'tester':\n+            raise Exception('crb should be tester')\n+        wrpcap('_', self.pktgen.pkts)\n+        file_path = '/tmp/%s.pcap' % tx_port\n+        scapy_session_bg = crb.prepare_scapy_env()\n+        scapy_session_bg.copy_file_to('_', file_path)\n+        scapy_session_bg.send_expect('pkts = rdpcap(\"%s\")' % file_path, '>>> ')\n+        scapy_session_bg.send_command('sendp(pkts, iface=\"%s\",count=%s,loop=%s,inter=%s)' % (tx_port, count, loop, inter))\n+        return scapy_session_bg\n+\n+    def _recompose_pkts_str(self, pkts_str):\n+        method_pattern = re.compile('<.+?>')\n+        method_li = method_pattern.findall(pkts_str)\n+        for i in method_li:\n+            pkts_str = method_pattern.sub(i.strip('<>')+'()', pkts_str, count=1)\n+        return pkts_str\n+\n+    def send_pkt(self, crb, tx_port='', count=1, interval=0, timeout=120):\n+        p_str = '[' + ','.join([p.command() if not isinstance(p, str) else p for p in self.pktgen.pkts]) + ']'\n+        pkts_str = self._recompose_pkts_str(pkts_str=p_str)\n+        cmd = 'sendp(' + pkts_str + f',iface=\"{tx_port}\",count={count},inter={interval},verbose=False)'\n+        if crb.name == 'tester':\n+            crb.scapy_session.send_expect(cmd, '>>> ', timeout=timeout)\n+        elif crb.name.startswith(\"tester_scapy\"):\n+            crb.send_expect(cmd, '>>> ', timeout=timeout)\n         else:\n-            crb.send_expect('python3 %s' % crb.tmp_file + scapy_cmd, '# ', timeout=timeout)\n-        return crb.tmp_file + scapy_cmd\n-\n-    def send_pkt(self, crb, tx_port='', count=1, interval=0, timeout=15):\n-        self._send_pkt(crb, tx_port, count, inter=interval, timeout=timeout)\n-\n-    def send_pkt_bg(self, crb, tx_port='', count=-1, loop=1, interval=0, timeout=3):\n-        return self._send_pkt(crb, tx_port=tx_port, count=count, send_bg=True, loop=loop, inter=interval,\n-                              timeout=timeout)\n-\n-    def stop_send_pkt_bg(self, crb, filenames):\n+            raise Exception(\"crb should be tester\\'s session and initialized\")\n+\n+    def send_pkt_bg(self, crb, tx_port='', count=-1, interval=0, loop=1):\n+        if crb.name != 'tester':\n+            raise Exception('crb should be tester')\n+        scapy_session_bg = crb.prepare_scapy_env()\n+        p_str = '[' + ','.join([p.command() if not isinstance(p, str) else p for p in self.pktgen.pkts]) + ']'\n+        pkts_str = self._recompose_pkts_str(pkts_str=p_str)\n+        cmd = 'sendp(' + pkts_str + f',iface=\"{tx_port}\",count={count},inter={interval},loop={loop},verbose=False)'\n+        scapy_session_bg.send_command(cmd)\n+        return scapy_session_bg\n+\n+    @staticmethod\n+    def stop_send_pkt_bg(session):\n         # stop sending action\n-        pids = []\n-        if isinstance(filenames, list):\n-            for file in filenames:\n-                out = crb.send_expect('ps -ef |grep %s|grep -v grep' % file, expected='# ')\n-                try:\n-                    pids.append(re.search('\\d+', out).group())\n-                except AttributeError as e:\n-                    print((e, ' :%s not killed' % file))\n-        else:\n-            out = crb.send_expect('ps -ef |grep %s|grep -v grep' % filenames, expected='# ')\n-            try:\n-                pids.append(re.search('\\d+', out).group())\n-            except AttributeError as e:\n-                print((e, ' :%s not killed' % filenames))\n-        pid = ' '.join(pids)\n-        if pid:\n-            crb.send_expect('kill -9 %s' % pid, expected='# ')\n-        for i in crb.sessions:\n-            if i.name.startswith('scapy_bg_session'):\n-                crb.destroy_session(i)\n+        session.send_expect('^C', '>>> ')\n \n     def check_layer_config(self):\n         \"\"\"\n@@ -835,7 +843,8 @@ class Packet(object):\n \n         if 'inner' in layer:\n             layer = layer[6:]\n-\n+        if isinstance(self.pktgen.pkt, str):\n+            raise Exception('string type packet not support config layer')\n         pkt_layer = self.pktgen.pkt.getlayer(idx)\n         layer_conf = getattr(self.pktgen, layer)\n         setattr(self, 'configured_layer_%s' % layer, True)\n@@ -1081,10 +1090,8 @@ def strip_pktload(pkt=None, layer=\"L2\", p_index=0):\n ###############################################################################\n ###############################################################################\n if __name__ == \"__main__\":\n-\n     pkt = Packet('Ether(type=0x894f)/NSH(Len=0x6,NextProto=0x0,NSP=0x000002,NSI=0xff)')\n     sendp(pkt, iface='lo')\n-    pkt.config_layer('ipv4', {'dst': '192.168.8.8'})\n     pkt.append_pkt(pkt_type='IPv6_TCP', pkt_len=100)\n     pkt.append_pkt(pkt_type='TCP', pkt_len=100)\n     pkt.config_layer('tcp', config={'flags': 'A'})\n",
    "prefixes": [
        "V1",
        "3/7"
    ]
}