Switch Abstraction POC. 18/12118/14
authorJamo Luhrsen <james.luhrsen@hp.com>
Tue, 7 Oct 2014 20:33:14 +0000 (13:33 -0700)
committerLuis Gomez <ecelgp@gmail.com>
Mon, 27 Oct 2014 01:51:09 +0000 (01:51 +0000)
- switch user made dynamic and defaults to ${MININET_USER}
- added ssh_key functionality for switches that use ssh for mgmt_protocol
- moved switch classes to a subdir in libraries for tidiness
- made Ovs default and SWITCH_IP == ${MININET} so that no changes
  need to be made to jenkins job

Change-Id: Ie563250ffd0b52c7c038f22fefde49f09ace209b
Signed-off-by: Jamo Luhrsen <james.luhrsen@hp.com>
using @property for dynamic switch class attribute strings

moved some attributes to properties so that the values of the
properties could change dynamically as other class attributes
are changed (e.g. when you get a flow object you need to update
the controller_ip, and because of that a lot of the strings
(for configs, etc) need to be updated in real time)

Change-Id: I82cbafda72927a56b73d2196d9694d426e855b8c
Signed-off-by: Jamo Luhrsen <james.luhrsen@hp.com>
moved generic keywords to new SwitchUtils library

also:
-  ${SWITCH} and ${CONTROLLER_IP} need to be passed as cmdLine
   args regexp will now match verifications, so long strings
   can be condensed in object definition (if desired)

Change-Id: I87c7357c16cf971eb7c4ece0014dbb6228b309a6
Signed-off-by: Jamo Luhrsen <james.luhrsen@hp.com>
renamed file

Change-Id: Ibd114274a1c402cb40b9c3d0ce7cf4530f9b6aa2
Signed-off-by: Jamo Luhrsen <james.luhrsen@hp.com>
OpenFlow validation enhancements

Actions Suite ported to use new switch abstraction.

Change-Id: I2f1f735c73bdfd7a1b3e58a6a8647f8bfeabf586
Signed-off-by: Jamo Luhrsen <james.luhrsen@hp.com>
${switch} object needed for read, write, execute wrappers

Change-Id: I7dce87eca1c3040c65716fc5585b9a2eb724a86b
Signed-off-by: Jamo Luhrsen <james.luhrsen@hp.com>
13 files changed:
test/csit/libraries/Switch.py [new file with mode: 0644]
test/csit/libraries/SwitchClasses/BaseSwitch.py [new file with mode: 0644]
test/csit/libraries/SwitchClasses/H3C.py [new file with mode: 0644]
test/csit/libraries/SwitchClasses/H3C.pyc [new file with mode: 0644]
test/csit/libraries/SwitchClasses/H3C_5920.py [new file with mode: 0644]
test/csit/libraries/SwitchClasses/H3C_5920.pyc [new file with mode: 0644]
test/csit/libraries/SwitchClasses/Ovs.py [new file with mode: 0644]
test/csit/libraries/SwitchClasses/ProVision.py [new file with mode: 0644]
test/csit/libraries/SwitchClasses/ProVision_3800.py [new file with mode: 0644]
test/csit/libraries/SwitchUtils.txt [new file with mode: 0644]
test/csit/suites/karaf-compatible/300_Switch_Qualification/010_OpenFlow_Connection.txt [new file with mode: 0644]
test/csit/suites/karaf-compatible/300_Switch_Qualification/020_OpenFlow_Actions.txt [moved from test/csit/suites/karaf-compatible/070__Flows_OF13/400__actions.txt with 61% similarity]
test/csit/variables/Variables.py

diff --git a/test/csit/libraries/Switch.py b/test/csit/libraries/Switch.py
new file mode 100644 (file)
index 0000000..e40d511
--- /dev/null
@@ -0,0 +1,8 @@
+import string
+import robot
+
+class Switch:
+    dude = ''
+
+def jamo_was_here():
+    print 'yep yep, yo!'
diff --git a/test/csit/libraries/SwitchClasses/BaseSwitch.py b/test/csit/libraries/SwitchClasses/BaseSwitch.py
new file mode 100644 (file)
index 0000000..2c66c87
--- /dev/null
@@ -0,0 +1,119 @@
+"""
+Base Switch Object Definition
+Authors: james.luhrsen@hp.com
+Created: 2014-09-20
+"""
+import string
+import robot
+import importlib
+from xml.etree.ElementTree import *
+
+class BaseSwitch(object):
+    '''
+    Switch Base Class
+    '''
+
+    make = ''
+    model = ''
+
+    mgmt_protocol = ''
+    ssh_key = ''
+    mgmt_ip = ''
+    mgmt_port = ''
+    mgmt_user = ''
+    mgmt_password = ''
+    mgmt_prompt = '' 
+
+    connection_index = ''
+
+    initialization_type = ''
+
+    of_controller_ip = ''
+
+    connection_configs = []
+
+    initialization_cmds = []
+
+    base_openflow_config = []
+
+    openflow_enable_config = []
+
+    openflow_enable_validations = []
+
+    openflow_disable_config = []
+    openflow_disable_validations = []
+
+    dump_all_flows = []
+
+    src_mac = ''
+    dst_mac = ''
+    ip_src = ''
+    ip_dst = ''
+    table_id = ''
+    action = ''
+
+    datapath_id_output_string = ''
+    datapath_id_output_command = ''
+    datapath_id = ''
+
+    def set_connection_index(self, idx):
+        self.connection_index = idx
+
+    def set_controller_ip(self, ip):
+        self.of_controller_ip = ip
+
+    def set_mgmt_ip(self, ip):
+        self.mgmt_ip = ip
+
+    def set_mgmt_user(self, user):
+        self.mgmt_user = user
+
+    def set_ssh_key(self, key):
+        self.ssh_key = key
+
+    def update_datapath_id(self):
+        raise NotImplementedError("Please implement this method")
+
+    def create_flow_match_elements(self, flow_xml):
+        flow_tree = fromstring(flow_xml)
+
+        self.table_id = flow_tree.find('{urn:opendaylight:flow:inventory}table_id').text
+
+        instructions_element = flow_tree.find('{urn:opendaylight:flow:inventory}instructions')
+        instruction_element = instructions_element.find('{urn:opendaylight:flow:inventory}instruction')
+        apply_actions = instruction_element.find('{urn:opendaylight:flow:inventory}apply-actions')
+        action = apply_actions.find('{urn:opendaylight:flow:inventory}action')
+        output_action = action.find('{urn:opendaylight:flow:inventory}output-action')
+        output_node_connector = output_action.find('{urn:opendaylight:flow:inventory}output-node-connector')
+        self.action = output_node_connector.text
+
+        match_element = flow_tree.find('{urn:opendaylight:flow:inventory}match')
+        ethernet_match_element = match_element.find('{urn:opendaylight:flow:inventory}ethernet-match')
+
+        ethernet_source = ethernet_match_element.find('{urn:opendaylight:flow:inventory}ethernet-source')
+        ethernet_source_address = ethernet_source.find('{urn:opendaylight:flow:inventory}address')
+        self.src_mac = ethernet_source_address.text
+
+        ethernet_destination = ethernet_match_element.find('{urn:opendaylight:flow:inventory}ethernet-destination')
+        ethernet_destination_address = ethernet_destination.find('{urn:opendaylight:flow:inventory}address')
+        self.dst_mac = ethernet_destination_address.text
+
+        self.ip_src = match_element.find('{urn:opendaylight:flow:inventory}ipv4-source').text
+        self.ip_dst = match_element.find('{urn:opendaylight:flow:inventory}ipv4-destination').text
+
+    def convert_hex_to_decimal_as_string(self, hex_string):
+        ##TODO: need to add error checking in case the hex_string is
+        ##not fully hex
+        return str(int(hex_string, 16))
+
+    def get_switch(self, switch_type):
+        '''
+        Generic method that will allow Robot Code to pass a string
+        to this "keyword - Get Switch" and create an object of that
+        type.  (EX: Get Switch  OVS)
+        '''
+
+        ##TODO:  what if the module "switch_type" does not exist.  Need some
+        ##error checking for that.
+        module = importlib.import_module(switch_type)
+        return getattr(module, switch_type)()
diff --git a/test/csit/libraries/SwitchClasses/H3C.py b/test/csit/libraries/SwitchClasses/H3C.py
new file mode 100644 (file)
index 0000000..4dc3421
--- /dev/null
@@ -0,0 +1,119 @@
+"""
+Provision 3800 Object Definition
+Authors: james.luhrsen@hp.com
+Created: 2014-10-02
+"""
+import string
+import robot
+import re
+from robot.libraries.BuiltIn import BuiltIn
+from BaseSwitch import *
+
+class H3C(BaseSwitch):
+    '''
+    H3C Super Class
+    '''
+
+    make = 'h3c'
+    model = ''
+
+    mgmt_protocol = 'telnet'
+    mgmt_ip = ''
+    mgmt_port = ''
+    mgmt_prompt = '(' + model + '.*>|' + model + '.*])'
+
+
+    initialization_type = 'reboot'
+
+    of_controller_ip = ''
+    of_instance_id = '21'
+
+    @property
+    def connection_configs(self):
+        return \
+            ['\r\r\r']
+
+    @property
+    def initialization_cmds(self):
+        return \
+            ['\rstartup saved-configuration odl_test_startup_config.cfg main\r', \
+             'reboot\r', \
+             'Y\r', \
+             '\r', \
+             'N\r', \
+             'Y\r']
+
+    @property
+    def cleanup_cmds(self):
+        return \
+            ['system-view', \
+             'undo openflow instance ' + self.of_instance_id, \
+             'return']
+
+    @property
+    def base_openflow_config(self):
+        return \
+            ['system-view', \
+             'openflow instance ' + self.of_instance_id, \
+             'classification vlan 1', \
+             'controller ' + self.of_instance_id + ' address ip ' + self.of_controller_ip, \
+             'active instance', \
+             'return']
+
+    @property
+    def openflow_enable_config(self):
+        return \
+            ['system-view', \
+             'openflow instance ' + self.of_instance_id, \
+             'classification vlan 1', \
+             'active instance', \
+             'return']
+
+    @property
+    def openflow_validation_cmd(self):
+        return \
+            'display openflow summary'
+
+    @property
+    def openflow_enable_validations(self):
+        return \
+            [self.of_instance_id + ' +Active', \
+             'Connected   1          24        N']
+
+    @property
+    def openflow_disable_config(self):
+        return \
+            ['system-view', \
+             'openflow instance ' + self.of_instance_id, \
+             'undo classification', \
+             'active instance', \
+             'return']
+
+    @property
+    def openflow_disable_validations(self):
+        return \
+            [self.of_instance_id + ' +Inactive  - +- +- +- +-']
+
+    @property
+    def dump_all_flows(self):
+        return \
+            ['']
+
+    @property
+    def datapath_id_output_command(self):
+        return \
+            'display openflow summary | include 0x'
+
+    datapath_id_output_string = ''
+    datapath_id = ''
+
+    def update_datapath_id(self):
+        if not self.datapath_id_output_string:
+            self.datapath_id = 'unknown'
+        else:
+         #21    Active    0x0015cc3e5f42ad23  Connected   1          24        N
+         #|---------------------------------(0)---------------------------------|
+         #|------(1)-------||------(2)-----|
+         matches = re.search('(.*0x)(\w+) +Connected', self.datapath_id_output_string)
+         datapath_id_hex = matches.group(2)
+         self.datapath_id = self.convert_hex_to_decimal_as_string(datapath_id_hex)
diff --git a/test/csit/libraries/SwitchClasses/H3C.pyc b/test/csit/libraries/SwitchClasses/H3C.pyc
new file mode 100644 (file)
index 0000000..700834c
Binary files /dev/null and b/test/csit/libraries/SwitchClasses/H3C.pyc differ
diff --git a/test/csit/libraries/SwitchClasses/H3C_5920.py b/test/csit/libraries/SwitchClasses/H3C_5920.py
new file mode 100644 (file)
index 0000000..cd14945
--- /dev/null
@@ -0,0 +1,17 @@
+"""
+Provision 3800 Object Definition
+Authors: james.luhrsen@hp.com
+Created: 2014-10-02
+"""
+import string
+import robot
+import re
+from robot.libraries.BuiltIn import BuiltIn
+from H3C import *
+
+class H3C_5920(H3C):
+    '''
+    Comware 5920
+    '''
+
+    model = '5920'
diff --git a/test/csit/libraries/SwitchClasses/H3C_5920.pyc b/test/csit/libraries/SwitchClasses/H3C_5920.pyc
new file mode 100644 (file)
index 0000000..fa00539
Binary files /dev/null and b/test/csit/libraries/SwitchClasses/H3C_5920.pyc differ
diff --git a/test/csit/libraries/SwitchClasses/Ovs.py b/test/csit/libraries/SwitchClasses/Ovs.py
new file mode 100644 (file)
index 0000000..d076d80
--- /dev/null
@@ -0,0 +1,113 @@
+"""
+Provision 3800 Object Definition
+Authors: james.luhrsen@hp.com
+Created: 2014-10-02
+"""
+import string
+import robot
+import re
+from robot.libraries.BuiltIn import BuiltIn
+from BaseSwitch import *
+
+class Ovs(BaseSwitch):
+    '''
+    OpenVswitch Class
+    '''
+
+    make = 'OpenVswitch'
+    model = 'OVS'
+
+    mgmt_protocol = 'ssh'
+    mgmt_ip = ''
+    mgmt_port = ''
+    mgmt_user = 'mininet'
+    mgmt_password = 'mininet'
+
+    mgmt_prompt = '>'
+
+
+    initialization_type = 'cleanup'
+
+    @property
+    def connection_configs(self):
+        return \
+            ['pwd']
+
+    @property
+    def cleanup_cmds(self):
+        return \
+            ['/sbin/ifconfig | egrep \'^s\' | awk \'{print \"sudo ovs-vsctl del-br\",$1}\' | sh']
+
+    @property
+    def initialization_cmds(self):
+        return \
+            [self.cleanup_cmds]
+
+    @property
+    def base_openflow_config(self):
+        return \
+            ['sudo ovs-vsctl add-br s1', \
+             'sudo ovs-vsctl set bridge s1 protocols=OpenFlow13', \
+             'sudo ovs-vsctl set-controller s1 tcp:' + self.of_controller_ip]
+
+    @property
+    def openflow_validation_cmd(self):
+        return \
+            'sudo ovs-vsctl show'
+
+    @property
+    def openflow_enable_config(self):
+        return \
+            ['sudo ovs-vsctl set-controller s1 tcp:' + self.of_controller_ip]
+
+    @property
+    def openflow_enable_validations(self):
+        return \
+            ['is_connected: true']
+
+    invalid_of_controller_ip = '1.1.1.1'
+    @property
+    def openflow_disable_config(self):
+        return \
+            ['sudo ovs-vsctl set-controller s1 tcp:' + self.invalid_of_controller_ip]
+
+    @property
+    def openflow_disable_validations(self):
+        return \
+            []
+
+    @property
+    def dump_all_flows(self):
+        return \
+            'sudo /usr/bin/ovs-ofctl dump-flows s1 -O OpenFlow13'
+
+    @property
+    def flow_validations(self):
+        return \
+            ['dl_src=' + self.src_mac + \
+             ',dl_dst=' + self.dst_mac + \
+             ',nw_src=' + self.ip_src + \
+             ',nw_dst=' + self.ip_dst + \
+             ' actions=' + self.action, \
+             'table=' + self.table_id]
+
+    def create_flow_match_elements(self, flow_xml):
+        super(Ovs, self).create_flow_match_elements(flow_xml)
+        if (self.action == 'INPORT'):
+            self.action = 'IN_PORT'
+
+    @property
+    def datapath_id_output_command(self):
+        return \
+            '/sbin/ifconfig | egrep \'^s1\' | awk \'{print $5}\''
+
+    datapath_id_output_string = ''
+    datapath_id = ''
+
+    def update_datapath_id(self):
+        if not self.datapath_id_output_string:
+            self.datapath_id = 'unknown'
+        else:
+         #32:cc:bf:34:ed:4c
+         datapath_id_hex = re.sub(':', '', self.datapath_id_output_string)
+         self.datapath_id = self.convert_hex_to_decimal_as_string(datapath_id_hex)
diff --git a/test/csit/libraries/SwitchClasses/ProVision.py b/test/csit/libraries/SwitchClasses/ProVision.py
new file mode 100644 (file)
index 0000000..e8d0d3d
--- /dev/null
@@ -0,0 +1,166 @@
+"""
+Provision 3800 Object Definition
+Authors: james.luhrsen@hp.com
+Created: 2014-10-02
+"""
+import string
+import robot
+import re
+from robot.libraries.BuiltIn import BuiltIn
+from BaseSwitch import *
+
+class ProVision(BaseSwitch):
+    '''
+    ProVision Super Class
+    '''
+
+    make = 'provision'
+    model = ''
+
+    mgmt_protocol = 'telnet'
+    mgmt_ip = ''
+    mgmt_port = ''
+    mgmt_prompt = model + '.*#'
+
+    initialization_type = 'reboot'
+
+    of_instance_id = '21'
+
+    @property
+    def connection_configs(self):
+        return \
+            ['\rend \
+             \rconfig \
+             \rconsole local-terminal none \
+             \rno page \
+             \rend\r']
+
+    @property
+    def initialization_cmds(self):
+        return \
+            ['\rend\rboot system flash primary config odl_test_startup_config\r', \
+             'y', \
+             'n']
+
+    @property
+    def cleanup_cmds(self):
+        return \
+            ['end', \
+             'config', \
+             'no openflow\r \
+             y']
+
+    @property
+    def base_openflow_config(self):
+        return \
+            'end', \
+            'config', \
+            'openflow', \
+            'controller-id ' + self.of_instance_id + ' ip ' + self.of_controller_ip + \
+                            ' controller-interface oobm', \
+            'instance ' + self.of_instance_id, \
+            'member vlan 10', \
+            'controller-id ' + self.of_instance_id + ' ', \
+            'version 1.3', \
+            'enable', \
+            'openflow enable', \
+            'end'
+
+    @property
+    def openflow_enable_config(self):
+        return \
+            ['end', \
+             'config', \
+             'openflow enable', \
+             'end']
+
+    @property
+    def openflow_validation_cmd(self):
+        return \
+            'show openflow'
+
+    @property
+    def openflow_enable_validations(self):
+        return \
+            ['OpenFlow +: Enabled', \
+             self.of_instance_id + ' +Up +2 +1 +1.3']
+
+    @property
+    def openflow_disable_config(self):
+        return \
+            ['end', \
+             'config', \
+             'openflow disable', \
+             'end']
+
+    @property
+    def openflow_disable_validations(self):
+        return \
+            ['OpenFlow +: Disabled', \
+             self.of_instance_id + ' +Down +0 +0 +1.3']
+
+    @property
+    def dump_all_flows(self):
+        return \
+            'show openflow instance ' + self.of_instance_id + ' flows'
+
+    @property
+    def flow_validations(self):
+        return \
+            ['(?ms)Flow Table ID : 0.*Flow Table ID : 100.*' + \
+             'Source Protocol Address : ' + self.ip_src + '.*' + \
+             'Target Protocol Address : ' + self.ip_dst + '.*' + \
+             'Flow Table ID : ' + self.table_id + '.*' + self.action, \
+             'Source MAC    : ' + self.src_mac, \
+             'Destination MAC  : ' + self.dst_mac]
+
+    def create_flow_match_elements(self, flow_xml):
+        super(ProVision, self).create_flow_match_elements(flow_xml)
+        self.src_mac = self.format_mac_with_no_hyphens_and_one_colon(self.src_mac)
+        self.dst_mac = self.format_mac_with_no_hyphens_and_one_colon(self.dst_mac)
+        self.action = self.convert_action_to_provision_format(self.action)
+
+    def format_mac_with_no_hyphens_and_one_colon(self, mac):
+        mac_no_colons = re.sub(':', '', mac)
+        mac_with_hyphen = mac_no_colons[:6] + '-' + mac_no_colons[6:]
+        return mac_with_hyphen
+
+    def convert_action_to_provision_format(self, action):
+        if (action == 'INPORT'):
+            return 'Ingress Port'
+        if (action == 'TABLE'):
+            return 'Table'
+        if (action == 'NORMAL'):
+            return 'Normal'
+        if (action == 'FLOOD'):
+            return 'Flood'
+        if (action == 'ALL'):
+            return 'All'
+        if (action == 'CONTROLLER'):
+            return 'Controller Port'
+        if (action == 'LOCAL'):
+            return 'Local'
+
+    @property
+    def datapath_id_output_command(self):
+        return \
+            'show openflow instance ' + self.of_instance_id + ' | include Datapath'
+
+    connection_index = ''
+
+    def set_connection_index(self, idx):
+        self.connection_index = idx
+
+    datapath_id_output_string = ''
+    datapath_id = ''
+
+    def update_datapath_id(self):
+        if not self.datapath_id_output_string:
+            self.datapath_id = 'unknown'
+        else:
+         #Datapath ID              : 000af0921c22bac0
+         #|-----------------(0)---------------------|
+         #|-----------(1)----------| |------(2)-----|
+         matches = re.search('(.*: )(\w+)', self.datapath_id_output_string)
+         datapath_id_hex = matches.group(2)
+         self.datapath_id = self.convert_hex_to_decimal_as_string(datapath_id_hex)
diff --git a/test/csit/libraries/SwitchClasses/ProVision_3800.py b/test/csit/libraries/SwitchClasses/ProVision_3800.py
new file mode 100644 (file)
index 0000000..fea6e0e
--- /dev/null
@@ -0,0 +1,17 @@
+"""
+Provision 3800 Object Definition
+Authors: james.luhrsen@hp.com
+Created: 2014-10-02
+"""
+import string
+import robot
+import re
+from robot.libraries.BuiltIn import BuiltIn
+from ProVision import *
+
+class ProVision_3800(ProVision):
+    '''
+    ProVision 3800
+    '''
+
+    model = '3800'
diff --git a/test/csit/libraries/SwitchUtils.txt b/test/csit/libraries/SwitchUtils.txt
new file mode 100644 (file)
index 0000000..c5b4030
--- /dev/null
@@ -0,0 +1,182 @@
+*** Settings ***
+Library           SSHLibrary
+Library           Telnet
+
+*** Variables ***
+
+*** Keywords ***
+Get Switch Datapath ID
+    [Arguments]    ${switch}
+    [Documentation]    Using the connection index for the given switch, will execute the command string
+    ...    "datapath_id_output_command" which will store the output in switch.datapath_id_output_string.
+    ...    The switch object method "update_datapath_id" is called which is assumed to place the ODL
+    ...    friendly (decimal) version of the datapath id in to switch.datapath_id and the value is also
+    ...    returned from this keyword.
+    Configure Connection Index And Prompt Wrapper    ${switch}
+    Read Wrapper    ${switch}
+    ${switch.datapath_id_output_string}=    Execute Command Wrapper    ${switch}    ${switch.datapath_id_output_command}
+    Log    ${switch.datapath_id_output_string}
+    Call Method    ${switch}    update_datapath_id
+    [Return]    ${switch.datapath_id}
+
+Verify Switch In Operational Data Store
+    [Arguments]    ${switch}
+    [Documentation]    Verifies the existence of the switch.datapath_id in the operational datastore.
+    ${resp}    Get    session    ${REST_CONTEXT}
+    Log    ${resp.content}
+    Should Match Regexp    ${resp.content}    openflow:${switch.datapath_id}
+
+Verify Switch Not In Operational Data Store
+    [Arguments]    ${switch}
+    [Documentation]    Verifies that the given switch.datapath_id is not in the operational datastore.
+    ${resp}    Get    session    ${REST_CONTEXT}
+    Log    ${resp.content}
+    Should Not Match Regexp    ${resp.content}    openflow:${switch.datapath_id}
+
+Iterate Switch Commands From List
+    [Arguments]    ${switch}    ${cmd_list}
+    [Documentation]    Each string in the @{cmd_list} argument is executed on the switch.connection_index.
+    Configure Connection Index And Prompt Wrapper    ${switch}
+    : FOR    ${cmd}    IN    @{cmd_list}
+    \    Log    ${cmd}
+    \    Read Wrapper    ${switch}
+    \    Execute Command Wrapper    ${switch}    ${cmd}
+
+Configure OpenFlow
+    [Arguments]    ${switch}
+    [Documentation]    The commands neccessary to configure openflow on the given switch object should exist in the switch.base_openflow_config attribute. \ Also, the commands/logic to verify that openflow is working are checked in this keyword and come
+    ...    from switch.openflow_validation_cmd output where the validation strings are
+    ...    stored in switch.openflow_enable_validations
+    Log    Applying configs to configure openflow on the given switch.
+    Configure Connection Index And Prompt Wrapper    ${switch}
+    Iterate Switch Commands From List    ${switch}    ${switch.base_openflow_config}
+    Read Wrapper    ${switch}
+    Wait Until Keyword Succeeds    10s    1s    Validate Switch Output    ${switch}    ${switch.openflow_validation_cmd}    ${switch.openflow_enable_validations}
+    Read Wrapper    ${switch}
+
+Validate Switch Output
+    [Arguments]    ${switch}    ${cmd}    ${validations}    ${should_exist}=true
+    [Documentation]    A generic keyword that will execute one command on the switch, and check for each string in the @{validations} argument. \ There is a boolean flag ${should_exist} that can be used to check that the validations are or are NOT in the output of the command executed.
+    Configure Connection Index And Prompt Wrapper    ${switch}
+    Read Wrapper    ${switch}
+    ${tmp}=    Execute Command Wrapper    ${switch}    /sbin/ifconfig
+    Log    ${tmp}
+    ${output}=    Execute Command Wrapper    ${switch}    ${cmd}
+    Log    ${output}
+    : FOR    ${str}    IN    @{validations}
+    \    Run Keyword If    "${should_exist}" == "true"    Should Match Regexp    ${output}    ${str}
+    \    Run Keyword If    "${should_exist}" == "false"    Should Not Match Regexp    ${output}    ${str}
+
+Enable OpenFlow
+    [Arguments]    ${switch}
+    [Documentation]    executes the switch.openflow_enable_config on the given switch and validates that openflow is operational with the switch.openflow_validation_command against all the strings in the switch.openflow_enable_validations list.
+    Log    Will toggle openflow to be ON
+    Iterate Switch Commands From List    ${switch}    ${switch.openflow_enable_config}
+    Read Wrapper    ${switch}
+    Wait Until Keyword Succeeds    10s    1s    Validate Switch Output    ${switch}    ${switch.openflow_validation_cmd}    ${switch.openflow_enable_validations}
+
+Disable OpenFlow
+    [Arguments]    ${switch}
+    [Documentation]    executes the switch.openflow_disable_config on the given switch and validates that openflow is NOT operational with the switch.openflow_validation_command against all the strings in the switch.openflow_disable_validations list.
+    Log    Will toggle openflow to be OFF
+    Iterate Switch Commands From List    ${switch}    ${switch.openflow_disable_config}
+    Read Wrapper    ${switch}
+    Wait Until Keyword Succeeds    10s    1s    Validate Switch Output    ${switch}    ${switch.openflow_validation_cmd}    ${switch.openflow_disable_validations}
+
+Open Connection Wrapper
+    [Arguments]    ${switch}
+    [Documentation]    Some switches require telnet access and others require ssh access. \ Based on the
+    ...    switch.mgmt_protocol, the connection open will be handled by the right robot
+    ...    library (Telnet or SSHLibrary). \ The connection_index is returned.
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    Call Method    ${switch}    set_ssh_key    ${USER_HOME}/.ssh/id_rsa
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    Call Method    ${switch}    set_mgmt_user    ${MININET_USER}
+    ${connection_index}=    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    SSHLibrary.Open Connection    ${switch.mgmt_ip}    prompt=${switch.mgmt_prompt}
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    Login With Public Key    ${switch.mgmt_user}    ${switch.ssh_key}    any
+    ${connection_index}=    Run Keyword If    "${switch.mgmt_protocol}" == "telnet"    Telnet.Open Connection    ${switch.mgmt_ip}    ELSE    Set Variable
+    ...    ${connection_index}
+    [Return]    ${connection_index}
+
+Configure Connection Index And Prompt Wrapper
+    [Arguments]    ${switch}
+    [Documentation]    when using multiple switch connections (e.g. more than one switch device) this keyword will switch the current connection index and prompt so that the following
+    ...    Read or Write actions happen on the correct device.
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    SSHLibrary.Switch Connection    ${switch.connection_index}
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    SSHLibrary.Set Client Configuration    prompt=${switch.mgmt_prompt}    timeout=5s
+    Run Keyword If    "${switch.mgmt_protocol}" == "telnet"    Telnet.Switch Connection    ${switch.connection_index}
+    Run Keyword If    "${switch.mgmt_protocol}" == "telnet"    Telnet.Set Prompt    ${switch.mgmt_prompt}    True
+
+Read Wrapper
+    [Arguments]    ${switch}
+    [Documentation]    Wraps the Read command so that depending on the switch.mgmt_protocol the right
+    ...    library (Telnet or SSHLibrary) is used.
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    SSHLibrary.Read
+    Run Keyword If    "${switch.mgmt_protocol}" == "telnet"    Telnet.Read
+
+Write Bare Wrapper
+    [Arguments]    ${switch}    ${cmd}
+    [Documentation]    Wraps the Write Bare command so that depending on the switch.mgmt_protocol the right
+    ...    library (Telnet or SSHLibrary) is used.
+    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    SSHLibrary.Write Bare    ${cmd}
+    Run Keyword If    "${switch.mgmt_protocol}" == "telnet"    Telnet.Write Bare    ${cmd}
+
+Execute Command Wrapper
+    [Arguments]    ${switch}    ${cmd}
+    [Documentation]    Wraps the Execute Command keyword so that depending on the switch.mgmt_protocol the right
+    ...    library (Telnet or SSHLibrary) is used.
+    ${output}=    Run Keyword If    "${switch.mgmt_protocol}" == "ssh"    SSHLibrary.Execute Command    ${cmd}
+    ${output}=    Run Keyword If    "${switch.mgmt_protocol}" == "telnet"    Telnet.Execute Command    ${cmd}    ELSE    Set Variable
+    ...    ${output}
+    [Return]    ${output}
+
+Connect To Switch
+    [Arguments]    ${switch}
+    [Documentation]    Will Open a connection to the switch, which will set the switch.connection_index.
+    ...    For each switch.connection_configs string, a write bare will be executed on the
+    ...    switch connection. \ The write bare is done becuase some switch consoles require
+    ...    extra input (CR/LF, etc.) that are needed. \ The connection_configs strings should
+    ...    be sufficient to put the switch console in to a usuable state so that further
+    ...    interactions with the switch can be used with the robot keyword "Execute
+    ...    Command"
+    ${connection_index}=    Open Connection Wrapper    ${switch}
+    Call Method    ${switch}    set_connection_index    ${connection_index}
+    Configure Connection Index And Prompt Wrapper    ${switch}
+    : FOR    ${cmd}    IN    @{switch.connection_configs}
+    \    Write Bare Wrapper    ${switch}    ${cmd}
+    \    Sleep    1
+    \    Read Wrapper    ${switch}
+
+Cleanup Switch
+    [Arguments]    ${switch}
+    [Documentation]    will execute and command strings stored in switch.cleanup_cmds
+    Iterate Switch Commands From List    ${switch}    ${switch.cleanup_cmds}
+
+Initialize Switch
+    [Arguments]    ${switch}
+    [Documentation]    Will connect and execute all switch.initialization_cmds on the given switch.
+    ...    In some cases, this may be a reboot. \ If so, the switch.initialization_type can
+    ...    be set to "reboot" and further logic is invoked to wait for the reboot to complete
+    ...    and a reconnect to the switch is made.
+    Connect To Switch    ${switch}
+    Configure Connection Index And Prompt Wrapper    ${switch}
+    : FOR    ${cmd}    IN    @{switch.initialization_cmds}
+    \    Write Bare Wrapper    ${switch}    ${cmd}
+    \    Sleep    1
+    \    Run Keyword And Ignore Error    Read Wrapper    ${switch}
+    Run Keyword If    "${switch.initialization_type}" == "reboot"    Wait For Switch Reboot    ${switch}
+    Run Keyword If    "${switch.initialization_type}" == "reboot"    Connect To Switch    ${switch}
+
+Wait For Switch Reboot
+    [Arguments]    ${switch}
+    [Documentation]    If a switch has been set to reboot, it may take some time. \ This keyword will first
+    ...    make sure the switch has gone down (10 pings over 10 seconds should not see
+    ...    a 100% success, although it may respond for a short time after the reload is
+    ...    issued). \ Then a poll is done with a single ping request every 5s until a success
+    ...    is found, at which point it is assumed the switch is up and ready.
+    ${output}=    Run    ping ${switch.mgmt_ip} -c 10 -W 1
+    Should Not Contain    ${output}    10 packets transmitted, 10 received, 0% packet loss    Does not appear that switch has rebooted
+    Wait Until Keyword Succeeds    240s    5s    Ping    ${switch.mgmt_ip}
+
+Ping
+    [Arguments]    ${ip}
+    ${output}=    Run    ping ${ip} -c 1 -W 1
+    Should Contain    ${output}    1 packets transmitted, 1 received
diff --git a/test/csit/suites/karaf-compatible/300_Switch_Qualification/010_OpenFlow_Connection.txt b/test/csit/suites/karaf-compatible/300_Switch_Qualification/010_OpenFlow_Connection.txt
new file mode 100644 (file)
index 0000000..fa03952
--- /dev/null
@@ -0,0 +1,49 @@
+*** Settings ***
+Documentation     TODO
+Suite Setup       Switch Qualification Suite Setup
+Suite Teardown    Switch Qualification Suite Teardown
+Test Timeout      5m
+Library           Collections
+Library           OperatingSystem
+Resource          ../../../libraries/SwitchUtils.txt
+Resource          ../../../libraries/Utils.txt
+Library           ../../../libraries/RequestsLibrary.py
+Library           ../../../libraries/Common.py
+Library           ../../../libraries/SwitchClasses/${SWITCH_CLASS}.py
+Variables         ../../../variables/Variables.py
+
+*** Variables ***
+${SWITCH_CLASS}    Ovs
+${SWITCH_IP}      ${MININET}
+${CONTROLLER}     null
+${REST_CONTEXT}    /restconf/operational/opendaylight-inventory:nodes
+
+*** Test Cases ***
+OF1.3 Connection Between Switch and Controller
+    [Tags]    switch_qualification
+    Configure OpenFlow    ${test_switch}
+    Enable OpenFlow    ${test_switch}
+    ${datapath_id_from_switch}=    Get Switch Datapath ID    ${test_switch}
+    Verify Switch In Operational Data Store    ${test_switch}
+    Disable OpenFlow    ${test_switch}
+    Verify Switch Not In Operational Data Store    ${test_switch}
+    ##MORE CHECKS TO ADD ON SWITCH AND OPERATIONAL DATA STORE
+    ##- proper OF version
+    ##- proper default flow rules
+    ##- ???
+
+*** Keywords ***
+Switch Qualification Suite Setup
+    ${test_switch}=    Get Switch    ${SWITCH_CLASS}
+    Set Suite Variable    ${test_switch}
+    Call Method    ${test_switch}    set_mgmt_ip    ${SWITCH_IP}
+    Call Method    ${test_switch}    set_controller_ip    ${CONTROLLER}
+    Log    MAKE: ${test_switch.make}\n MODEL: ${test_switch.model}\n IP: ${test_switch.mgmt_ip}\n PROMPT: ${test_switch.mgmt_prompt}\n CONTROLLER_IP: ${test_switch.of_controller_ip}\n MGMT_PROTOCOL: ${test_switch.mgmt_protocol}
+    Ping    ${test_switch.mgmt_ip}
+    Initialize Switch    ${test_switch}
+    Create Session    session    http://${CONTROLLER}:${RESTCONFPORT}    auth=${AUTH}    headers=${HEADERS_XML}
+
+Switch Qualification Suite Teardown
+    Cleanup Switch    ${test_switch}
+    SSHLibrary.Close All Connections
+    Telnet.Close All Connections
similarity index 61%
rename from test/csit/suites/karaf-compatible/070__Flows_OF13/400__actions.txt
rename to test/csit/suites/karaf-compatible/300_Switch_Qualification/020_OpenFlow_Actions.txt
index 124bcecc32228636eb0bd37f74c032ed34679697..f8396b7a478b38d11377897dc310b32ae7412926 100644 (file)
@@ -10,20 +10,24 @@ Documentation     OF1.3 Suite for flow actions
 ...               - output ANY
 ...
 ...               NOTE: for OVS, INPORT does not appear to be supported
-Suite Setup       Create Session    session    http://${CONTROLLER}:${RESTCONFPORT}    auth=${AUTH}    headers=${HEADERS_XML}
-Suite Teardown    Delete All Sessions
+Suite Setup       OpenFlow Actions Suite Setup
+Suite Teardown    OpenFlow Actions Suite Teardown
 Test Template     Create And Remove Flow
-Library           SSHLibrary
 Library           Collections
 Library           OperatingSystem
 Library           String
 Library           XML
 Resource          ../../../libraries/FlowLib.txt
+Resource          ../../../libraries/SwitchUtils.txt
 Library           ../../../libraries/RequestsLibrary.py
 Library           ../../../libraries/Common.py
 Variables         ../../../variables/Variables.py
+Library           ../../../libraries/SwitchClasses/${SWITCH_CLASS}.py
 
 *** Variables ***
+${SWITCH_CLASS}    Ovs
+${SWITCH_IP}      ${MININET}
+${CONTROLLER}     null
 ${REST_CON}       /restconf/config/opendaylight-inventory:nodes
 ${REST_OPR}       /restconf/operational/opendaylight-inventory:nodes
 ${ipv4_src}       11.3.0.0/16
@@ -31,7 +35,6 @@ ${ipv4_dst}       99.0.0.0/8
 ${eth_type}       0x800
 ${eth_src}        00:ab:cd:ef:01:23
 ${eth_dst}        ff:ff:ff:ff:ff:ff
-${node_id}        openflow:1
 ##documentation strings
 ${INPORT_doc}     OF1.3: OFPP_INPORT = 0xfffffff8, /* Send the packet out the input port. This\nreserved port must be explicitly used\nin order to send back out of the input\nport. */\n
 ${TABLE_doc}      OF1.3: OFPP_TABLE = 0xfffffff9, /* Submit the packet to the first flow table NB: This destination port can only be used in packet-out messages. */
@@ -42,45 +45,42 @@ ${CONTROLLER_doc}    OF1.3 OFPP_CONTROLLER = 0xfffffffd, /* Send to controller.
 ${LOCAL_doc}      OF1.3 OFPP_LOCAL = 0xfffffffe, /* Local openflow "port". */
 ${ANY_doc}        OF1.3 OFPP_ANY = 0xffffffff /* Wildcard port used only for flow mod (delete) and flow stats requests. Selects all flows regardless of output port (including flows with no output port). */
 
-*** Test Cases ***    output port        tableID              flowID    verify OVS?    OVS specific strings
+*** Test Cases ***    output port        tableID              flowID
 INPORT                [Documentation]    ${INPORT_doc}
                       [Tags]             inport
-                      ${TEST_NAME}       22                   161       yes            actions=IN_PORT
+                      ${TEST_NAME}       200                  161
 
 TABLE                 [Documentation]    ${TABLE_doc}
                       [Tags]             table
-                      ${TEST_NAME}       29                   261       yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  261
 
 NORMAL                [Documentation]    ${NORMAL_doc}
                       [Tags]             normal
-                      ${TEST_NAME}       13                   361       yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  361
 
 FLOOD                 [Documentation]    ${FLOOD_doc}
                       [Tags]             flood
-                      ${TEST_NAME}       47                   81        yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  81
 
 ALL                   [Documentation]    ${ALL_doc}
                       [Tags]             all
-                      ${TEST_NAME}       42                   88        yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  88
 
 CONTROLLER            [Documentation]    ${CONTROLLER_doc}
                       [Tags]             controller
-                      ${TEST_NAME}       81                   21        yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  21
 
 LOCAL                 [Documentation]    ${LOCAL_doc}
                       [Tags]             local
-                      ${TEST_NAME}       122                  32        yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  32
 
 ANY                   [Documentation]    ${ANY_doc}
                       [Tags]             any
-                      ${TEST_NAME}       222                  111       yes            actions=${TEST_NAME}
+                      ${TEST_NAME}       200                  111
 
 *** Keywords ***
 Create And Remove Flow
-    [Arguments]    ${output_port}    ${table_id}    ${flow_id}    ${verify_switch_flag}    ${additional_ovs_flowelements}
-    Remove Default Flows    ${node_id}
-    @{OVS_FLOWELEMENTS}    Create List    dl_dst=${eth_dst}    table=${table_id}    dl_src=${eth_src}    nw_src=${ipv4_src}    nw_dst=${ipv4_dst}
-    ...    ${additional_ovs_flowelements}
+    [Arguments]    ${output_port}    ${table_id}    ${flow_id}
     ##The dictionaries here will be used to populate the match and action elements of the flow mod
     ${ethernet_match_dict}=    Create Dictionary    type=${eth_type}    destination=${eth_dst}    source=${eth_src}
     ${ipv4_match_dict}=    Create Dictionary    source=${ipv4_src}    destination=${ipv4_dst}
@@ -93,7 +93,26 @@ Create And Remove Flow
     Set Flow Ethernet Match    ${flow}    ${ethernet_match_dict}
     Set Flow IPv4 Match    ${flow}    ${ipv4_match_dict}
     Log    Flow XML is ${flow.xml}
-    Add Flow To Controller And Verify    ${flow.xml}    ${node_id}    ${flow.table_id}    ${flow.id}
-    Run Keyword If    "${verify_switch_flag}" == "yes"    Verify Flow On Mininet Switch    ${OVS_FLOWELEMENTS}
-    Remove Flow From Controller And Verify    ${flow.xml}    ${node_id}    ${flow.table_id}    ${flow.id}
-    Run Keyword If    "${verify_switch_flag}" == "yes"    Verify Flow Does Not Exist On Mininet Switch    ${OVS_FLOWELEMENTS}
+    Call Method    ${test_switch}    create_flow_match_elements    ${flow.xml}
+    Log    ${test_switch.flow_validations}
+    ${dpid_id}=    Get Switch Datapath ID    ${test_switch}
+    Add Flow To Controller And Verify    ${flow.xml}    openflow:${dpid_id}    ${flow.table_id}    ${flow.id}
+    Validate Switch Output    ${test_switch}    ${test_switch.dump_all_flows}    ${test_switch.flow_validations}
+    Remove Flow From Controller And Verify    ${flow.xml}    openflow:${dpid_id}    ${flow.table_id}    ${flow.id}
+    Validate Switch Output    ${test_switch}    ${test_switch.dump_all_flows}    ${test_switch.flow_validations}    false
+
+OpenFlow Actions Suite Setup
+    ${test_switch}=    Get Switch    ${SWITCH_CLASS}
+    Set Suite Variable    ${test_switch}
+    Call Method    ${test_switch}    set_mgmt_ip    ${SWITCH_IP}
+    Call Method    ${test_switch}    set_controller_ip    ${CONTROLLER}
+    Log    MAKE: ${test_switch.make}\n MODEL: ${test_switch.model}\n IP: ${test_switch.mgmt_ip}\n PROMPT: ${test_switch.mgmt_prompt}\n CONTROLLER_IP: ${test_switch.of_controller_ip}\n MGMT_PROTOCOL: ${test_switch.mgmt_protocol}
+    Ping    ${test_switch.mgmt_ip}
+    Initialize Switch    ${test_switch}
+    Configure OpenFlow    ${test_switch}
+    Create Session    session    http://${CONTROLLER}:${RESTCONFPORT}    auth=${AUTH}    headers=${HEADERS_XML}
+
+OpenFlow Actions Suite Teardown
+    Cleanup Switch    ${test_switch}
+    SSHLibrary.Close All Connections
+    Telnet.Close All Connections
index ecd8b0e0b7fda5ccde11982af880b47ce044d909..d7b9a504631e2d090164119acd8f95e1e9435d9c 100644 (file)
@@ -9,7 +9,7 @@ import collections
 CONTROLLER = '127.0.0.1'
 PORT = '8080'
 RESTPORT = '8080'
-RESTCONFPORT = '8080'
+RESTCONFPORT = '8181'
 PREFIX = 'http://' + CONTROLLER + ':' + PORT
 CONTAINER = 'default'
 USER = 'admin'