blob: 915ba9644f1557acbe2a743497ee901ea7add562 [file] [log] [blame]
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Connection test cases
"""
import time
import sys
import logging
import unittest
import random
from oftest import config
import oftest.controller as controller
import ofp
import oftest.dataplane as dataplane
from oftest.testutils import *
@disabled
class BaseHandshake(unittest.TestCase):
"""
Base handshake case to set up controller, but do not send hello.
"""
def controllerSetup(self, host, port):
con = controller.Controller(host=host,port=port)
# clean_shutdown should be set to False to force quit app
self.clean_shutdown = True
# disable initial hello so hello is under control of test
con.initial_hello = False
con.start()
self.controllers.append(con)
def setUp(self):
logging.info("** START TEST CASE " + str(self))
self.controllers = []
self.default_timeout = test_param_get('default_timeout',
default=2)
def tearDown(self):
logging.info("** END TEST CASE " + str(self))
for con in self.controllers:
con.shutdown()
if self.clean_shutdown:
con.join()
def runTest(self):
# do nothing in the base case
pass
def assertTrue(self, cond, msg):
if not cond:
logging.error("** FAILED ASSERTION: " + msg)
unittest.TestCase.assertTrue(self, cond, msg)
class HandshakeNoHello(BaseHandshake):
"""
TCP connect to switch, but do not sent hello,
and wait for disconnect.
"""
def runTest(self):
self.controllerSetup(config["controller_host"],
config["controller_port"])
self.controllers[0].connect(self.default_timeout)
logging.info("TCP Connected " +
str(self.controllers[0].switch_addr))
logging.info("Hello not sent, waiting for timeout")
# wait for controller to die
self.assertTrue(self.controllers[0].wait_disconnected(timeout=10),
"Not notified of controller disconnect")
class HandshakeNoFeaturesRequest(BaseHandshake):
"""
TCP connect to switch, send hello, but do not send features request,
and wait for disconnect.
"""
def runTest(self):
self.controllerSetup(config["controller_host"],
config["controller_port"])
self.controllers[0].connect(self.default_timeout)
logging.info("TCP Connected " +
str(self.controllers[0].switch_addr))
logging.info("Sending hello")
self.controllers[0].message_send(ofp.message.hello())
logging.info("Features request not sent, waiting for timeout")
# wait for controller to die
self.assertTrue(self.controllers[0].wait_disconnected(timeout=10),
"Not notified of controller disconnect")
@disabled
class CompleteHandshake(BaseHandshake):
"""
Set up multiple controllers and complete handshake, but otherwise do nothing.
"""
def buildControllerList(self):
# controller_list is a list of IP:port tuples
con_list = test_param_get('controller_list')
if con_list is not None:
self.controller_list = []
for controller in con_list:
ip,portstr = controller.split(':')
try:
port = int(portstr)
except:
self.assertTrue(0, "failure converting port " +
portstr + " to integer")
self.controller_list.append( (ip, int(port)) )
else:
self.controller_list = [(config["controller_host"],
config["controller_port"])]
def __init__(self, keep_alive=True, cxn_cycles=5,
controller_timeout=-1, hello_timeout=5,
features_req_timeout=5, disconnected_timeout=3,
report_pkts=False):
BaseHandshake.__init__(self)
self.buildControllerList()
self.keep_alive = keep_alive
self.cxn_cycles = test_param_get('cxn_cycles') \
or cxn_cycles
self.controller_timeout = test_param_get('controller_timeout') \
or controller_timeout
self.hello_timeout = test_param_get('hello_timeout') \
or hello_timeout
self.features_req_timeout = test_param_get('features_req_timeout') \
or features_req_timeout
self.disconnected_timeout = test_param_get('disconnected_timeout') \
or disconnected_timeout
self.report_pkts = report_pkts
# These functions provide per-tick processing
def periodic_task_init(self, tick_time):
"""
Assumes tick_time is in seconds, usually 1/10 of a sec
"""
if not self.report_pkts:
return
self.start_time = time.time()
self.last_report = self.start_time
self.pkt_in_count = 0 # Total packet in count
self.periodic_pkt_in_count = 0 # Packet-ins this cycle
def periodic_task_tick(self, con):
"""
Process one tick. Currently this just counts pkt-in msgs
"""
if not self.report_pkts:
return
if con.cstate != 4:
return
# Gather packets from control cxn
current_time = time.time()
new_pkts = con.packet_in_count - self.pkt_in_count
self.pkt_in_count = con.packet_in_count
self.periodic_pkt_in_count += new_pkts
con.clear_queue()
# Report every second or so
if (current_time - self.last_report >= 1):
if self.periodic_pkt_in_count:
print "%7.2f: pkt/sec last period: %6d. Total %10d." % (
current_time - self.start_time,
self.periodic_pkt_in_count/(current_time - self.last_report),
self.pkt_in_count)
self.last_report = current_time
self.periodic_pkt_in_count = 0
def periodic_task_done(self):
if not self.report_pkts:
return
print "Received %d pkt-ins over %d seconds" % (
self.pkt_in_count, time.time() - self.start_time)
def runTest(self):
for conspec in self.controller_list:
self.controllerSetup(conspec[0], conspec[1])
for i in range(len(self.controller_list)):
self.controllers[i].cstate = 0
self.controllers[i].keep_alive = self.keep_alive
self.controllers[i].saved_switch_addr = None
tick = 0.1 # time period in seconds at which controllers are handled
self.periodic_task_init(tick)
disconnected_count = 0
cycle = 0
while True:
states = []
for con in self.controllers:
condesc = con.host + ":" + str(con.port) + ": "
logging.debug("Checking " + condesc)
if con.switch_socket:
if con.switch_addr != con.saved_switch_addr:
con.saved_switch_addr = con.switch_addr
con.cstate = 0
if con.cstate == 0:
logging.info(condesc + "Sending hello to " +
str(con.switch_addr))
con.message_send(ofp.message.hello())
con.cstate = 1
con.count = 0
elif con.cstate == 1:
reply, pkt = con.poll(exp_msg=ofp.OFPT_HELLO,
timeout=0)
if reply is not None:
logging.info(condesc +
"Hello received from " +
str(con.switch_addr))
con.cstate = 2
else:
con.count = con.count + 1
# fall back to previous state on timeout
if con.count >= self.hello_timeout/tick:
logging.info(condesc +
"Timeout hello from " +
str(con.switch_addr))
con.cstate = 0
elif con.cstate == 2:
logging.info(condesc + "Sending features request to " +
str(con.switch_addr))
con.message_send(ofp.message.features_request())
con.cstate = 3
con.count = 0
elif con.cstate == 3:
reply, pkt = con.poll(exp_msg=ofp.OFPT_FEATURES_REPLY,
timeout=0)
if reply is not None:
logging.info(condesc +
"Features reply received from " +
str(con.switch_addr))
con.cstate = 4
con.count = 0
cycle = cycle + 1
logging.info("Cycle " + str(cycle))
else:
con.count = con.count + 1
# fall back to previous state on timeout
if con.count >= self.features_req_timeout/tick:
logging.info(condesc +
"Timeout features request from " +
str(con.switch_addr))
con.cstate = 2
elif con.cstate == 4:
if (self.controller_timeout < 0 or
con.count < self.controller_timeout/tick):
logging.debug(condesc +
"Maintaining connection to " +
str(con.switch_addr))
con.count = con.count + 1
else:
logging.info(condesc +
"Disconnecting from " +
str(con.switch_addr))
con.disconnect()
con.cstate = 0
else:
con.cstate = 0
states.append(con.cstate)
self.periodic_task_tick(con)
logging.debug("Cycle " + str(cycle) +
", states " + str(states) +
", disconnected_count " + str(disconnected_count))
if 4 in states:
disconnected_count = 0
else:
disconnected_count = disconnected_count + 1
if cycle != 0:
self.assertTrue(disconnected_count < self.disconnected_timeout/tick,
"Timeout expired connecting to controller")
else:
# on first cycle, allow more time for initial connect
self.assertTrue(disconnected_count < 2*self.disconnected_timeout/tick,
"Timeout expired connecting to controller on init")
if cycle > self.cxn_cycles:
break
time.sleep(tick)
self.periodic_task_done()
@disabled
class HandshakeAndKeepalive(CompleteHandshake):
"""
Complete handshake and respond to echo request, but otherwise do nothing.
Good for manual testing.
"""
def __init__(self):
CompleteHandshake.__init__(self, keep_alive=True)
@disabled
class MonitorPacketIn(CompleteHandshake):
"""
Complete handshake and respond to echo request. As packet-in messages
arrive, report the count and pkts/second
"""
def __init__(self):
CompleteHandshake.__init__(self, keep_alive=True, report_pkts=True)
@disabled
class HandshakeNoEcho(CompleteHandshake):
"""
Complete handshake, but otherwise do nothing, and do not respond to echo.
"""
def __init__(self):
CompleteHandshake.__init__(self, keep_alive=False)
@disabled
class HandshakeAndDrop(CompleteHandshake):
"""
Complete handshake, but otherwise do nothing, and drop connection after a while.
"""
def __init__(self):
CompleteHandshake.__init__(self, keep_alive=True, controller_timeout=10)