test to configure 100k flows
authorPeter Gubka <pgubka@cisco.com>
Fri, 10 Oct 2014 13:10:09 +0000 (15:10 +0200)
committerPeter Gubka <pgubka@cisco.com>
Fri, 10 Oct 2014 13:10:09 +0000 (15:10 +0200)
no switch needed to be connected
it is and example of how to to run several threads in the background
comments expected for this solution
this test will be enlarged to 1M flows if concept acceptable
for 1M odl need to be run with -Xmx8196m

Change-Id: I6c6d5e24ec2be88513b744e46a103bc65246d735
Signed-off-by: Peter Gubka <pgubka@cisco.com>
test/tools/OF_Test/robot_suites/998__Independent_OF_Tests_ovs/030_Config_100k_flows.txt [new file with mode: 0644]
test/tools/OF_Test/robot_suites/998__Independent_OF_Tests_ovs/libconfig.py [new file with mode: 0644]

diff --git a/test/tools/OF_Test/robot_suites/998__Independent_OF_Tests_ovs/030_Config_100k_flows.txt b/test/tools/OF_Test/robot_suites/998__Independent_OF_Tests_ovs/030_Config_100k_flows.txt
new file mode 100644 (file)
index 0000000..c5931e1
--- /dev/null
@@ -0,0 +1,50 @@
+*** Settings ***
+Documentation     Test suite for Stats Manager flows collection
+Library           libconfig.py
+Suite Teardown    Delete Flows
+
+
+*** Variables ***
+${expdur}=     660
+
+*** Test Cases ***
+Configure 100k Flows
+      ${task1}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   1   10000
+      ${task2}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   10001   20000
+      ${task3}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   20001   30000
+      ${task4}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   30001   40000
+      ${task5}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   40001   50000
+      ${task6}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   1   10000
+      ${task7}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   10001   20000
+      ${task8}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   20001   30000
+      ${task9}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   30001   40000
+      ${task10}=    Configure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2    40001   50000
+      ${dur}=       Wait Until   ${task1}   ${task2}   ${task3}   ${task4}   ${task5}  ${task6}   ${task7}   ${task8}   ${task9}   ${task10}    timeout=${expdur}
+      ${added1}=    Call Method    ${task1}   result
+      ${added2}=    Call Method    ${task2}   result
+      ${added3}=    Call Method    ${task3}   result
+      ${added4}=    Call Method    ${task4}   result
+      ${added5}=    Call Method    ${task5}   result
+      ${added6}=    Call Method    ${task6}   result
+      ${added7}=    Call Method    ${task7}   result
+      ${added8}=    Call Method    ${task8}   result
+      ${added9}=    Call Method    ${task9}   result
+      ${added10}=    Call Method    ${task10}   result
+      ${count}=  Evaluate  ${added1}+${added2}+${added3}+${added4}+${added5}+${added6}+${added7}+${added8}+${added9}+${added10}
+      Should Be Equal As Strings   ${count}   100000    ${count} flows added in ${dur} seconds
+
+
+*** Keywords ****
+Delete Flows
+      ${task1}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   1   10000
+      ${task2}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   10001   20000
+      ${task3}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   20001   30000
+      ${task4}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   30001   40000
+      ${task5}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   1   40001   50000
+      ${task6}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   1   10000
+      ${task7}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   10001   20000
+      ${task8}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   20001   30000
+      ${task9}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   30001   40000
+      ${task10}=    Deconfigure Flows   ${CONTROLLER}    ${RESTCONFPORT}   1   2   40001   50000
+      Wait Until   ${task1}   ${task2}   ${task3}   ${task4}   ${task5}   ${task6}   ${task7}   ${task8}   ${task9}   ${task10}   timeout=${expdur}
+
diff --git a/test/tools/OF_Test/robot_suites/998__Independent_OF_Tests_ovs/libconfig.py b/test/tools/OF_Test/robot_suites/998__Independent_OF_Tests_ovs/libconfig.py
new file mode 100644 (file)
index 0000000..8cbc75e
--- /dev/null
@@ -0,0 +1,155 @@
+import requests
+import time
+from threading import Thread
+from functools import wraps
+#from multiprocessing import Process
+
+__all__ = ['configure_flows', 'wait_until', 'deconfigure_flows']
+
+#class KeyWord(Process):
+class KeyWord(Thread):
+    def __init__(self, *args, **kwargs):
+        super(KeyWord, self).__init__(*args, **kwargs)
+        self._stop = False
+        self._kw_result = None
+
+    def stop(self):
+        self._stop = True
+
+    def result(self):
+        return self._kw_result
+
+def async_task(func):
+    """Taken from http://code.activestate.com/recipes/576684-simple-threading-decorator/
+    and modified
+    """
+    @wraps(func)
+    def async_func(*args, **kwargs):
+        func_hl = KeyWord(target = func, args = args, kwargs = kwargs)
+        func_hl._Thread__args = (func_hl,) + func_hl._Thread__args
+        #func_hl._args = (func_hl,) + func_hl._args
+        func_hl.start()
+        return func_hl
+
+    return async_func
+
+
+def wait_until(*tasks, **kwargs):
+    tstart = time.time()
+
+    timeout = 30
+    if 'timeout' in kwargs:
+        timeout = int(kwargs['timeout'])
+
+    cnt = len(tasks)
+    while time.time() < (timeout+tstart):
+        tfinished = 0
+        for t in tasks:
+            if t.is_alive() == False:
+                tfinished += 1
+                continue
+            t.join(timeout=0.2)
+        if tfinished == cnt:
+            return (time.time()-tstart)
+
+    for t in tasks:
+        if t.is_alive() == True:
+            t.stop()
+            #t.terminate()
+            t.join()
+
+    return (time.time()-tstart)
+
+@async_task
+def Example_of_robot_keyword(self, a, b, c):
+    """be carefull, when calling this kw from robot,
+    do not count on self, it is a thread object itself
+    injected by decorator. The purpose is to make
+    possibility to exit from thread on demand by 
+    wait until keywork which makes thread.stop()
+    if needed. In your fw you should use self._stop
+    variable.
+
+
+    robot sample:
+    ${thread}=  Example Of Robot Keyword   a   b   c
+    """
+    while True:
+        if self._stop == True:
+            break
+
+
+
+@async_task
+def configure_flows(self, host, port, switchid, tableid, minid, maxid):
+    flow_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<flow xmlns="urn:opendaylight:flow:inventory">
+    <strict>false</strict>
+    <instructions>
+        <instruction>
+            <order>0</order>
+            <apply-actions>
+                <action>
+                    <order>0</order>
+                    <dec-nw-ttl/>
+                </action>
+            </apply-actions>
+        </instruction>
+    </instructions>
+    <table_id>{}</table_id>
+    <id>{}</id>
+    <cookie_mask>255</cookie_mask>
+    <installHw>false</installHw>
+    <match>
+        <ethernet-match>
+            <ethernet-type>
+                <type>2048</type>
+            </ethernet-type>
+        </ethernet-match>
+        <ipv4-destination>10.0.1.0/24</ipv4-destination>
+    </match>
+    <cookie>1</cookie>
+    <flow-name>FooXf{}</flow-name>
+    <priority>{}</priority>
+    <barrier>false</barrier>
+</flow>'''
+
+    self._kw_result = 0
+
+    ses = requests.Session()
+
+    for i in range(int(minid),int(maxid)+1):
+        if self._stop == True:
+            break
+        fid = str(i)
+        flow = flow_template.format(tableid,fid,fid,fid)
+        url = 'http://{}:{}/restconf/config/opendaylight-inventory:nodes/node/openflow:{}/table/{}/flow/{}'.format(host,
+            port, switchid, tableid, fid)
+
+        try:
+            rsp = ses.put(url, headers={'Content-Type':'application/xml'}, data=flow, timeout=3)
+            if rsp.status_code == 200:
+                self._kw_result += 1
+
+        except Exception as e:
+            pass
+
+
+@async_task
+def deconfigure_flows(self, host, port, switchid, tableid, minid, maxid):
+    """Result will be the number of status code 200 returned"""
+    self._kw_result = 0
+    ses = requests.Session()
+
+    for fid in range(int(minid),int(maxid)):
+        if self._stop == True:
+            break;
+        url = 'http://{}:{}/restconf/config/opendaylight-inventory:nodes/node/openflow:{}/table/{}/flow/{}'.format(host,
+            port, switchid, tableid, fid)
+
+        try:
+            rsp = ses.delete(url, headers={'Content-Type':'application/xml'}, timeout=3)
+            if rsp.status_code == 200:
+                self._kw_result += 1
+        except Exception as e:
+            pass