From 08788216c8171783811e19b49a065fe4a59ef797 Mon Sep 17 00:00:00 2001 From: vagrant Date: Thu, 11 Jun 2015 07:30:27 +0000 Subject: [PATCH 01/43] execute and dump output + first test case --- rftest/tst/RFTest.py | 26 ++++++++++++++++++++ rftest/tst/execanddump.py | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 rftest/tst/RFTest.py create mode 100644 rftest/tst/execanddump.py diff --git a/rftest/tst/RFTest.py b/rftest/tst/RFTest.py new file mode 100644 index 00000000..c862a6c1 --- /dev/null +++ b/rftest/tst/RFTest.py @@ -0,0 +1,26 @@ +import sys +import logging +import time +import argparse +import unittest +import os + +from execanddump import DumpToFile + +class RFTest(unittest.TestCase): + + """ + defnition to check status of openVswitch process. + :test_OVS_Status: calls the function execute and passes + 1. command + 2. path + 3. log file name + 4. log file type + """ + def test_OVS_Status(self): + ins = DumpToFile() + ins.execute(['ps', 'aux', '|', 'grep', 'ovs'], os.getcwd() , "ovsStatus","txt") + ins.execute(['ps', 'aux'], os.getcwd() , "allStatus", "txt") + + +# def execute(self,command,filepath,filename): diff --git a/rftest/tst/execanddump.py b/rftest/tst/execanddump.py new file mode 100644 index 00000000..979979fb --- /dev/null +++ b/rftest/tst/execanddump.py @@ -0,0 +1,52 @@ +import subprocess +import os +import json +from subprocess import call + +class DumpToFile(object): + + def __init__(self): + self.tangerine = "And now a thousand years between" + + """ + Command to execute the os commands required. Runs the commands in background and + capture the output. + command : Command to run. + filepath : location to store output file. should not be appended by "/" at the end. + filename : name of the output file to store. + filetype : to store output in the file format specified. + + """ + def execute(self,command,filepath,filename,filetype): + popen = subprocess.Popen(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + lines_iterator = iter(popen.stdout.readline, b"") + #lines_iterator_error = iter(popen.stderr.readline, c"") + + if filetype == "json": + dumpfile = filepath+"/" + filename + ".output" + ".json" + try: + os.remove(dumpfile) + except OSError: + pass + with open(dumpfile, "a") as jsonfile: + for line in lines_iterator: + json.dump(line,jsonfile) + elif filetype == "txt": + dumpfile = filepath+"/" + filename + ".output" + ".txt" + try: + os.remove(dumpfile) + except OSError: + pass + with open(dumpfile, "a") as txtfile: + for line in lines_iterator: + txtfile.write(line) + + + # dumpfile1 = filepath+"/" + filename + ".error" + # try: + # os.remove(dumpfile1) + # except OSError: + # pass + # with open(dumpfile1, "a") as jsonfile: + # for line in lines_iterator_error: + # json.dump(line,jsonfile) From 22d6b6806f6bc2b8d57f6588e3fee6436bf3ae03 Mon Sep 17 00:00:00 2001 From: vagrant Date: Mon, 22 Jun 2015 12:51:28 +0000 Subject: [PATCH 02/43] included tox, pytest frameworks. Classes with logging argparse needs to be updated. --- rftest/runtests.py | 3114 +++++++++++++++++ rftest/setup.py | 23 + rftest/test-requirements.txt | 5 + rftest/{tst/RFTest.py => tests/test_RF.py} | 11 +- rftest/{tst/execanddump.py => tests/utils.py} | 6 +- rftest/tox.ini | 11 + 6 files changed, 3161 insertions(+), 9 deletions(-) create mode 100644 rftest/runtests.py create mode 100644 rftest/setup.py create mode 100644 rftest/test-requirements.txt rename rftest/{tst/RFTest.py => tests/test_RF.py} (69%) rename rftest/{tst/execanddump.py => tests/utils.py} (90%) create mode 100644 rftest/tox.ini diff --git a/rftest/runtests.py b/rftest/runtests.py new file mode 100644 index 00000000..b5a4c18c --- /dev/null +++ b/rftest/runtests.py @@ -0,0 +1,3114 @@ +#! /usr/bin/env python + +# Hi There! +# You may be wondering what this giant blob of binary data here is, you might +# even be worried that we're up to something nefarious (good for you for being +# paranoid!). This is a base64 encoding of a zip file, this zip file contains +# a fully functional basic pytest script. +# +# Pytest is a thing that tests packages, pytest itself is a package that some- +# one might want to install, especially if they're looking to run tests inside +# some package they want to install. Pytest has a lot of code to collect and +# execute tests, and other such sort of "tribal knowledge" that has been en- +# coded in its code base. Because of this we basically include a basic copy +# of pytest inside this blob. We do this because it let's you as a maintainer +# or application developer who wants people who don't deal with python much to +# easily run tests without installing the complete pytest package. +# +# If you're wondering how this is created: you can create it yourself if you +# have a complete pytest installation by using this command on the command- +# line: ``py.test --genscript=runtests.py``. + +sources = """ +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 +""" + +import sys +import base64 +import zlib + +class DictImporter(object): + def __init__(self, sources): + self.sources = sources + + def find_module(self, fullname, path=None): + if fullname == "argparse" and sys.version_info >= (2,7): + # we were generated with = (3, 0): + exec("def do_exec(co, loc): exec(co, loc)\n") + import pickle + sources = sources.encode("ascii") # ensure bytes + sources = pickle.loads(zlib.decompress(base64.decodebytes(sources))) + else: + import cPickle as pickle + exec("def do_exec(co, loc): exec co in loc\n") + sources = pickle.loads(zlib.decompress(base64.decodestring(sources))) + + importer = DictImporter(sources) + sys.meta_path.insert(0, importer) + + entry = "import pytest; raise SystemExit(pytest.cmdline.main())" + do_exec(entry, locals()) # noqa diff --git a/rftest/setup.py b/rftest/setup.py new file mode 100644 index 00000000..10588527 --- /dev/null +++ b/rftest/setup.py @@ -0,0 +1,23 @@ +from distutils.core import setup, Command +# you can also import from setuptools + +class PyTest(Command): + user_options = [] + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + import subprocess + import sys + errno = subprocess.call([sys.executable, 'runtests.py']) + raise SystemExit(errno) + + +setup( + #..., + cmdclass = {'test': PyTest}, + #..., +) diff --git a/rftest/test-requirements.txt b/rftest/test-requirements.txt new file mode 100644 index 00000000..e4a0274b --- /dev/null +++ b/rftest/test-requirements.txt @@ -0,0 +1,5 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +pytest diff --git a/rftest/tst/RFTest.py b/rftest/tests/test_RF.py similarity index 69% rename from rftest/tst/RFTest.py rename to rftest/tests/test_RF.py index c862a6c1..65b5f8ae 100644 --- a/rftest/tst/RFTest.py +++ b/rftest/tests/test_RF.py @@ -2,16 +2,17 @@ import logging import time import argparse -import unittest +#import unittest import os +import pytest -from execanddump import DumpToFile +from utils import DumpToFile -class RFTest(unittest.TestCase): +class TestRF(): """ defnition to check status of openVswitch process. - :test_OVS_Status: calls the function execute and passes + :test_OVS_Status: calls the function execute and passes 1. command 2. path 3. log file name @@ -22,5 +23,3 @@ def test_OVS_Status(self): ins.execute(['ps', 'aux', '|', 'grep', 'ovs'], os.getcwd() , "ovsStatus","txt") ins.execute(['ps', 'aux'], os.getcwd() , "allStatus", "txt") - -# def execute(self,command,filepath,filename): diff --git a/rftest/tst/execanddump.py b/rftest/tests/utils.py similarity index 90% rename from rftest/tst/execanddump.py rename to rftest/tests/utils.py index 979979fb..5560e6c5 100644 --- a/rftest/tst/execanddump.py +++ b/rftest/tests/utils.py @@ -9,7 +9,7 @@ def __init__(self): self.tangerine = "And now a thousand years between" """ - Command to execute the os commands required. Runs the commands in background and + Command to execute the os commands required. Runs the commands in background and capture the output. command : Command to run. filepath : location to store output file. should not be appended by "/" at the end. @@ -23,7 +23,7 @@ def execute(self,command,filepath,filename,filetype): #lines_iterator_error = iter(popen.stderr.readline, c"") if filetype == "json": - dumpfile = filepath+"/" + filename + ".output" + ".json" + dumpfile = filepath+"/output/" + filename + ".output" + ".json" try: os.remove(dumpfile) except OSError: @@ -32,7 +32,7 @@ def execute(self,command,filepath,filename,filetype): for line in lines_iterator: json.dump(line,jsonfile) elif filetype == "txt": - dumpfile = filepath+"/" + filename + ".output" + ".txt" + dumpfile = filepath+"/output/" + filename + ".output" + ".txt" try: os.remove(dumpfile) except OSError: diff --git a/rftest/tox.ini b/rftest/tox.ini new file mode 100644 index 00000000..91b36410 --- /dev/null +++ b/rftest/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py27,py31 +[testenv] +changedir=tests +deps= -r{toxinidir}/test-requirements.txt +commands= + find . -type f -name "*.pyc" -delete + py.test \ + --basetemp={envtmpdir} +# py.test tempdir setting +# {posargs} # substitute with tox' positional arguments From 00172295780ea67841fec4d8d3f18c1f5f2c7e6d Mon Sep 17 00:00:00 2001 From: vagrant Date: Thu, 25 Jun 2015 07:23:50 +0000 Subject: [PATCH 03/43] ovs tests ready with logging and output files --- rftest/tests/test_RF.py | 141 +++++++++++++++++++++++++++++++++++----- rftest/tests/utils.py | 50 +++++++------- rftest/tox.ini | 4 ++ 3 files changed, 154 insertions(+), 41 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 65b5f8ae..c70bb60e 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -1,25 +1,132 @@ import sys -import logging import time import argparse #import unittest import os import pytest +import logging +import subprocess + +#from utils import DumpToFile + +logging.basicConfig( + filename = 'RFtest.log', + level=logging.INFO, + format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', + datefmt='%b %d %H:%M:%S' + ) + + + +class RF_Tests(object): + + def __init__(self, mongoPort, controllerOne, controllerTwo, lxc, testsToRun): + logger = logging.getLogger('RFtest') + self.logger.info("test case execution start") + self.mongoPort = mongoPort + self.controllerOne = controllerOne + self.controllerTwo = controllerTwo + self.lxc = lxc + self.testsToRun = testsToRun + + def create_parser(self): + logger.info("create Parser base function") + pass + + def run_tests(): + pass + + def verify_tests(): + pass + + +#class TestOVS(Tests_RF): +#class TestOVS(RF_Tests): +class TestOVS(): + + logger = logging.getLogger('Entering TestOVS') + + def test_netwokvsctl(self,capfd): + self.logger.info('TestOVS Network ovs-vsctl') + subprocess.call('ovs-vsctl show | grep dp0', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Network ovs-vsctl ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Network ovs-vsctl ==== \n ") + txtfile.write(err) + assert out.find("dp0") != -1 + + def test_netwokdpctl(self,capfd): + self.logger.info('TestOVS Network ovs-dpctl') + subprocess.call('ovs-dpctl show | grep dp0', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") + txtfile.write(err) + assert out.find("dp0") != -1 + + + def test_processdbserver(self,capfd): + self.logger.info('TestOVS Process ovsdb server') + subprocess.call('ps aux | grep ovs', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") + txtfile.write(err) + assert out.find("ovsdb-server") != -1 + + def test_processswitchd(self,capfd): + self.logger.info('TestOVS Process ovs-vswitchd') + subprocess.call('ps aux | grep ovs', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") + txtfile.write(err) + assert out.find("ovs-vswitchd") != -1 + + def test_specific(self,capfd): + self.logger.info('TestOVS Specific') + #pass + + def run_tests(): + self.logger.info('TestOVS testcases start execution') + #self.test_netwok() + #self.test_process() + #self.test_specific() + + def verify_tests(): + pass + + +if __name__ == '__main__': + description = 'RFTest suite, to run the tests and determine the state of system' + epilog = 'Report bugs to: https://github.com/routeflow/RouteFlow/issues' + + #config = os.path.dirname(os.path.realpath(__file__)) + "/config.csv" + #islconf = os.path.dirname(os.path.realpath(__file__)) + "/islconf.csv" + + parser = argparse.ArgumentParser(description=description, epilog=epilog) + parser.add_argument('-m', '--mongoport', default=27017, type = int, + help='port number to verify running of mongodb') + parser.add_argument('-c', '--controllerport1', default=6533, type = int, + help='rfproxy controller port1') + parser.add_argument('-cc', '--controllerport2', default=6653, type = int, + help='rfproxy controller port2') + parser.add_argument('-l', '--lxcname', default='', + help='Lxc container name, should be given to verify, default not supported') -from utils import DumpToFile - -class TestRF(): - - """ - defnition to check status of openVswitch process. - :test_OVS_Status: calls the function execute and passes - 1. command - 2. path - 3. log file name - 4. log file type - """ - def test_OVS_Status(self): - ins = DumpToFile() - ins.execute(['ps', 'aux', '|', 'grep', 'ovs'], os.getcwd() , "ovsStatus","txt") - ins.execute(['ps', 'aux'], os.getcwd() , "allStatus", "txt") + args = parser.parse_args() + tests = ['lxc','ovs'] + server = RF_Run_Tests(args.mongoport, args.controllerport1, args.controllerport2, args.lxcname,tests) diff --git a/rftest/tests/utils.py b/rftest/tests/utils.py index 5560e6c5..beda39df 100644 --- a/rftest/tests/utils.py +++ b/rftest/tests/utils.py @@ -17,34 +17,36 @@ def __init__(self): filetype : to store output in the file format specified. """ - def execute(self,command,filepath,filename,filetype): - popen = subprocess.Popen(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE) - lines_iterator = iter(popen.stdout.readline, b"") + #def execute(self,command,filepath,filename,filetype): + def execute(self,command): + subprocess.call(command, shell = True) #Popen(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + #popen = subprocess.Popen(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + #lines_iterator = iter(popen.stdout.readline, b"") #lines_iterator_error = iter(popen.stderr.readline, c"") - if filetype == "json": - dumpfile = filepath+"/output/" + filename + ".output" + ".json" - try: - os.remove(dumpfile) - except OSError: - pass - with open(dumpfile, "a") as jsonfile: - for line in lines_iterator: - json.dump(line,jsonfile) - elif filetype == "txt": - dumpfile = filepath+"/output/" + filename + ".output" + ".txt" - try: - os.remove(dumpfile) - except OSError: - pass - with open(dumpfile, "a") as txtfile: - for line in lines_iterator: - txtfile.write(line) + # if filetype == "json": + # dumpfile = filepath+"/output/" + filename + ".output" + ".json" + # try: + # os.remove(dumpfile) + # except OSError: + # pass + # with open(dumpfile, "a") as jsonfile: + # for line in lines_iterator: + # json.dump(line,jsonfile) + # elif filetype == "txt": + # dumpfile = filepath+"/output/" + filename + ".output" + ".txt" + # try: + # os.remove(dumpfile) + # except OSError: + # pass + # with open(dumpfile, "a") as txtfile: + # for line in lines_iterator: + # txtfile.write(line) - # dumpfile1 = filepath+"/" + filename + ".error" - # try: - # os.remove(dumpfile1) + ## dumpfile1 = filepath+"/" + filename + ".error" + ## try: + ## os.remove(dumpfile1) # except OSError: # pass # with open(dumpfile1, "a") as jsonfile: diff --git a/rftest/tox.ini b/rftest/tox.ini index 91b36410..f0ca1eeb 100644 --- a/rftest/tox.ini +++ b/rftest/tox.ini @@ -5,7 +5,11 @@ changedir=tests deps= -r{toxinidir}/test-requirements.txt commands= find . -type f -name "*.pyc" -delete + find . -type f -name "testout.txt" -delete + find . -type f -name "testerr.txt" -delete py.test \ --basetemp={envtmpdir} # py.test tempdir setting # {posargs} # substitute with tox' positional arguments +#find . -type f -name "testout.txt" -delete +#find . -type f -name "testerr.txt" -delete From 10cdddd25a1b836bda79dfb71105a31689c902fd Mon Sep 17 00:00:00 2001 From: vagrant Date: Thu, 25 Jun 2015 08:10:41 +0000 Subject: [PATCH 04/43] added tests for rfserver and rfproxy --- rftest/tests/test_RF.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index c70bb60e..33587c61 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -40,11 +40,9 @@ def verify_tests(): pass -#class TestOVS(Tests_RF): -#class TestOVS(RF_Tests): class TestOVS(): - logger = logging.getLogger('Entering TestOVS') + logger = logging.getLogger('TestOVS') def test_netwokvsctl(self,capfd): self.logger.info('TestOVS Network ovs-vsctl') @@ -108,6 +106,40 @@ def run_tests(): def verify_tests(): pass +class Testrfserver(): + + logger = logging.getLogger('TestRFServer') + + def test_processrfserverrunning(self,capfd): + self.logger.info('Test for process rfserver running') + subprocess.call('ps aux| grep rfserver', shell = True) + out,err = capfd.readouterr() + str2 = 'python ./rfserver/rfserver/py' + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfserver running ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfserver running ==== \n ") + txtfile.write(err) + assert out.find(str2) != -1 + +class Testrfproxy(): + + logger = logging.getLogger('TestRFProxy') + + def test_processrfproxy(self,capfd): + self.logger.info('Test for process rfproxy running') + subprocess.call('ps aux| grep rfproxy', shell = True) + out,err = capfd.readouterr() + str2 = 'ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py' + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfproxy running ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfproxy running ==== \n ") + txtfile.write(err) + assert out.find(str2) != -1 + if __name__ == '__main__': description = 'RFTest suite, to run the tests and determine the state of system' @@ -128,5 +160,5 @@ def verify_tests(): args = parser.parse_args() tests = ['lxc','ovs'] - server = RF_Run_Tests(args.mongoport, args.controllerport1, args.controllerport2, args.lxcname,tests) + server = RF_Tests(args.mongoport, args.controllerport1, args.controllerport2, args.lxcname,tests) From 4c3a1f0bc82eace8a97804e0f7ee16f19e7c4671 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Mon, 13 Jul 2015 20:18:12 +0000 Subject: [PATCH 05/43] logging and argparse --- rftest/tests/test_RF.py | 255 ++++++++++++++++++++++++++++++++++------ 1 file changed, 222 insertions(+), 33 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 33587c61..31e520c9 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -9,51 +9,224 @@ #from utils import DumpToFile -logging.basicConfig( - filename = 'RFtest.log', - level=logging.INFO, - format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', - datefmt='%b %d %H:%M:%S' - ) +#logging.basicConfig( +# filename = 'RFtest.log', +# level=logging.INFO, +# format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', +# datefmt='%b %d %H:%M:%S' +# ) + +class Tests: + CATALOGUE = { + 'ovs':'OVS', + 'containers':'Containers', + 'rfapps':'RFApps', + } + LOGLEVEL = { + 10 : 'DEBUG', + 20 : 'INFO', + 30 : 'WARNING', + 40 : 'ERROR', + 50 : 'CRITICAL', + } + + def __init__(self): + mongoport = 27017 + containers = ['rfvmA','rfvmB'] + self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} + self.testsParams = {'mongo':mongoport, 'containers':containers, 'rfapps':['rfproxy','rfserver']} + self.outputFormat = {'txt':False, 'terminal':False} + self.outputModes = {'json':False, 'raw':False} + self.use_pytest = False + + + #def setTestsToRun(self, *args, **kwargs): + def setTestsToRun(self, *args): + ''' + fill self.testsToRun with proper arguments + like lxc, ovs, rfserver, rfproxy,.... true or false + + define if tests will use pytest or not (self.use_pytest = False) + ''' + pass + + + def configureTests(self, **kwargs): + ''' + fill self.testsParams with arguments + ''' + pass + + def setTestsOutput(self, *args, **kwargs): + ''' + check if tests results must be saved + in txt file or sent to terminal + in json or raw formats + (first let`s save in raw format) + ''' + pass + + def setup(self,name,output_dir): + ''' + takes self.testsToRun, self.outputFormat, self.outputModes + and prepare environment to runtests + ex: creates loggers, open files/terminal outputs. + + for loggers: define a self.logger and instatiate it to save in file or terminal + In this case let`s not use open text files, instead let`s create a logger for each Test class + according to self.testsToRun and outputFormat. you can create a function for that too + And create a dict like CATALOGUE that will contain testsToRun:logger + this dict will be used to call RF_tests + example: https://aykutakin.wordpress.com/2013/08/06/logging-to-console-and-file-in-python/ + + for testsToRun create a function that reads self.testsToRun and do the following: + - look for test_ files in dir and subdir (ex: test_ovs) and load it importing + its class (ex from test_ovs import OVS - class name associated with ovs in CATALOGUE) + - returns a dict (ex: self.setupTests) with teststorun:classes objects + following self.testParams + this dict will be used to be used in runTests function + ''' + logger = setLoggers(name,output_dir) + + def setLoggers(name,output_dir): + ''' + :name: used to print the class name in the get logger. + :output_dir: directory to store logs + ''' + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + + #INFO handler + handler = logging.FileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true") + handler.setLevel(logging.INFO) + formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + #ERROR handler + handler = logging.FileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true") + handler.setLevel(logging.ERROR) + formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + return logger + + def runTests(self): + ''' + Two approaches here depending on self.use_pytest = True or False: + - first call pytest.main() https://pytest.org/latest/usage.html + ex: pytest.main("-qq", plugins=[MyPlugin()]) + I will explain you how to run using pytest! ASK ME + + Second run the tests by ourselves. How? + Using dict returned by setup (ex: self.setupTests) + call run_tests for each object + ''' + # call setup with the name argument. This name will be used by logger. + # The name is an itervalue from self.testsToRun dictionary. Pass each key as an argument, if the corresponding value is true. + pass +class RFUnitTests(object): -class RF_Tests(object): + def __init__(self, logger): + self.logger = logger + self.tests = {} - def __init__(self, mongoPort, controllerOne, controllerTwo, lxc, testsToRun): - logger = logging.getLogger('RFtest') - self.logger.info("test case execution start") - self.mongoPort = mongoPort - self.controllerOne = controllerOne - self.controllerTwo = controllerTwo - self.lxc = lxc - self.testsToRun = testsToRun - def create_parser(self): - logger.info("create Parser base function") + def evaluate(self, capfd): + ''' + for each process in self.tests run it using capfd + This function only execute the commands, get the output/err, + return them as {command: {'out':output,'err':err} ) + ''' pass + def verify(self, tests_out): + ''' + receives dict {command: {'out':output,'err':err} ) + run comparisons of responses (output/err) and + expected_result of TESTS using command as key + and looking for the method and expected_output + if using method expected_output == output + associate dict {command: {'assert':True, 'result':output}} + else + associate dict {command: {'assert':False, 'result':err}} + returns dict with commands:assertions,results + No need to write assertion + ''' + pass + + def analyse(self, verify_out): + ''' + receives dict of verify_processes + check assertion values and write results to logger + example: + self.logger.info("OUTPUT\n %s", out) + self.logger.info("ERROR\n %s", err) + No need to write assertion + ''' + pass + + def run_tests(): + ''' + just pass, the classes inheriting RFUnitTests will + overwrite this function + ''' pass - def verify_tests(): + +class OVS(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = { + 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, + 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, + } + + def __init__(self, logger): + super().__init__(logger) + + + def addTestsDefault(self): + self.tests = TESTS + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' pass + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + pass -class TestOVS(): + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + pass - logger = logging.getLogger('TestOVS') - def test_netwokvsctl(self,capfd): - self.logger.info('TestOVS Network ovs-vsctl') + def test_netwokvsctl(self, logger, capfd): + logger.info('TestOVS Network ovs-vsctl') subprocess.call('ovs-vsctl show | grep dp0', shell = True) out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Network ovs-vsctl ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Network ovs-vsctl ==== \n ") - txtfile.write(err) + logger.info("\n ==== TestOVS Network ovs-vsctl ==== \n ") + logger.info("OUTPUT\n %s", out) + logger.info("ERROR\n %s", err) assert out.find("dp0") != -1 def test_netwokdpctl(self,capfd): @@ -149,16 +322,32 @@ def test_processrfproxy(self,capfd): #islconf = os.path.dirname(os.path.realpath(__file__)) + "/islconf.csv" parser = argparse.ArgumentParser(description=description, epilog=epilog) + #run pytest(y/n) + parser.add_argument('-p', '--pytest', default=False, type = bool, + help='Run tests with pytest(True/False)') + #accept a list of test modules to be run. + parser.add_argument('-tc', '--testcases', choices =['ovs','rfapps','mongo','containers'], + help='Testcases to be run.choose form the list specified') + + #list of containers : either user gives his own container names(option:l) or chooses from the list(option:ln) + parser.add_argument('-l', '--lxc', nargs='*', + help='Lxc container name, should be given to verify, default not supported, zero or more container names accepted') + parser.add_argument('-ln', '--lxcnames', choices =['rfvm1','b1','b2','rfvmA','rfvmB'], + help='Lxc container name, should be given to verify, default not supported,to be choosen from the list') + + #accept the mongodb port. default 27017 parser.add_argument('-m', '--mongoport', default=27017, type = int, help='port number to verify running of mongodb') + + #accept controller ports. defaults specified. parser.add_argument('-c', '--controllerport1', default=6533, type = int, help='rfproxy controller port1') + parser.add_argument('-cc', '--controllerport2', default=6653, type = int, help='rfproxy controller port2') - parser.add_argument('-l', '--lxcname', default='', - help='Lxc container name, should be given to verify, default not supported') args = parser.parse_args() - tests = ['lxc','ovs'] - server = RF_Tests(args.mongoport, args.controllerport1, args.controllerport2, args.lxcname,tests) - + testsobj = Tests() + testsobj.setTestsToRun(args.pytest,args.testcases) + testsobj.configureTests(mongo = args.mongoport, containers = args.lxcnames) + testsobj.runTests() From 2300c2b9f9ae0bbcb77b43d8c8f6ee9e602fc8a2 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 16 Jul 2015 07:42:04 +0000 Subject: [PATCH 06/43] modularising code --- rftest/tests/test_Ovs.py | 101 ++++++++++++++++ rftest/tests/test_RF.py | 230 ++++++++++++------------------------ rftest/tests/test_mongo.py | 0 rftest/tests/test_rfapps.py | 33 ++++++ 4 files changed, 209 insertions(+), 155 deletions(-) create mode 100644 rftest/tests/test_Ovs.py create mode 100644 rftest/tests/test_mongo.py create mode 100644 rftest/tests/test_rfapps.py diff --git a/rftest/tests/test_Ovs.py b/rftest/tests/test_Ovs.py new file mode 100644 index 00000000..f14f184d --- /dev/null +++ b/rftest/tests/test_Ovs.py @@ -0,0 +1,101 @@ +from test_RF import RFUnitTests +#from test_RF import logger + +class OVS(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = { + 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, + 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, + } + + def __init__(self, logger): + super().__init__(logger) + + def addTestsDefault(self): + self.tests = TESTS + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' + pass + + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + pass + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + pass + + def test_netwokvsctl(self, logger, capfd): + logger.info('TestOVS Network ovs-vsctl') + subprocess.call('ovs-vsctl show | grep dp0', shell = True) + out,err = capfd.readouterr() + logger.info("\n ==== TestOVS Network ovs-vsctl ==== \n ") + logger.info("OUTPUT\n %s", out) + logger.info("ERROR\n %s", err) + assert out.find("dp0") != -1 + + def test_netwokdpctl(self,capfd): + self.logger.info('TestOVS Network ovs-dpctl') + subprocess.call('ovs-dpctl show | grep dp0', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") + txtfile.write(err) + assert out.find("dp0") != -1 + + + def test_processdbserver(self,capfd): + self.logger.info('TestOVS Process ovsdb server') + subprocess.call('ps aux | grep ovs', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") + txtfile.write(err) + assert out.find("ovsdb-server") != -1 + + def test_processswitchd(self,capfd): + self.logger.info('TestOVS Process ovs-vswitchd') + subprocess.call('ps aux | grep ovs', shell = True) + out,err = capfd.readouterr() + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") + txtfile.write(err) + assert out.find("ovs-vswitchd") != -1 + + def test_specific(self,capfd): + self.logger.info('TestOVS Specific') + #pass + + def run_tests(): + self.logger.info('TestOVS testcases start execution') + #self.test_netwok() + #self.test_process() + #self.test_specific() + + def verify_tests(): + pass diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 31e520c9..8cdafbed 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -6,6 +6,8 @@ import pytest import logging import subprocess +import json +import fnmatch #from utils import DumpToFile @@ -31,40 +33,67 @@ class Tests: } def __init__(self): - mongoport = 27017 - containers = ['rfvmA','rfvmB'] self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} - self.testsParams = {'mongo':mongoport, 'containers':containers, 'rfapps':['rfproxy','rfserver']} + self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} + + #This can be a single dictionary. txt terminal json and raw. self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False #def setTestsToRun(self, *args, **kwargs): - def setTestsToRun(self, *args): + def setTestsToRun(self, isPytest,**kwargs): ''' fill self.testsToRun with proper arguments like lxc, ovs, rfserver, rfproxy,.... true or false define if tests will use pytest or not (self.use_pytest = False) ''' - pass + self.use_pytest = isPytest + + #If kwargs is empty leave the testsToRun dictionary with default arguments. + if kwargs: + self.testsToRun.clear() + for key,tests in kwargs.items(): + self.testsToRun[key] = tests def configureTests(self, **kwargs): ''' fill self.testsParams with arguments ''' - pass + if kwargs: + self.testsParams.clear() + for key,param in kwargs.items(): + self.testsParams[key] = param - def setTestsOutput(self, *args, **kwargs): + #def setTestsOutput(self, *args, **kwargs): + def setTestsOutputFormat(self, **kwargs): ''' check if tests results must be saved in txt file or sent to terminal in json or raw formats (first let`s save in raw format) ''' - pass + self.outputFormat.clear() + if kwargs: + for key,formats in kwargs.items(): + self.outputFormat[key] = formats + + def setTestsOutputModes(self, **kwargs): + ''' + check if tests results must be saved + in txt file or sent to terminal + in json or raw formats + (first let`s save in raw format) + ''' + self.outputModes.clear() + if kwargs: + for key,modes in kwargs.items(): + self.outputModes[key] = modes + del self.outputModes['raw'] + self.outputModes['raw'] = True def setup(self,name,output_dir): ''' @@ -85,14 +114,11 @@ def setup(self,name,output_dir): - returns a dict (ex: self.setupTests) with teststorun:classes objects following self.testParams this dict will be used to be used in runTests function - ''' - logger = setLoggers(name,output_dir) - def setLoggers(name,output_dir): - ''' :name: used to print the class name in the get logger. :output_dir: directory to store logs ''' + logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) @@ -112,6 +138,21 @@ def setLoggers(name,output_dir): return logger + def findTests(self): + ''' + for testsToRun create a function that reads self.testsToRun and do the following: + - look for test_ files in dir and subdir (ex: test_ovs) and load it importing + its class (ex from test_ovs import OVS - class name associated with ovs in CATALOGUE) + - returns a dict (ex: self.setupTests) with teststorun:classes objects + following self.testParams + this dict will be used to be used in runTests function + ''' + path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests + matches = [] + for root, dirnames, filenames in os.walk(path): + for filename in fnmatch.filter(filenames, 'test_*'): + matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. + def runTests(self): ''' Two approaches here depending on self.use_pytest = True or False: @@ -125,7 +166,10 @@ def runTests(self): ''' # call setup with the name argument. This name will be used by logger. # The name is an itervalue from self.testsToRun dictionary. Pass each key as an argument, if the corresponding value is true. - pass + self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. + for key,tests in self.testsToRun.items(): + if tests == True: + logger = setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. class RFUnitTests(object): @@ -134,13 +178,14 @@ def __init__(self, logger): self.logger = logger self.tests = {} - def evaluate(self, capfd): ''' for each process in self.tests run it using capfd This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' + #logger.info('TestOVS Network ovs-vsctl') + #subprocess.call('ovs-vsctl show | grep dp0', shell = True) pass def verify(self, tests_out): @@ -177,143 +222,6 @@ def run_tests(): ''' pass - -class OVS(RFUnitTests): - - ''' - TESTS contain association of commands and a dict containing - the method of evaluation of the command and the expected output - ''' - TESTS = { - 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, - 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, - } - - def __init__(self, logger): - super().__init__(logger) - - - def addTestsDefault(self): - self.tests = TESTS - - def addTest(self, cmd, method, output): - ''' - add in self.tests new tests following the TEST structure - ''' - pass - - def setTestsParams(self, cmd, param): - ''' - Define for example self.containernames, self.mongoport, self.controllerport - for Container, Mongo, Controller classes respectively - In this case modify cmd in self.tests - ''' - pass - - def run_tests(self): - ''' - basically runs methods inherited with self.tests attribute - self.evaluate - self.verify - self.analyse - ''' - pass - - - def test_netwokvsctl(self, logger, capfd): - logger.info('TestOVS Network ovs-vsctl') - subprocess.call('ovs-vsctl show | grep dp0', shell = True) - out,err = capfd.readouterr() - logger.info("\n ==== TestOVS Network ovs-vsctl ==== \n ") - logger.info("OUTPUT\n %s", out) - logger.info("ERROR\n %s", err) - assert out.find("dp0") != -1 - - def test_netwokdpctl(self,capfd): - self.logger.info('TestOVS Network ovs-dpctl') - subprocess.call('ovs-dpctl show | grep dp0', shell = True) - out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") - txtfile.write(err) - assert out.find("dp0") != -1 - - - def test_processdbserver(self,capfd): - self.logger.info('TestOVS Process ovsdb server') - subprocess.call('ps aux | grep ovs', shell = True) - out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") - txtfile.write(err) - assert out.find("ovsdb-server") != -1 - - def test_processswitchd(self,capfd): - self.logger.info('TestOVS Process ovs-vswitchd') - subprocess.call('ps aux | grep ovs', shell = True) - out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") - txtfile.write(err) - assert out.find("ovs-vswitchd") != -1 - - def test_specific(self,capfd): - self.logger.info('TestOVS Specific') - #pass - - def run_tests(): - self.logger.info('TestOVS testcases start execution') - #self.test_netwok() - #self.test_process() - #self.test_specific() - - def verify_tests(): - pass - -class Testrfserver(): - - logger = logging.getLogger('TestRFServer') - - def test_processrfserverrunning(self,capfd): - self.logger.info('Test for process rfserver running') - subprocess.call('ps aux| grep rfserver', shell = True) - out,err = capfd.readouterr() - str2 = 'python ./rfserver/rfserver/py' - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfserver running ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfserver running ==== \n ") - txtfile.write(err) - assert out.find(str2) != -1 - -class Testrfproxy(): - - logger = logging.getLogger('TestRFProxy') - - def test_processrfproxy(self,capfd): - self.logger.info('Test for process rfproxy running') - subprocess.call('ps aux| grep rfproxy', shell = True) - out,err = capfd.readouterr() - str2 = 'ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py' - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfproxy running ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfproxy running ==== \n ") - txtfile.write(err) - assert out.find(str2) != -1 - - if __name__ == '__main__': description = 'RFTest suite, to run the tests and determine the state of system' epilog = 'Report bugs to: https://github.com/routeflow/RouteFlow/issues' @@ -322,12 +230,17 @@ def test_processrfproxy(self,capfd): #islconf = os.path.dirname(os.path.realpath(__file__)) + "/islconf.csv" parser = argparse.ArgumentParser(description=description, epilog=epilog) + #run pytest(y/n) parser.add_argument('-p', '--pytest', default=False, type = bool, help='Run tests with pytest(True/False)') + #accept a list of test modules to be run. - parser.add_argument('-tc', '--testcases', choices =['ovs','rfapps','mongo','containers'], - help='Testcases to be run.choose form the list specified') + #parser.add_argument('-tc', '--testcases', choices =['ovs','rfapps','mongo','containers'], + # help='Testcases to be run.choose form the list specified') + + parser.add_argument('-tc', '--testcases', type=json.loads, + help="Testcases to be run.enter on a dict format like :{'ovs':True, 'containers':False, 'rfapps':True}") #list of containers : either user gives his own container names(option:l) or chooses from the list(option:ln) parser.add_argument('-l', '--lxc', nargs='*', @@ -335,6 +248,10 @@ def test_processrfproxy(self,capfd): parser.add_argument('-ln', '--lxcnames', choices =['rfvm1','b1','b2','rfvmA','rfvmB'], help='Lxc container name, should be given to verify, default not supported,to be choosen from the list') + #list of rfapps + parser.add_argument('-rf', '--rfapps', choices = ['rfserver', 'rfproxy', 'rfclient'], + help='list of rfapps to be tested. select from the choice') + #accept the mongodb port. default 27017 parser.add_argument('-m', '--mongoport', default=27017, type = int, help='port number to verify running of mongodb') @@ -348,6 +265,9 @@ def test_processrfproxy(self,capfd): args = parser.parse_args() testsobj = Tests() - testsobj.setTestsToRun(args.pytest,args.testcases) - testsobj.configureTests(mongo = args.mongoport, containers = args.lxcnames) + mydict = {} + #mydict = args.testcases + #testsobj.setTestsToRun(args.pytest, **mydict) #args.testcases will be a dictionary that is passed. + testsobj.setTestsToRun(True, args.testcases) #args.testcases will be a dictionary that is passed. + testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps ) testsobj.runTests() diff --git a/rftest/tests/test_mongo.py b/rftest/tests/test_mongo.py new file mode 100644 index 00000000..e69de29b diff --git a/rftest/tests/test_rfapps.py b/rftest/tests/test_rfapps.py new file mode 100644 index 00000000..120bd872 --- /dev/null +++ b/rftest/tests/test_rfapps.py @@ -0,0 +1,33 @@ +class Testrfserver(): + + logger = logging.getLogger('TestRFServer') + + def test_processrfserverrunning(self,capfd): + self.logger.info('Test for process rfserver running') + subprocess.call('ps aux| grep rfserver', shell = True) + out,err = capfd.readouterr() + str2 = 'python ./rfserver/rfserver/py' + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfserver running ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfserver running ==== \n ") + txtfile.write(err) + assert out.find(str2) != -1 + +class Testrfproxy(): + + logger = logging.getLogger('TestRFProxy') + + def test_processrfproxy(self,capfd): + self.logger.info('Test for process rfproxy running') + subprocess.call('ps aux| grep rfproxy', shell = True) + out,err = capfd.readouterr() + str2 = 'ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py' + with open("testout.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfproxy running ==== \n ") + txtfile.write(out) + with open("testerr.txt","a") as txtfile: + txtfile.write("\n ==== Test for process rfproxy running ==== \n ") + txtfile.write(err) + assert out.find(str2) != -1 From c4aea794142a382893e6d26d397c45eadc8b8276 Mon Sep 17 00:00:00 2001 From: sai krishna Date: Thu, 16 Jul 2015 08:02:36 +0000 Subject: [PATCH 07/43] half way through calling the functions --- rftest/tests/test_RF.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 8cdafbed..31f5ce4e 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -152,6 +152,12 @@ def findTests(self): for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, 'test_*'): matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. + for obj in enumerate(matches): + if obj in self.testsToRun.keys(): + if self.testsToRun[obj] == True: + #create an object to the class and run the tests. + + def runTests(self): ''' @@ -169,7 +175,7 @@ def runTests(self): self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. for key,tests in self.testsToRun.items(): if tests == True: - logger = setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. + logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. class RFUnitTests(object): From c89cf41bd2d368a45839e7d5246b9cbf8009c85b Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 17 Jul 2015 07:25:22 +0000 Subject: [PATCH 08/43] commiting broken code for the first time, couldnot help myself. Confused with OOP --- rftest/tests/test_Ovs.py | 64 +++++++++++++++++++++------------------- rftest/tests/test_RF.py | 46 +++++++++++++++++------------ 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/rftest/tests/test_Ovs.py b/rftest/tests/test_Ovs.py index f14f184d..a19f5bae 100644 --- a/rftest/tests/test_Ovs.py +++ b/rftest/tests/test_Ovs.py @@ -1,37 +1,41 @@ -from test_RF import RFUnitTests -#from test_RF import logger - -class OVS(RFUnitTests): - - ''' - TESTS contain association of commands and a dict containing - the method of evaluation of the command and the expected output - ''' - TESTS = { - 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, - 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, - } - - def __init__(self, logger): - super().__init__(logger) - - def addTestsDefault(self): - self.tests = TESTS - - def addTest(self, cmd, method, output): - ''' - add in self.tests new tests following the TEST structure - ''' - pass - - def setTestsParams(self, cmd, param): - ''' +from test_RF import RFUnitTests +#from test_RF import logger + +class OVS(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = { + 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, + 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, + } + + def __init__(self, logger): + super().__init__(logger) + + def addTestsDefault(self): + self.tests = TESTS #Where did this "tests" come from ? simply return tests is enough ?? + return tests + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' + pass + + def setTestsParams(self, cmd, param): + ''' Define for example self.containernames, self.mongoport, self.controllerport - for Container, Mongo, Controller classes respectively - In this case modify cmd in self.tests + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests ''' pass + def evaluate(self, capfd): + executiondict = super(OVS,self).evaluate(self.tests) + def run_tests(self): ''' basically runs methods inherited with self.tests attribute diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 31f5ce4e..8753104f 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -20,9 +20,9 @@ class Tests: CATALOGUE = { - 'ovs':'OVS', - 'containers':'Containers', - 'rfapps':'RFApps', + 'test_OVS':'ovs', + 'test_Containers':'containers', + 'test_RFApps':'rfapps', } LOGLEVEL = { 10 : 'DEBUG', @@ -36,21 +36,22 @@ def __init__(self): self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} - #This can be a single dictionary. txt terminal json and raw. + #This can be a single dictionary. txt, terminal json and raw. self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False #def setTestsToRun(self, *args, **kwargs): - def setTestsToRun(self, isPytest,**kwargs): + def setTestsToRun(self, *kargs, **kwargs): ''' fill self.testsToRun with proper arguments like lxc, ovs, rfserver, rfproxy,.... true or false define if tests will use pytest or not (self.use_pytest = False) ''' - self.use_pytest = isPytest + if 'pytest' in kwargs.keys(): + self.use_pytest = kwargs['pytest'] #If kwargs is empty leave the testsToRun dictionary with default arguments. if kwargs: @@ -66,7 +67,8 @@ def configureTests(self, **kwargs): if kwargs: self.testsParams.clear() for key,param in kwargs.items(): - self.testsParams[key] = param + if key in self.testsParams.keys(): + self.testsParams[key] = param #def setTestsOutput(self, *args, **kwargs): def setTestsOutputFormat(self, **kwargs): @@ -92,8 +94,9 @@ def setTestsOutputModes(self, **kwargs): if kwargs: for key,modes in kwargs.items(): self.outputModes[key] = modes - del self.outputModes['raw'] - self.outputModes['raw'] = True + + #del self.outputModes['raw'] #These two line(this and the one below) are temporary, If seen in Vandervecken branch, please delete. + #self.outputModes['raw'] = True def setup(self,name,output_dir): ''' @@ -153,12 +156,13 @@ def findTests(self): for filename in fnmatch.filter(filenames, 'test_*'): matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. for obj in enumerate(matches): - if obj in self.testsToRun.keys(): + if obj in Tests.CATALOGUE.keys() and Tests.CATALOGUE[obj] in self.testsToRun.keys(): if self.testsToRun[obj] == True: - #create an object to the class and run the tests. - - - + #create an object to the class and run the tests. How?? + # from rftest.tests.____?? import * + #How to build a dictionary "setUpTests ?? + pass + def runTests(self): ''' Two approaches here depending on self.use_pytest = True or False: @@ -178,18 +182,22 @@ def runTests(self): logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. + class RFUnitTests(object): def __init__(self, logger): self.logger = logger self.tests = {} - def evaluate(self, capfd): + def evaluate(self, capfd): #I think this defnition should be modified to accept "tests" dictionary from derived classes. ''' for each process in self.tests run it using capfd This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' + logger.info('TestOVS Network ovs-vsctl') + out,err = subprocess.call( + return executiondict #logger.info('TestOVS Network ovs-vsctl') #subprocess.call('ovs-vsctl show | grep dp0', shell = True) pass @@ -271,9 +279,9 @@ def run_tests(): args = parser.parse_args() testsobj = Tests() - mydict = {} + #mydict = {} #mydict = args.testcases - #testsobj.setTestsToRun(args.pytest, **mydict) #args.testcases will be a dictionary that is passed. - testsobj.setTestsToRun(True, args.testcases) #args.testcases will be a dictionary that is passed. + testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. + #testsobj.setTestsToRun(True, **mydict) #args.testcases will be a dictionary that is passed. testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps ) - testsobj.runTests() + testsobj.runTests() \ No newline at end of file From 2e3d26f78ce1b8614430749aea21765f6c949b4f Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 17 Jul 2015 10:00:25 +0000 Subject: [PATCH 09/43] doubts regarding OOP clarified --- rftest/tests/test_RF.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 8753104f..efb947fc 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -20,9 +20,9 @@ class Tests: CATALOGUE = { - 'test_OVS':'ovs', - 'test_Containers':'containers', - 'test_RFApps':'rfapps', + 'test_OVS':'OVS', + 'test_Containers':'Containers', + 'test_RFApps':['RFserver','RFproxy','RFclient'] } LOGLEVEL = { 10 : 'DEBUG', @@ -40,7 +40,7 @@ def __init__(self): self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False - + self.setUpTests = {} #def setTestsToRun(self, *args, **kwargs): def setTestsToRun(self, *kargs, **kwargs): @@ -158,6 +158,15 @@ def findTests(self): for obj in enumerate(matches): if obj in Tests.CATALOGUE.keys() and Tests.CATALOGUE[obj] in self.testsToRun.keys(): if self.testsToRun[obj] == True: + if type(Tests.CATALOGUE[obj]) is list: + for classes_ in Tests.CATALOGUE[obj]: + module = __import__(obj) + class_ = getattr(module, classes_) + self.setupTests[obj] = class_() + else: + module = __import__(obj) + class_ = getattr(module, Tests.CATALOGUE[obj]) + self.setupTests[obj] = class_() #create an object to the class and run the tests. How?? # from rftest.tests.____?? import * #How to build a dictionary "setUpTests ?? @@ -187,14 +196,15 @@ class RFUnitTests(object): def __init__(self, logger): self.logger = logger - self.tests = {} + - def evaluate(self, capfd): #I think this defnition should be modified to accept "tests" dictionary from derived classes. + def evaluate(self, capfd): #I think this defnition should be modified to accept "tests" dictionary from derived classes(from test_ovs or test_mongo .. classes). ''' for each process in self.tests run it using capfd This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' + self.tests logger.info('TestOVS Network ovs-vsctl') out,err = subprocess.call( return executiondict From 509261a0d2518e8d06a05adb742b0e695785e666 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 23 Jul 2015 00:31:11 +0000 Subject: [PATCH 10/43] classes for RF Components --- rftest/tests/test_Containers.py | 47 +++++++++++ rftest/tests/test_Mongo.py | 48 ++++++++++++ rftest/tests/test_Ovs.py | 133 ++++++++++---------------------- rftest/tests/test_RFApps.py | 109 ++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 94 deletions(-) create mode 100644 rftest/tests/test_Containers.py create mode 100644 rftest/tests/test_Mongo.py create mode 100644 rftest/tests/test_RFApps.py diff --git a/rftest/tests/test_Containers.py b/rftest/tests/test_Containers.py new file mode 100644 index 00000000..393acc82 --- /dev/null +++ b/rftest/tests/test_Containers.py @@ -0,0 +1,47 @@ +from test_RF import RFUnitTests + +class Containers(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = {} + + def __init__(self, logger): + super().__init__(logger) + + def addTestsDefault(self): + self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' + self.TESTS[str(cmd)] = {'method':str(method), + 'output':str(output),} + + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + return str(cmd) + str(param) + + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + self.addTestsDefault() + cmd = self.setTestsParams("lxc-info -n","rfvmA") + self.addTest(cmd,"find","state: RUNNING") + self.logger.getlogger("Test_containers") + self.logger.INFO("Test containers class Begin") + self.evaluate() + self.verify() + self.analyse() diff --git a/rftest/tests/test_Mongo.py b/rftest/tests/test_Mongo.py new file mode 100644 index 00000000..958a63be --- /dev/null +++ b/rftest/tests/test_Mongo.py @@ -0,0 +1,48 @@ +from test_RF import RFUnitTests + +class Mongo(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = { + 'ps aux | grep mongo':{'method':'find', 'output':'mongod'}, + } + + def __init__(self, logger): + super().__init__(logger) + + def addTestsDefault(self): + self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' + self.TESTS[str(cmd)] = {'method':str(method), + 'output':str(output),} + + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + return str(cmd) + str(param) + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + self.addTestsDefault() + cmd = self.setTestsParams("netstat -plant | grep", 27017) + self.addTest(cmd,"find","mongod") + self.logger.getlogger("Test_Mongod") + self.logger.INFO("Test Mongod class Begin") + self.evaluate() + self.verify() + self.analyse() diff --git a/rftest/tests/test_Ovs.py b/rftest/tests/test_Ovs.py index a19f5bae..22555cfc 100644 --- a/rftest/tests/test_Ovs.py +++ b/rftest/tests/test_Ovs.py @@ -1,41 +1,38 @@ -from test_RF import RFUnitTests -#from test_RF import logger - -class OVS(RFUnitTests): - - ''' - TESTS contain association of commands and a dict containing - the method of evaluation of the command and the expected output - ''' - TESTS = { - 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, - 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, - } - - def __init__(self, logger): - super().__init__(logger) - - def addTestsDefault(self): - self.tests = TESTS #Where did this "tests" come from ? simply return tests is enough ?? - return tests - - def addTest(self, cmd, method, output): - ''' - add in self.tests new tests following the TEST structure - ''' - pass - - def setTestsParams(self, cmd, param): - ''' +from test_RF import RFUnitTests + +class OVS(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = { + 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, + 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, + } + + def __init__(self, logger): + super().__init__(logger) + + def addTestsDefault(self): + self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' + self.TESTS[str(cmd)] = {'method':str(method), + 'output':str(output),} + + def setTestsParams(self, cmd, param): + ''' Define for example self.containernames, self.mongoport, self.controllerport - for Container, Mongo, Controller classes respectively - In this case modify cmd in self.tests + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests ''' pass - def evaluate(self, capfd): - executiondict = super(OVS,self).evaluate(self.tests) - + def run_tests(self): ''' basically runs methods inherited with self.tests attribute @@ -43,63 +40,11 @@ def run_tests(self): self.verify self.analyse ''' - pass - - def test_netwokvsctl(self, logger, capfd): - logger.info('TestOVS Network ovs-vsctl') - subprocess.call('ovs-vsctl show | grep dp0', shell = True) - out,err = capfd.readouterr() - logger.info("\n ==== TestOVS Network ovs-vsctl ==== \n ") - logger.info("OUTPUT\n %s", out) - logger.info("ERROR\n %s", err) - assert out.find("dp0") != -1 - - def test_netwokdpctl(self,capfd): - self.logger.info('TestOVS Network ovs-dpctl') - subprocess.call('ovs-dpctl show | grep dp0', shell = True) - out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Network ovs-dpctl ==== \n ") - txtfile.write(err) - assert out.find("dp0") != -1 - - - def test_processdbserver(self,capfd): - self.logger.info('TestOVS Process ovsdb server') - subprocess.call('ps aux | grep ovs', shell = True) - out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovsdb server === \n ") - txtfile.write(err) - assert out.find("ovsdb-server") != -1 - - def test_processswitchd(self,capfd): - self.logger.info('TestOVS Process ovs-vswitchd') - subprocess.call('ps aux | grep ovs', shell = True) - out,err = capfd.readouterr() - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== TestOVS Process ovs-vswitchd === \n ") - txtfile.write(err) - assert out.find("ovs-vswitchd") != -1 - - def test_specific(self,capfd): - self.logger.info('TestOVS Specific') - #pass - - def run_tests(): - self.logger.info('TestOVS testcases start execution') - #self.test_netwok() - #self.test_process() - #self.test_specific() - - def verify_tests(): - pass + self.addTestsDefault() + self.addTest("ps aux | grep dp0","find","ovsdb-server") + self.addTest("ps aux | grep dp0","find","ovs-vswitchd") + self.logger.getlogger("Test_OVS") + self.logger.INFO("Test OVS class Begin") + self.evaluate() + self.verify() + self.analyse() diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py new file mode 100644 index 00000000..b0e8cf08 --- /dev/null +++ b/rftest/tests/test_RFApps.py @@ -0,0 +1,109 @@ +from test_RF import RFUnitTests + +class RFApps(RFUnitTests): + + ''' + TESTS contain association of commands and a dict containing + the method of evaluation of the command and the expected output + ''' + TESTS = {} + + def __init__(self, logger): + super().__init__(logger) + + def addTestsDefault(self): + self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. + + def addTest(self, cmd, method, output): + ''' + add in self.tests new tests following the TEST structure + ''' + self.TESTS[str(cmd)] = {'method':str(method), + 'output':str(output),} + + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + return str(cmd) + str(param) + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + pass + +class RFserver(RFApps): + + def __init__(self, logger): + super().__init__(logger) + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + self.addTestsDefault() + self.addTest("ps aux | grep rfserver","find","python ./rfserver/rfserver/py") + self.logger.getlogger("Test_rfserver") + self.logger.INFO("Test rfserver class Begin") + self.evaluate() + self.verify() + self.analyse() + +class RFproxy(RFApps): + + def __init__(self, logger): + super().__init__(logger) + + #ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py" + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + return str(cmd) + str(param) + + def setTestsOutputs(self, output1, output2, param): + ''' + The output to be analysed contains port number as argument. + To accomodate this change add a method specific to this class. + :output1, output2: string that is to be tested + :param: controller port number + ''' + return str(output1) + str(param) + str(output2) + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + self.addTestsDefault() + output = setTestsOutputs('ryu-manager --use-stderr --ofp-tcp-listen-port=',' ryu-rfproxy/rfproxy.py', 6633) + self.addTest("ps aux | grep rfproxy","find",output) + output = setTestsOutputs('ryu-manager --use-stderr --ofp-tcp-listen-port=',' ryu-rfproxy/rfproxy.py', 6653) + self.addTest("ps aux | grep rfproxy","find",output) + + output = setTestsOutputs('127.0.0.1:', '', 6633) + cmd = self.setTestsParams("netstat -plant | grep", 6633) + self.addTest(cmd,"find",output) + + output = setTestsOutputs('127.0.0.1:', '', 6653) + cmd = self.setTestsParams("netstat -plant | grep", 6653) + self.addTest(cmd,"find",output) + + self.logger.getlogger("Test_rfproxy") + self.logger.INFO("Test rfproxy class Begin") + self.evaluate() + self.verify() + self.analyse() From c813e32fbdd80167dad6bc9c5356bffb87347b65 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 23 Jul 2015 00:33:02 +0000 Subject: [PATCH 11/43] modified test_RF --- rftest/tests/test_RF.py | 66 +++++++++++++++++--------------------- rftest/tests/test_mongo.py | 0 2 files changed, 30 insertions(+), 36 deletions(-) delete mode 100644 rftest/tests/test_mongo.py diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index efb947fc..9b17fa02 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -21,6 +21,7 @@ class Tests: CATALOGUE = { 'test_OVS':'OVS', + 'test_Mongo':'Mongo', 'test_Containers':'Containers', 'test_RFApps':['RFserver','RFproxy','RFclient'] } @@ -35,14 +36,12 @@ class Tests: def __init__(self): self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} - - #This can be a single dictionary. txt, terminal json and raw. self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False self.setUpTests = {} + self.logger = None - #def setTestsToRun(self, *args, **kwargs): def setTestsToRun(self, *kargs, **kwargs): ''' fill self.testsToRun with proper arguments @@ -86,7 +85,6 @@ def setTestsOutputFormat(self, **kwargs): def setTestsOutputModes(self, **kwargs): ''' check if tests results must be saved - in txt file or sent to terminal in json or raw formats (first let`s save in raw format) ''' @@ -94,11 +92,8 @@ def setTestsOutputModes(self, **kwargs): if kwargs: for key,modes in kwargs.items(): self.outputModes[key] = modes - - #del self.outputModes['raw'] #These two line(this and the one below) are temporary, If seen in Vandervecken branch, please delete. - #self.outputModes['raw'] = True - def setup(self,name,output_dir): + def setup(self,output_dir): ''' takes self.testsToRun, self.outputFormat, self.outputModes and prepare environment to runtests @@ -118,28 +113,25 @@ def setup(self,name,output_dir): following self.testParams this dict will be used to be used in runTests function - :name: used to print the class name in the get logger. :output_dir: directory to store logs ''' - logger = logging.getLogger(name) - logger.setLevel(logging.DEBUG) + self.logger = logging.getLogger() + self.logger.setLevel(logging.DEBUG) #INFO handler handler = logging.FileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true") handler.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") handler.setFormatter(formatter) - logger.addHandler(handler) + self.logger.addHandler(handler) #ERROR handler handler = logging.FileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true") handler.setLevel(logging.ERROR) formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") handler.setFormatter(formatter) - logger.addHandler(handler) - - return logger + self.logger.addHandler(handler) def findTests(self): ''' @@ -159,19 +151,16 @@ def findTests(self): if obj in Tests.CATALOGUE.keys() and Tests.CATALOGUE[obj] in self.testsToRun.keys(): if self.testsToRun[obj] == True: if type(Tests.CATALOGUE[obj]) is list: - for classes_ in Tests.CATALOGUE[obj]: + for classes_ in Tests.CATALOGUE[obj]: #For each file, instantiate an object for the classes in it. + #for classes_ in Tests.CATALOGUE[obj]: module = __import__(obj) class_ = getattr(module, classes_) self.setupTests[obj] = class_() else: module = __import__(obj) class_ = getattr(module, Tests.CATALOGUE[obj]) - self.setupTests[obj] = class_() - #create an object to the class and run the tests. How?? - # from rftest.tests.____?? import * - #How to build a dictionary "setUpTests ?? - pass - + self.setupTests[obj] = class_(self.logger) + def runTests(self): ''' Two approaches here depending on self.use_pytest = True or False: @@ -185,29 +174,32 @@ def runTests(self): ''' # call setup with the name argument. This name will be used by logger. # The name is an itervalue from self.testsToRun dictionary. Pass each key as an argument, if the corresponding value is true. - self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. - for key,tests in self.testsToRun.items(): - if tests == True: - logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. + #self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. + #for key,tests in self.testsToRun.items(): + # if tests == True: + # logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. + for test in self.setupTests.keys(): + self.setupTests[test].run_tests() class RFUnitTests(object): def __init__(self, logger): self.logger = logger - - def evaluate(self, capfd): #I think this defnition should be modified to accept "tests" dictionary from derived classes(from test_ovs or test_mongo .. classes). + + def evaluate(self, capfd): ''' for each process in self.tests run it using capfd This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' - self.tests - logger.info('TestOVS Network ovs-vsctl') - out,err = subprocess.call( - return executiondict + self.tests + #Extract the list which is value of another list. save it as value. + for key,value in tests.items(): + out,err = subprocess.call(key #Extract the values from value above and save it to our executuindict. + executiondict[key] #logger.info('TestOVS Network ovs-vsctl') #subprocess.call('ovs-vsctl show | grep dp0', shell = True) pass @@ -244,7 +236,7 @@ def run_tests(): just pass, the classes inheriting RFUnitTests will overwrite this function ''' - pass + #pass if __name__ == '__main__': description = 'RFTest suite, to run the tests and determine the state of system' @@ -292,6 +284,8 @@ def run_tests(): #mydict = {} #mydict = args.testcases testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. - #testsobj.setTestsToRun(True, **mydict) #args.testcases will be a dictionary that is passed. - testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps ) - testsobj.runTests() \ No newline at end of file + testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} + testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) + testsobj.findTests() + testsobj.setup() + testsobj.runTests() diff --git a/rftest/tests/test_mongo.py b/rftest/tests/test_mongo.py deleted file mode 100644 index e69de29b..00000000 From eb489a4126ab20e951c394c6b882b7d17caa0b00 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 23 Jul 2015 00:35:06 +0000 Subject: [PATCH 12/43] unwanted rfapps module --- rftest/tests/test_rfapps.py | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 rftest/tests/test_rfapps.py diff --git a/rftest/tests/test_rfapps.py b/rftest/tests/test_rfapps.py deleted file mode 100644 index 120bd872..00000000 --- a/rftest/tests/test_rfapps.py +++ /dev/null @@ -1,33 +0,0 @@ -class Testrfserver(): - - logger = logging.getLogger('TestRFServer') - - def test_processrfserverrunning(self,capfd): - self.logger.info('Test for process rfserver running') - subprocess.call('ps aux| grep rfserver', shell = True) - out,err = capfd.readouterr() - str2 = 'python ./rfserver/rfserver/py' - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfserver running ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfserver running ==== \n ") - txtfile.write(err) - assert out.find(str2) != -1 - -class Testrfproxy(): - - logger = logging.getLogger('TestRFProxy') - - def test_processrfproxy(self,capfd): - self.logger.info('Test for process rfproxy running') - subprocess.call('ps aux| grep rfproxy', shell = True) - out,err = capfd.readouterr() - str2 = 'ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py' - with open("testout.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfproxy running ==== \n ") - txtfile.write(out) - with open("testerr.txt","a") as txtfile: - txtfile.write("\n ==== Test for process rfproxy running ==== \n ") - txtfile.write(err) - assert out.find(str2) != -1 From ebb343e036ae5f90a467060ffc0e9af4401cf753 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 23 Jul 2015 08:01:39 +0000 Subject: [PATCH 13/43] update RFUnitTest class --- rftest/tests/test_RF.py | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 9b17fa02..c048dfa6 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -183,10 +183,27 @@ def runTests(self): self.setupTests[test].run_tests() +class Dictlist(dict): + ''' + class to accept dictionary of lists. + Required to handle the outputDictionary in RFUnitTests. + The commands that are to be executed are not unique within testcases. + Hence this class is required. + ''' + def __setitem__(self, key, value): + try: + self[key] + except KeyError: + super(Dictlist, self).__setitem__(key, []) + self[key].append(value) + class RFUnitTests(object): def __init__(self, logger): self.logger = logger + self.evaluateDictionary = Dictlist() + self.verifyDictionary = Dictlist() + #self.dictlist = Dictlist() def evaluate(self, capfd): @@ -197,12 +214,17 @@ def evaluate(self, capfd): ''' self.tests #Extract the list which is value of another list. save it as value. + # I think it is not required to return the dictionary since the object for each class is instantiated seperately. + #Everytime a new object calls the evaluate function, it will be followed by verify and analyse. + #Each time a new object calls the evaluate, a the dictionary will be cleared. + #This approach is fine as long as we follow the non-threading approach. are we planning to use threads?? not in near future. + if self.evaluateDictionary: + self.evaluateDictionary.clear() for key,value in tests.items(): - out,err = subprocess.call(key #Extract the values from value above and save it to our executuindict. - executiondict[key] - #logger.info('TestOVS Network ovs-vsctl') - #subprocess.call('ovs-vsctl show | grep dp0', shell = True) - pass + out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. + self.evaluateDictionary[key] = {'out' : out, + 'err' : err, } # since we are only executing command, err will always be empty. + def verify(self, tests_out): ''' @@ -216,7 +238,25 @@ def verify(self, tests_out): associate dict {command: {'assert':False, 'result':err}} returns dict with commands:assertions,results No need to write assertion + + Verify if the command is a list or not. define behaviour accordingly. ''' + # how to access the values in each list item which is a key.?? + #compare the + #if type(self.evaluateDictionary[key]) is list: + # for tests_ in self.evaluateDictionary[key]: + # tests_. + + for inputs,valuelist in self.evaluateDictionary.items(): + #if type(self.evaluateDictionary[inputs]) is list: values is a list by default + for commandOutput in valuelist: + for key,values in commandOutput.items(): #key : 'out','errr' values: console out and err. + #write the compare logic and save to verifyDictionary. + self.tests[str(inputs)][] # inputs here is equal to the command of tests dictionary. + + else: + for key,values in self.evaluateDictionary[inputs]: + #write the compare logic and save to verifyDictionary. pass def analyse(self, verify_out): From 07b88b90c73a1f61e9db6d3386e3c7422c4ab529 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 24 Jul 2015 14:04:12 +0000 Subject: [PATCH 14/43] corrected syntax errors --- rftest/tests/test_RF.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index c048dfa6..e48f4e4d 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -155,11 +155,11 @@ def findTests(self): #for classes_ in Tests.CATALOGUE[obj]: module = __import__(obj) class_ = getattr(module, classes_) - self.setupTests[obj] = class_() + self.setUpTests[obj] = class_(self.logger) else: module = __import__(obj) class_ = getattr(module, Tests.CATALOGUE[obj]) - self.setupTests[obj] = class_(self.logger) + self.setUpTests[obj] = class_(self.logger) def runTests(self): ''' @@ -179,8 +179,8 @@ def runTests(self): # if tests == True: # logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. - for test in self.setupTests.keys(): - self.setupTests[test].run_tests() + for test in self.setUpTests.keys(): + self.setUpTests[test].run_tests() class Dictlist(dict): @@ -226,7 +226,7 @@ def evaluate(self, capfd): 'err' : err, } # since we are only executing command, err will always be empty. - def verify(self, tests_out): + def verify(self): ''' receives dict {command: {'out':output,'err':err} ) run comparisons of responses (output/err) and @@ -251,15 +251,16 @@ def verify(self, tests_out): #if type(self.evaluateDictionary[inputs]) is list: values is a list by default for commandOutput in valuelist: for key,values in commandOutput.items(): #key : 'out','errr' values: console out and err. + pass #write the compare logic and save to verifyDictionary. - self.tests[str(inputs)][] # inputs here is equal to the command of tests dictionary. + #self.tests[str(inputs)][] # inputs here is equal to the command of tests dictionary. else: for key,values in self.evaluateDictionary[inputs]: + pass #write the compare logic and save to verifyDictionary. - pass - def analyse(self, verify_out): + def analyse(self): ''' receives dict of verify_processes check assertion values and write results to logger @@ -327,5 +328,5 @@ def run_tests(): testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) testsobj.findTests() - testsobj.setup() + testsobj.setup(os.path.dirname(os.path.realpath(__file__))) testsobj.runTests() From f57073494e8f5134afc3aff2efaed360e74b6c3e Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 24 Jul 2015 16:56:27 +0000 Subject: [PATCH 15/43] added verify function. analyze is still pending --- rftest/tests/test_RF.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index e48f4e4d..b3029224 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -222,8 +222,8 @@ def evaluate(self, capfd): self.evaluateDictionary.clear() for key,value in tests.items(): out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. - self.evaluateDictionary[key] = {'out' : out, - 'err' : err, } # since we are only executing command, err will always be empty. + self.evaluateDictionary[key] = {'out' : str(out), + 'err' : str(err), } # since we are only executing command, err will always be empty. def verify(self): @@ -241,24 +241,26 @@ def verify(self): Verify if the command is a list or not. define behaviour accordingly. ''' - # how to access the values in each list item which is a key.?? - #compare the - #if type(self.evaluateDictionary[key]) is list: - # for tests_ in self.evaluateDictionary[key]: - # tests_. + #sample evaluateDictionary. + #{'ovs-vsctl show | grep dp0': [{'err': 'krishna sai klfhahsd dp01', + # 'out': 'sai krishnaalskdfkaskj dp0'}], + # 'test': [1]} + + #In [48]: evaluateDictionary['ovs-vsctl show | grep dp0'][0].keys()[0] + #Out[48]: 'err' + for inputs,valuelist in self.evaluateDictionary.items(): - #if type(self.evaluateDictionary[inputs]) is list: values is a list by default - for commandOutput in valuelist: - for key,values in commandOutput.items(): #key : 'out','errr' values: console out and err. - pass - #write the compare logic and save to verifyDictionary. - #self.tests[str(inputs)][] # inputs here is equal to the command of tests dictionary. - - else: - for key,values in self.evaluateDictionary[inputs]: - pass - #write the compare logic and save to verifyDictionary. + for vlist in valuelist: + if type(vlist) is dict: + for outerr,consoleMessage in vlist.items(): + #This is for out.find("dp0") Then it is found + if self.tests[inputs]['method'] == 'find' && self.evaluateDictionary[inputs][0].keys()[0] == 'out': #This condition is to be handled properly. This will fail. + if str(self.evaluateDictionary[inputs][outerr]).find(self.tests[inputs]['output']) != -1: + verifyDictionary[inputs] = {'assert':True, 'result':str(self.evaluateDictionary[inputs][vlist]['out'])} + else : + verifyDictionary[inputs] = {'assert':False, 'result':str(self.evaluateDictionary[inputs][vlist]['err'])} + def analyse(self): ''' @@ -272,7 +274,7 @@ def analyse(self): pass - def run_tests(): + def run_tests(self): ''' just pass, the classes inheriting RFUnitTests will overwrite this function From c11d3ab7e6290c658c3eb3ae5f142e4108f5e94b Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Sat, 25 Jul 2015 11:34:29 +0000 Subject: [PATCH 16/43] done with implementation except for one clumsy if condition --- rftest/tests/test_RF.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index b3029224..b38c6cf4 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -249,13 +249,13 @@ def verify(self): #In [48]: evaluateDictionary['ovs-vsctl show | grep dp0'][0].keys()[0] #Out[48]: 'err' - + #key of self.evaluateDictionary is always equal to key of self.tests dictionary. This is how data structure is built.Hence tests[inputs] will work. for inputs,valuelist in self.evaluateDictionary.items(): for vlist in valuelist: if type(vlist) is dict: for outerr,consoleMessage in vlist.items(): #This is for out.find("dp0") Then it is found - if self.tests[inputs]['method'] == 'find' && self.evaluateDictionary[inputs][0].keys()[0] == 'out': #This condition is to be handled properly. This will fail. + if self.tests[inputs]['method'] == 'find' && self.evaluateDictionary[inputs][0].keys()[0] == 'out': #The second 'if' condition is to be handled properly. This will fail. if str(self.evaluateDictionary[inputs][outerr]).find(self.tests[inputs]['output']) != -1: verifyDictionary[inputs] = {'assert':True, 'result':str(self.evaluateDictionary[inputs][vlist]['out'])} else : @@ -271,8 +271,11 @@ def analyse(self): self.logger.info("ERROR\n %s", err) No need to write assertion ''' - pass - + for keys,values in verifyDictionary.items(): + if values['assert'] == True: + self.logger.info("OUTPUT\n %s", values['result']) + else if values['assert'] == False: + self.logger.info("ERROR\n %s", values['result']) def run_tests(self): ''' From e7b019821f7598e894bcbff2d348ecc088055c99 Mon Sep 17 00:00:00 2001 From: vagrant Date: Tue, 11 Aug 2015 18:40:54 +0000 Subject: [PATCH 17/43] fixed two issues of subprocess handling and dictionary io --- rftest/tests/test_RF.py | 86 +++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 50 deletions(-) diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index b38c6cf4..75a92dbd 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -3,12 +3,13 @@ import argparse #import unittest import os -import pytest +#import pytest import logging import subprocess import json import fnmatch +from subprocess import Popen, PIPE #from utils import DumpToFile #logging.basicConfig( @@ -58,7 +59,6 @@ def setTestsToRun(self, *kargs, **kwargs): for key,tests in kwargs.items(): self.testsToRun[key] = tests - def configureTests(self, **kwargs): ''' fill self.testsParams with arguments @@ -69,7 +69,6 @@ def configureTests(self, **kwargs): if key in self.testsParams.keys(): self.testsParams[key] = param - #def setTestsOutput(self, *args, **kwargs): def setTestsOutputFormat(self, **kwargs): ''' check if tests results must be saved @@ -183,37 +182,34 @@ def runTests(self): self.setUpTests[test].run_tests() -class Dictlist(dict): - ''' - class to accept dictionary of lists. - Required to handle the outputDictionary in RFUnitTests. - The commands that are to be executed are not unique within testcases. - Hence this class is required. - ''' - def __setitem__(self, key, value): - try: - self[key] - except KeyError: - super(Dictlist, self).__setitem__(key, []) - self[key].append(value) +#class Dictlist(dict): +# ''' +# class to accept dictionary of lists. +# Required to handle the outputDictionary in RFUnitTests. +# The commands that are to be executed are not unique within testcases. +# Hence this class is required. +# ''' +# def __setitem__(self, key, value): +# try: +# self[key] +# except KeyError: +# super(Dictlist, self).__setitem__(key, []) +# self[key].append(value) class RFUnitTests(object): def __init__(self, logger): self.logger = logger - self.evaluateDictionary = Dictlist() - self.verifyDictionary = Dictlist() - #self.dictlist = Dictlist() - + self.evaluateDictionary = {} + self.verifyDictionary = {} - def evaluate(self, capfd): + def evaluate(self): ''' for each process in self.tests run it using capfd This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' self.tests - #Extract the list which is value of another list. save it as value. # I think it is not required to return the dictionary since the object for each class is instantiated seperately. #Everytime a new object calls the evaluate function, it will be followed by verify and analyse. #Each time a new object calls the evaluate, a the dictionary will be cleared. @@ -221,10 +217,11 @@ def evaluate(self, capfd): if self.evaluateDictionary: self.evaluateDictionary.clear() for key,value in tests.items(): - out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. + #out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. + sp = subprocess.popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) #Extract the values from value above and save it to our executuindict. + out,err = sp.communicate() self.evaluateDictionary[key] = {'out' : str(out), - 'err' : str(err), } # since we are only executing command, err will always be empty. - + 'err' : str(err), } def verify(self): ''' @@ -240,27 +237,21 @@ def verify(self): No need to write assertion Verify if the command is a list or not. define behaviour accordingly. + + sample evaluateDictionary. + {'ovs-vsctl show | grep dp0': {'err': 'krishna sai klfhahsd dp01', + 'out': 'sai krishnaalskdfkaskj dp0'},} ''' - #sample evaluateDictionary. - #{'ovs-vsctl show | grep dp0': [{'err': 'krishna sai klfhahsd dp01', - # 'out': 'sai krishnaalskdfkaskj dp0'}], - # 'test': [1]} - - #In [48]: evaluateDictionary['ovs-vsctl show | grep dp0'][0].keys()[0] - #Out[48]: 'err' - #key of self.evaluateDictionary is always equal to key of self.tests dictionary. This is how data structure is built.Hence tests[inputs] will work. - for inputs,valuelist in self.evaluateDictionary.items(): - for vlist in valuelist: - if type(vlist) is dict: - for outerr,consoleMessage in vlist.items(): - #This is for out.find("dp0") Then it is found - if self.tests[inputs]['method'] == 'find' && self.evaluateDictionary[inputs][0].keys()[0] == 'out': #The second 'if' condition is to be handled properly. This will fail. - if str(self.evaluateDictionary[inputs][outerr]).find(self.tests[inputs]['output']) != -1: - verifyDictionary[inputs] = {'assert':True, 'result':str(self.evaluateDictionary[inputs][vlist]['out'])} - else : - verifyDictionary[inputs] = {'assert':False, 'result':str(self.evaluateDictionary[inputs][vlist]['err'])} - + for cmdInput,outErrDict in self.evaluateDictionary.items(): + if outErrDict.values()[0]['out']: + if self.tests[inputs]['method'] == 'find': #The second 'if' condition is to be handled properly. This will fail. + if str(outErrDict.values()[0]['out']).find(self.tests[inputs]['output']) != -1: + verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0]['out'])} + else : + verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['out'])} + if outErrDict.values()[0]['err']: + verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['err'])} def analyse(self): ''' @@ -274,7 +265,7 @@ def analyse(self): for keys,values in verifyDictionary.items(): if values['assert'] == True: self.logger.info("OUTPUT\n %s", values['result']) - else if values['assert'] == False: + elif values['assert'] == False: self.logger.info("ERROR\n %s", values['result']) def run_tests(self): @@ -288,9 +279,6 @@ def run_tests(self): description = 'RFTest suite, to run the tests and determine the state of system' epilog = 'Report bugs to: https://github.com/routeflow/RouteFlow/issues' - #config = os.path.dirname(os.path.realpath(__file__)) + "/config.csv" - #islconf = os.path.dirname(os.path.realpath(__file__)) + "/islconf.csv" - parser = argparse.ArgumentParser(description=description, epilog=epilog) #run pytest(y/n) @@ -327,8 +315,6 @@ def run_tests(self): args = parser.parse_args() testsobj = Tests() - #mydict = {} - #mydict = args.testcases testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) From e008ec0f5990b9e74f748fc2e685a94dad656d67 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Wed, 12 Aug 2015 04:39:29 +0000 Subject: [PATCH 18/43] adding debug messgaes and Connectivity cases --- rftest/tests/test_Connectivity.py | 147 ++++++++++++++++++++++++++++++ rftest/tests/test_Ovs.py | 1 + rftest/tests/test_RF.py | 55 +++++++++-- 3 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 rftest/tests/test_Connectivity.py diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py new file mode 100644 index 00000000..e0bc6020 --- /dev/null +++ b/rftest/tests/test_Connectivity.py @@ -0,0 +1,147 @@ + +class Connectivity(RFUnitTests(): + + TESTS = {} + + def __init__(self, containerNames): + self.containerRoutes = {} + self.containerInterfaces = {} + self.containerNames = containerNames #string to be defined by user (e.g., in rftest2 is rfvmA, B, ... + self.tests = {} + + def getContainerRoutes(self, name): + cmd = "lxc-ps -n " + name + "route -n" + + return subprocess.call(cmd) + + def parseContainerRoutes(self, name): + ''' + for container name parse the result of getContainerRoutes + and put it as list of strings (one string for each route) inside dict self.containerRoutes[name] + route format is the destination ip address + + sudo lxc-console -n b2 + ubuntu/ubuntu + + ubuntu@b2:~$ route -n + Kernel IP routing table + Destination Gateway Genmask Flags Metric Ref Use Iface + 0.0.0.0 172.31.2.1 0.0.0.0 UG 100 0 0 eth0 + 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 + + ''' + pass + + + def getContainerInterfaces(self, name): + cmd = "lxc-ps -n " + name + "ifconfig" + + return subprocess.call(cmd) + + def parseContainerInterfaces(self, name): + ''' + parse the result of getContainerInterfaces storing in self.containerInterfaces + the result of parse (list of strings) containing all interfaces ip address + remember to filter 127.0.0.1 interface + + ubuntu@b2:~$ ifconfig + eth0 Link encap:Ethernet HWaddr 02:b2:b2:b2:b2:b2 + inet addr:172.31.2.2 Bcast:172.31.2.255 Mask:255.255.255.0 + inet6 addr: fe80::b2:b2ff:feb2:b2b2/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:14 errors:0 dropped:0 overruns:0 frame:0 + TX packets:20 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1212 (1.2 KB) TX bytes:1296 (1.2 KB) + + lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:2 errors:0 dropped:0 overruns:0 frame:0 + TX packets:2 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:176 (176.0 B) TX bytes:176 (176.0 B) + ''' + pass + + def addTest(self, cmd, method, output, **kwargs): + ''' + add in self.tests new tests following the TEST structure + ''' + self.tests[str(cmd)] = {'method':str(method), + 'output':str(output), kwargs} + + + def addTestsDefault(self): + ''' + Break point 1. + calls getcontainerInterfaces and parseContainerInterfaces for every container in self.containerNames + calls getContainerRoutes and parseContainerRoutes for every container in self.containerNames + for containerName in self.containerNames: + self.containerRoutes[containerName] = parseContainerRoutes(getContainerRoutes(contanername)) + + for containerName in self.containerNames: + cmd = "lxc-ps -n {name} ping -c3 {target}" + cmd.format(name=containerName, target=containerNameInterface) + + + with the results of the calls above we need to addTest for every pair of containers + the idea is that for each container it is going to ping every interfaces of all the other containers + + example: + rfvmA ping rfvmB interface 172.31.2.1 then we will have the command + + cmd = "lxc-ps -n {name} ping -c3 {target}" + cmd.format(name=rfvmA, target=rfvmBInterface) + + method is going to be a find of the % of packet loss, if it is less than 100% then + we consider True otherwise false + + expected output=True + + method = 'customFind' + methodFunc = findFunction + + then call addTest(cmd, method, output, name=rfvmA, target="rfvmB,rfvmBInterface", methodFunc = findFunction) + + I think we will need to create/customize a find function to parse the result of the ping command + + I put kwargs in addTest to insert the containers name and target so it can be logged + in the analyse function + ''' + def findFunction(self): + ''' + find packet loss percentage in ping result + and set true or false if is 100% less or not + ''' + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + self.addTestsDefault() + self.logger.getlogger("Test_connectivity") + self.logger.INFO("Test connectivity class Begin") + self.evaluate() + self.verify() + self.analyse() + + + + +class Topology(Connectivity): + + + TESTS = {} + + def __init__(self, containerNames): + super(Topology, self).__init__(containerNames) + + def addTestsDefault(): + ''' + cmd instead of ping, traceroute and iperf + ''' diff --git a/rftest/tests/test_Ovs.py b/rftest/tests/test_Ovs.py index 22555cfc..234f181a 100644 --- a/rftest/tests/test_Ovs.py +++ b/rftest/tests/test_Ovs.py @@ -40,6 +40,7 @@ def run_tests(self): self.verify self.analyse ''' + print "sai OVS runtests" self.addTestsDefault() self.addTest("ps aux | grep dp0","find","ovsdb-server") self.addTest("ps aux | grep dp0","find","ovs-vswitchd") diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 75a92dbd..b6e438be 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -21,7 +21,7 @@ class Tests: CATALOGUE = { - 'test_OVS':'OVS', + 'test_Ovs.py':'OVS', 'test_Mongo':'Mongo', 'test_Containers':'Containers', 'test_RFApps':['RFserver','RFproxy','RFclient'] @@ -35,7 +35,7 @@ class Tests: } def __init__(self): - self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} + self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':False} self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} @@ -43,15 +43,18 @@ def __init__(self): self.setUpTests = {} self.logger = None - def setTestsToRun(self, *kargs, **kwargs): + #def setTestsToRun(self, *kargs, **kwargs): + def setTestsToRun(self, **kwargs): ''' fill self.testsToRun with proper arguments like lxc, ovs, rfserver, rfproxy,.... true or false define if tests will use pytest or not (self.use_pytest = False) ''' - if 'pytest' in kwargs.keys(): - self.use_pytest = kwargs['pytest'] + print "saikrishna setTestsToRun" + print "%s"%( kwargs) + #if 'pytest' in kwargs.keys(): + # self.use_pytest = kwargs['pytest'] #If kwargs is empty leave the testsToRun dictionary with default arguments. if kwargs: @@ -63,6 +66,7 @@ def configureTests(self, **kwargs): ''' fill self.testsParams with arguments ''' + print "saikrishna configureTests" if kwargs: self.testsParams.clear() for key,param in kwargs.items(): @@ -76,6 +80,7 @@ def setTestsOutputFormat(self, **kwargs): in json or raw formats (first let`s save in raw format) ''' + print "saikrishna setTestsOutputFormat" self.outputFormat.clear() if kwargs: for key,formats in kwargs.items(): @@ -87,6 +92,7 @@ def setTestsOutputModes(self, **kwargs): in json or raw formats (first let`s save in raw format) ''' + print "saikrishna setTestsOutputModes" self.outputModes.clear() if kwargs: for key,modes in kwargs.items(): @@ -115,6 +121,7 @@ def setup(self,output_dir): :output_dir: directory to store logs ''' + print "saikrishna setup" self.logger = logging.getLogger() self.logger.setLevel(logging.DEBUG) @@ -141,15 +148,29 @@ def findTests(self): following self.testParams this dict will be used to be used in runTests function ''' + #TODO: remove the .py extension. else tests will not be found., Instead change the keys in CATELOGUE with .py extension. + #print matches #['test_Ovs.py', 'test_Containers.py', 'test_RFApps.py', 'test_RF.py', 'test_Connectivity.py', 'test_Mongo.py', 'test_RF.pyc'] + #print "creating test class object matches", obj #prints (0, 'test_Ovs.py') + #print self.testsToRun.keys() #prints ['ovs'] + #print Tests.CATALOGUE.keys() #prints ['test_Containers', 'test_Mongo', 'test_RFApps', 'test_Ovs.py'] + #if obj in Tests.CATALOGUE: + # print "success1" + #if Tests.CATALOGUE[obj] in self.testsToRun.keys(): + # print "success2" + print "saikrishna findTests" path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests + print "saikrishna findTests %s"% (path) matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, 'test_*'): - matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. + matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. for obj in enumerate(matches): + #print "sai krishna matches %s"% (Tests.CATALOGUE.keys()[1]) if obj in Tests.CATALOGUE.keys() and Tests.CATALOGUE[obj] in self.testsToRun.keys(): + #if obj in Tests.CATALOGUE.keys() and self.testsToRun.keys() in Tests.CATALOGUE.keys()[1]: if self.testsToRun[obj] == True: if type(Tests.CATALOGUE[obj]) is list: + print "creating test class object if" for classes_ in Tests.CATALOGUE[obj]: #For each file, instantiate an object for the classes in it. #for classes_ in Tests.CATALOGUE[obj]: module = __import__(obj) @@ -178,6 +199,7 @@ def runTests(self): # if tests == True: # logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. + print "saikrishna runTests" for test in self.setUpTests.keys(): self.setUpTests[test].run_tests() @@ -209,6 +231,7 @@ def evaluate(self): This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' + print "saikrishna RFUnitTests evaluate" self.tests # I think it is not required to return the dictionary since the object for each class is instantiated seperately. #Everytime a new object calls the evaluate function, it will be followed by verify and analyse. @@ -242,14 +265,16 @@ def verify(self): {'ovs-vsctl show | grep dp0': {'err': 'krishna sai klfhahsd dp01', 'out': 'sai krishnaalskdfkaskj dp0'},} ''' + print "saikrishna RFUnitTests verify" #key of self.evaluateDictionary is always equal to key of self.tests dictionary. This is how data structure is built.Hence tests[inputs] will work. for cmdInput,outErrDict in self.evaluateDictionary.items(): if outErrDict.values()[0]['out']: - if self.tests[inputs]['method'] == 'find': #The second 'if' condition is to be handled properly. This will fail. + if self.tests[inputs]['method'] == 'find': if str(outErrDict.values()[0]['out']).find(self.tests[inputs]['output']) != -1: verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0]['out'])} else : verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['out'])} + #if self.tests[inputs]['method'] == 'findfp': #This is a part of the function passed as argument from test_Connectivity file. if outErrDict.values()[0]['err']: verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['err'])} @@ -262,6 +287,7 @@ def analyse(self): self.logger.info("ERROR\n %s", err) No need to write assertion ''' + print "saikrishna RFUnitTests analyse" for keys,values in verifyDictionary.items(): if values['assert'] == True: self.logger.info("OUTPUT\n %s", values['result']) @@ -315,9 +341,18 @@ def run_tests(self): args = parser.parse_args() testsobj = Tests() - testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. + #testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. + #testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} + #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) + kwargs = {'ovs':True} + args = ("true",1) + #testsobj.setTestsToRun(*args,**kwargs) #args.testcases will be a dictionary that is passed. + testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. + #testsobj.setTestsToRun() #args.testcases will be a dictionary that is passed. + #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) + testsobj.setTestsOutputFormat() testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} - testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - testsobj.findTests() + testsobj.setup(os.path.dirname(os.path.realpath(__file__))) + testsobj.findTests() testsobj.runTests() From 9391b50ca32979b9b6b03adcbe11e3b0c5731d90 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Wed, 12 Aug 2015 11:53:28 +0000 Subject: [PATCH 19/43] Connectivity --- rftest/tests/test_Connectivity.py | 11 ++++++++--- rftest/tests/test_RF.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index e0bc6020..b191409c 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -11,8 +11,11 @@ def __init__(self, containerNames): def getContainerRoutes(self, name): cmd = "lxc-ps -n " + name + "route -n" - - return subprocess.call(cmd) + sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + out,err = sp.communicate() + return out,err + #return sp.stdout This is a file pointer. No use in using this method + #return subprocess.call(cmd) def parseContainerRoutes(self, name): ''' @@ -30,7 +33,9 @@ def parseContainerRoutes(self, name): 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 ''' - pass + out = getContainerRoutes(name) + for line in out: + print line #handle storing of only routes in the self.containerRoutes dictionary. def getContainerInterfaces(self, name): diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index b6e438be..490453bc 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -241,7 +241,7 @@ def evaluate(self): self.evaluateDictionary.clear() for key,value in tests.items(): #out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. - sp = subprocess.popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) #Extract the values from value above and save it to our executuindict. + sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) #Extract the values from value above and save it to our executuindict. out,err = sp.communicate() self.evaluateDictionary[key] = {'out' : str(out), 'err' : str(err), } From ddfa68b1e0d136729fa322b0edc111af7c66aab7 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Sun, 16 Aug 2015 02:14:06 +0000 Subject: [PATCH 20/43] minor changes --- rftest/tests/test_Connectivity.py | 74 ++++---- rftest/tests/test_RF.py | 8 +- rftest/tests/test_RF.py.bkp | 278 ++++++++++++++++++++++++++++++ rftest/tests/test_RFApps.py | 17 +- rftest/tests/tt/test_a.py | 0 rftest/tests/tt/test_b.py | 0 6 files changed, 325 insertions(+), 52 deletions(-) create mode 100644 rftest/tests/test_RF.py.bkp create mode 100644 rftest/tests/tt/test_a.py create mode 100644 rftest/tests/tt/test_b.py diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index b191409c..51f00122 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -1,14 +1,14 @@ class Connectivity(RFUnitTests(): - + TESTS = {} - + def __init__(self, containerNames): self.containerRoutes = {} self.containerInterfaces = {} self.containerNames = containerNames #string to be defined by user (e.g., in rftest2 is rfvmA, B, ... self.tests = {} - + def getContainerRoutes(self, name): cmd = "lxc-ps -n " + name + "route -n" sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) @@ -16,7 +16,7 @@ def getContainerRoutes(self, name): return out,err #return sp.stdout This is a file pointer. No use in using this method #return subprocess.call(cmd) - + def parseContainerRoutes(self, name): ''' for container name parse the result of getContainerRoutes @@ -31,18 +31,18 @@ def parseContainerRoutes(self, name): Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.31.2.1 0.0.0.0 UG 100 0 0 eth0 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 - + ''' out = getContainerRoutes(name) for line in out: print line #handle storing of only routes in the self.containerRoutes dictionary. - + def getContainerInterfaces(self, name): cmd = "lxc-ps -n " + name + "ifconfig" - + return subprocess.call(cmd) - + def parseContainerInterfaces(self, name): ''' parse the result of getContainerInterfaces storing in self.containerInterfaces @@ -50,26 +50,26 @@ def parseContainerInterfaces(self, name): remember to filter 127.0.0.1 interface ubuntu@b2:~$ ifconfig - eth0 Link encap:Ethernet HWaddr 02:b2:b2:b2:b2:b2 + eth0 Link encap:Ethernet HWaddr 02:b2:b2:b2:b2:b2 inet addr:172.31.2.2 Bcast:172.31.2.255 Mask:255.255.255.0 inet6 addr: fe80::b2:b2ff:feb2:b2b2/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:14 errors:0 dropped:0 overruns:0 frame:0 TX packets:20 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 + collisions:0 txqueuelen:1000 RX bytes:1212 (1.2 KB) TX bytes:1296 (1.2 KB) - - lo Link encap:Local Loopback + + lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:2 errors:0 dropped:0 overruns:0 frame:0 TX packets:2 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:0 + collisions:0 txqueuelen:0 RX bytes:176 (176.0 B) TX bytes:176 (176.0 B) ''' pass - + def addTest(self, cmd, method, output, **kwargs): ''' add in self.tests new tests following the TEST structure @@ -77,7 +77,7 @@ def addTest(self, cmd, method, output, **kwargs): self.tests[str(cmd)] = {'method':str(method), 'output':str(output), kwargs} - + def addTestsDefault(self): ''' Break point 1. @@ -85,34 +85,34 @@ def addTestsDefault(self): calls getContainerRoutes and parseContainerRoutes for every container in self.containerNames for containerName in self.containerNames: self.containerRoutes[containerName] = parseContainerRoutes(getContainerRoutes(contanername)) - + for containerName in self.containerNames: cmd = "lxc-ps -n {name} ping -c3 {target}" cmd.format(name=containerName, target=containerNameInterface) - - + + with the results of the calls above we need to addTest for every pair of containers the idea is that for each container it is going to ping every interfaces of all the other containers - - example: + + example: rfvmA ping rfvmB interface 172.31.2.1 then we will have the command - + cmd = "lxc-ps -n {name} ping -c3 {target}" cmd.format(name=rfvmA, target=rfvmBInterface) - - method is going to be a find of the % of packet loss, if it is less than 100% then + + method is going to be a find of the % of packet loss, if it is less than 100% then we consider True otherwise false - + expected output=True - + method = 'customFind' methodFunc = findFunction - + then call addTest(cmd, method, output, name=rfvmA, target="rfvmB,rfvmBInterface", methodFunc = findFunction) - + I think we will need to create/customize a find function to parse the result of the ping command - - I put kwargs in addTest to insert the containers name and target so it can be logged + + I put kwargs in addTest to insert the containers name and target so it can be logged in the analyse function ''' def findFunction(self): @@ -120,7 +120,7 @@ def findFunction(self): find packet loss percentage in ping result and set true or false if is 100% less or not ''' - + def run_tests(self): ''' basically runs methods inherited with self.tests attribute @@ -134,18 +134,18 @@ def run_tests(self): self.evaluate() self.verify() self.analyse() - - - + + + class Topology(Connectivity): - - + + TESTS = {} - + def __init__(self, containerNames): super(Topology, self).__init__(containerNames) - + def addTestsDefault(): ''' cmd instead of ping, traceroute and iperf diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 490453bc..852a4e9a 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -163,7 +163,7 @@ def findTests(self): matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, 'test_*'): - matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. + matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. for obj in enumerate(matches): #print "sai krishna matches %s"% (Tests.CATALOGUE.keys()[1]) if obj in Tests.CATALOGUE.keys() and Tests.CATALOGUE[obj] in self.testsToRun.keys(): @@ -239,7 +239,7 @@ def evaluate(self): #This approach is fine as long as we follow the non-threading approach. are we planning to use threads?? not in near future. if self.evaluateDictionary: self.evaluateDictionary.clear() - for key,value in tests.items(): + for key,value in self.tests.items(): #out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) #Extract the values from value above and save it to our executuindict. out,err = sp.communicate() @@ -260,7 +260,7 @@ def verify(self): No need to write assertion Verify if the command is a list or not. define behaviour accordingly. - + sample evaluateDictionary. {'ovs-vsctl show | grep dp0': {'err': 'krishna sai klfhahsd dp01', 'out': 'sai krishnaalskdfkaskj dp0'},} @@ -269,7 +269,7 @@ def verify(self): #key of self.evaluateDictionary is always equal to key of self.tests dictionary. This is how data structure is built.Hence tests[inputs] will work. for cmdInput,outErrDict in self.evaluateDictionary.items(): if outErrDict.values()[0]['out']: - if self.tests[inputs]['method'] == 'find': + if self.tests[inputs]['method'] == 'find': if str(outErrDict.values()[0]['out']).find(self.tests[inputs]['output']) != -1: verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0]['out'])} else : diff --git a/rftest/tests/test_RF.py.bkp b/rftest/tests/test_RF.py.bkp new file mode 100644 index 00000000..63279cfe --- /dev/null +++ b/rftest/tests/test_RF.py.bkp @@ -0,0 +1,278 @@ +import sys +import time +import argparse +#import unittest +import os +import pytest +import logging +import subprocess +import json +import fnmatch + +#from utils import DumpToFile + +#logging.basicConfig( +# filename = 'RFtest.log', +# level=logging.INFO, +# format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', +# datefmt='%b %d %H:%M:%S' +# ) + +class Tests: + CATALOGUE = { + 'ovs':'OVS', + 'containers':'Containers', + 'rfapps':'RFApps', + } + LOGLEVEL = { + 10 : 'DEBUG', + 20 : 'INFO', + 30 : 'WARNING', + 40 : 'ERROR', + 50 : 'CRITICAL', + } + + def __init__(self): + self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} + self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} + + #This can be a single dictionary. txt terminal json and raw. + self.outputFormat = {'txt':False, 'terminal':False} + self.outputModes = {'json':False, 'raw':False} + self.use_pytest = False + + + #def setTestsToRun(self, *args, **kwargs): + def setTestsToRun(self, isPytest,**kwargs): + ''' + fill self.testsToRun with proper arguments + like lxc, ovs, rfserver, rfproxy,.... true or false + + define if tests will use pytest or not (self.use_pytest = False) + ''' + self.use_pytest = isPytest + + #If kwargs is empty leave the testsToRun dictionary with default arguments. + if kwargs: + self.testsToRun.clear() + for key,tests in kwargs.items(): + self.testsToRun[key] = tests + + + def configureTests(self, **kwargs): + ''' + fill self.testsParams with arguments + ''' + if kwargs: + self.testsParams.clear() + for key,param in kwargs.items(): + self.testsParams[key] = param + + #def setTestsOutput(self, *args, **kwargs): + def setTestsOutputFormat(self, **kwargs): + ''' + check if tests results must be saved + in txt file or sent to terminal + in json or raw formats + (first let`s save in raw format) + ''' + self.outputFormat.clear() + if kwargs: + for key,formats in kwargs.items(): + self.outputFormat[key] = formats + + def setTestsOutputModes(self, **kwargs): + ''' + check if tests results must be saved + in txt file or sent to terminal + in json or raw formats + (first let`s save in raw format) + ''' + self.outputModes.clear() + if kwargs: + for key,modes in kwargs.items(): + self.outputModes[key] = modes + del self.outputModes['raw'] + self.outputModes['raw'] = True + + def setup(self,name,output_dir): + ''' + takes self.testsToRun, self.outputFormat, self.outputModes + and prepare environment to runtests + ex: creates loggers, open files/terminal outputs. + + for loggers: define a self.logger and instatiate it to save in file or terminal + In this case let`s not use open text files, instead let`s create a logger for each Test class + according to self.testsToRun and outputFormat. you can create a function for that too + And create a dict like CATALOGUE that will contain testsToRun:logger + this dict will be used to call RF_tests + example: https://aykutakin.wordpress.com/2013/08/06/logging-to-console-and-file-in-python/ + + for testsToRun create a function that reads self.testsToRun and do the following: + - look for test_ files in dir and subdir (ex: test_ovs) and load it importing + its class (ex from test_ovs import OVS - class name associated with ovs in CATALOGUE) + - returns a dict (ex: self.setupTests) with teststorun:classes objects + following self.testParams + this dict will be used to be used in runTests function + + :name: used to print the class name in the get logger. + :output_dir: directory to store logs + ''' + + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + + #INFO handler + handler = logging.FileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true") + handler.setLevel(logging.INFO) + formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + #ERROR handler + handler = logging.FileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true") + handler.setLevel(logging.ERROR) + formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + return logger + + def findTests(self): + ''' + for testsToRun create a function that reads self.testsToRun and do the following: + - look for test_ files in dir and subdir (ex: test_ovs) and load it importing + its class (ex from test_ovs import OVS - class name associated with ovs in CATALOGUE) + - returns a dict (ex: self.setupTests) with teststorun:classes objects + following self.testParams + this dict will be used to be used in runTests function + ''' + path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests + matches = [] + for root, dirnames, filenames in os.walk(path): + for filename in fnmatch.filter(filenames, 'test_*'): + matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. + for obj in enumerate(matches): + if obj in self.testsToRun.keys(): + if self.testsToRun[obj] == True: + #create an object to the class and run the tests. + pass + + def runTests(self): + ''' + Two approaches here depending on self.use_pytest = True or False: + - first call pytest.main() https://pytest.org/latest/usage.html + ex: pytest.main("-qq", plugins=[MyPlugin()]) + I will explain you how to run using pytest! ASK ME + + Second run the tests by ourselves. How? + Using dict returned by setup (ex: self.setupTests) + call run_tests for each object + ''' + # call setup with the name argument. This name will be used by logger. + # The name is an itervalue from self.testsToRun dictionary. Pass each key as an argument, if the corresponding value is true. + self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. + for key,tests in self.testsToRun.items(): + if tests == True: + logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. + + +class RFUnitTests(object): + + def __init__(self, logger): + self.logger = logger + self.tests = {} + + def evaluate(self, capfd): + ''' + for each process in self.tests run it using capfd + This function only execute the commands, get the output/err, + return them as {command: {'out':output,'err':err} ) + ''' + #logger.info('TestOVS Network ovs-vsctl') + #subprocess.call('ovs-vsctl show | grep dp0', shell = True) + pass + + def verify(self, tests_out): + ''' + receives dict {command: {'out':output,'err':err} ) + run comparisons of responses (output/err) and + expected_result of TESTS using command as key + and looking for the method and expected_output + if using method expected_output == output + associate dict {command: {'assert':True, 'result':output}} + else + associate dict {command: {'assert':False, 'result':err}} + returns dict with commands:assertions,results + No need to write assertion + ''' + pass + + def analyse(self, verify_out): + ''' + receives dict of verify_processes + check assertion values and write results to logger + example: + self.logger.info("OUTPUT\n %s", out) + self.logger.info("ERROR\n %s", err) + No need to write assertion + ''' + pass + + + def run_tests(): + ''' + just pass, the classes inheriting RFUnitTests will + overwrite this function + ''' + pass + +if __name__ == '__main__': + description = 'RFTest suite, to run the tests and determine the state of system' + epilog = 'Report bugs to: https://github.com/routeflow/RouteFlow/issues' + + #config = os.path.dirname(os.path.realpath(__file__)) + "/config.csv" + #islconf = os.path.dirname(os.path.realpath(__file__)) + "/islconf.csv" + + parser = argparse.ArgumentParser(description=description, epilog=epilog) + + #run pytest(y/n) + parser.add_argument('-p', '--pytest', default=False, type = bool, + help='Run tests with pytest(True/False)') + + #accept a list of test modules to be run. + #parser.add_argument('-tc', '--testcases', choices =['ovs','rfapps','mongo','containers'], + # help='Testcases to be run.choose form the list specified') + + parser.add_argument('-tc', '--testcases', type=json.loads, + help="Testcases to be run.enter on a dict format like :{'ovs':True, 'containers':False, 'rfapps':True}") + + #list of containers : either user gives his own container names(option:l) or chooses from the list(option:ln) + parser.add_argument('-l', '--lxc', nargs='*', + help='Lxc container name, should be given to verify, default not supported, zero or more container names accepted') + parser.add_argument('-ln', '--lxcnames', choices =['rfvm1','b1','b2','rfvmA','rfvmB'], + help='Lxc container name, should be given to verify, default not supported,to be choosen from the list') + + #list of rfapps + parser.add_argument('-rf', '--rfapps', choices = ['rfserver', 'rfproxy', 'rfclient'], + help='list of rfapps to be tested. select from the choice') + + #accept the mongodb port. default 27017 + parser.add_argument('-m', '--mongoport', default=27017, type = int, + help='port number to verify running of mongodb') + + #accept controller ports. defaults specified. + parser.add_argument('-c', '--controllerport1', default=6533, type = int, + help='rfproxy controller port1') + + parser.add_argument('-cc', '--controllerport2', default=6653, type = int, + help='rfproxy controller port2') + + args = parser.parse_args() + testsobj = Tests() + mydict = {} + #mydict = args.testcases + #testsobj.setTestsToRun(args.pytest, **mydict) #args.testcases will be a dictionary that is passed. + testsobj.setTestsToRun(True, args.testcases) #args.testcases will be a dictionary that is passed. + testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps ) + testsobj.runTests() diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index b0e8cf08..2dd4b655 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -41,7 +41,7 @@ def run_tests(self): class RFserver(RFApps): def __init__(self, logger): - super().__init__(logger) + super(RFserver, self).__init__(logger) def run_tests(self): ''' @@ -61,7 +61,8 @@ def run_tests(self): class RFproxy(RFApps): def __init__(self, logger): - super().__init__(logger) + super(RFproxy, self).__init__(logger, port=6653) + self.port = port #ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py" def setTestsParams(self, cmd, param): @@ -89,17 +90,11 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() - output = setTestsOutputs('ryu-manager --use-stderr --ofp-tcp-listen-port=',' ryu-rfproxy/rfproxy.py', 6633) - self.addTest("ps aux | grep rfproxy","find",output) - output = setTestsOutputs('ryu-manager --use-stderr --ofp-tcp-listen-port=',' ryu-rfproxy/rfproxy.py', 6653) + output = self.setTestsOutputs('ryu-manager --use-stderr --ofp-tcp-listen-port=',' ryu-rfproxy/rfproxy.py', self.port) self.addTest("ps aux | grep rfproxy","find",output) - output = setTestsOutputs('127.0.0.1:', '', 6633) - cmd = self.setTestsParams("netstat -plant | grep", 6633) - self.addTest(cmd,"find",output) - - output = setTestsOutputs('127.0.0.1:', '', 6653) - cmd = self.setTestsParams("netstat -plant | grep", 6653) + output = self.setTestsOutputs('127.0.0.1:', '', self.port) + cmd = self.setTestsParams("netstat -plant | grep", self.port) self.addTest(cmd,"find",output) self.logger.getlogger("Test_rfproxy") diff --git a/rftest/tests/tt/test_a.py b/rftest/tests/tt/test_a.py new file mode 100644 index 00000000..e69de29b diff --git a/rftest/tests/tt/test_b.py b/rftest/tests/tt/test_b.py new file mode 100644 index 00000000..e69de29b From 40b01fb431f37df037bcea220a740b92895873f5 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Sun, 16 Aug 2015 02:14:58 +0000 Subject: [PATCH 21/43] removed unwanted files --- rftest/tests/test_RF.py.bkp | 278 ------------------------------------ rftest/tests/tt/test_a.py | 0 rftest/tests/tt/test_b.py | 0 3 files changed, 278 deletions(-) delete mode 100644 rftest/tests/test_RF.py.bkp delete mode 100644 rftest/tests/tt/test_a.py delete mode 100644 rftest/tests/tt/test_b.py diff --git a/rftest/tests/test_RF.py.bkp b/rftest/tests/test_RF.py.bkp deleted file mode 100644 index 63279cfe..00000000 --- a/rftest/tests/test_RF.py.bkp +++ /dev/null @@ -1,278 +0,0 @@ -import sys -import time -import argparse -#import unittest -import os -import pytest -import logging -import subprocess -import json -import fnmatch - -#from utils import DumpToFile - -#logging.basicConfig( -# filename = 'RFtest.log', -# level=logging.INFO, -# format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', -# datefmt='%b %d %H:%M:%S' -# ) - -class Tests: - CATALOGUE = { - 'ovs':'OVS', - 'containers':'Containers', - 'rfapps':'RFApps', - } - LOGLEVEL = { - 10 : 'DEBUG', - 20 : 'INFO', - 30 : 'WARNING', - 40 : 'ERROR', - 50 : 'CRITICAL', - } - - def __init__(self): - self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':True} - self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} - - #This can be a single dictionary. txt terminal json and raw. - self.outputFormat = {'txt':False, 'terminal':False} - self.outputModes = {'json':False, 'raw':False} - self.use_pytest = False - - - #def setTestsToRun(self, *args, **kwargs): - def setTestsToRun(self, isPytest,**kwargs): - ''' - fill self.testsToRun with proper arguments - like lxc, ovs, rfserver, rfproxy,.... true or false - - define if tests will use pytest or not (self.use_pytest = False) - ''' - self.use_pytest = isPytest - - #If kwargs is empty leave the testsToRun dictionary with default arguments. - if kwargs: - self.testsToRun.clear() - for key,tests in kwargs.items(): - self.testsToRun[key] = tests - - - def configureTests(self, **kwargs): - ''' - fill self.testsParams with arguments - ''' - if kwargs: - self.testsParams.clear() - for key,param in kwargs.items(): - self.testsParams[key] = param - - #def setTestsOutput(self, *args, **kwargs): - def setTestsOutputFormat(self, **kwargs): - ''' - check if tests results must be saved - in txt file or sent to terminal - in json or raw formats - (first let`s save in raw format) - ''' - self.outputFormat.clear() - if kwargs: - for key,formats in kwargs.items(): - self.outputFormat[key] = formats - - def setTestsOutputModes(self, **kwargs): - ''' - check if tests results must be saved - in txt file or sent to terminal - in json or raw formats - (first let`s save in raw format) - ''' - self.outputModes.clear() - if kwargs: - for key,modes in kwargs.items(): - self.outputModes[key] = modes - del self.outputModes['raw'] - self.outputModes['raw'] = True - - def setup(self,name,output_dir): - ''' - takes self.testsToRun, self.outputFormat, self.outputModes - and prepare environment to runtests - ex: creates loggers, open files/terminal outputs. - - for loggers: define a self.logger and instatiate it to save in file or terminal - In this case let`s not use open text files, instead let`s create a logger for each Test class - according to self.testsToRun and outputFormat. you can create a function for that too - And create a dict like CATALOGUE that will contain testsToRun:logger - this dict will be used to call RF_tests - example: https://aykutakin.wordpress.com/2013/08/06/logging-to-console-and-file-in-python/ - - for testsToRun create a function that reads self.testsToRun and do the following: - - look for test_ files in dir and subdir (ex: test_ovs) and load it importing - its class (ex from test_ovs import OVS - class name associated with ovs in CATALOGUE) - - returns a dict (ex: self.setupTests) with teststorun:classes objects - following self.testParams - this dict will be used to be used in runTests function - - :name: used to print the class name in the get logger. - :output_dir: directory to store logs - ''' - - logger = logging.getLogger(name) - logger.setLevel(logging.DEBUG) - - #INFO handler - handler = logging.FileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true") - handler.setLevel(logging.INFO) - formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") - handler.setFormatter(formatter) - logger.addHandler(handler) - - #ERROR handler - handler = logging.FileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true") - handler.setLevel(logging.ERROR) - formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") - handler.setFormatter(formatter) - logger.addHandler(handler) - - return logger - - def findTests(self): - ''' - for testsToRun create a function that reads self.testsToRun and do the following: - - look for test_ files in dir and subdir (ex: test_ovs) and load it importing - its class (ex from test_ovs import OVS - class name associated with ovs in CATALOGUE) - - returns a dict (ex: self.setupTests) with teststorun:classes objects - following self.testParams - this dict will be used to be used in runTests function - ''' - path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests - matches = [] - for root, dirnames, filenames in os.walk(path): - for filename in fnmatch.filter(filenames, 'test_*'): - matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. - for obj in enumerate(matches): - if obj in self.testsToRun.keys(): - if self.testsToRun[obj] == True: - #create an object to the class and run the tests. - pass - - def runTests(self): - ''' - Two approaches here depending on self.use_pytest = True or False: - - first call pytest.main() https://pytest.org/latest/usage.html - ex: pytest.main("-qq", plugins=[MyPlugin()]) - I will explain you how to run using pytest! ASK ME - - Second run the tests by ourselves. How? - Using dict returned by setup (ex: self.setupTests) - call run_tests for each object - ''' - # call setup with the name argument. This name will be used by logger. - # The name is an itervalue from self.testsToRun dictionary. Pass each key as an argument, if the corresponding value is true. - self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. - for key,tests in self.testsToRun.items(): - if tests == True: - logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. - - -class RFUnitTests(object): - - def __init__(self, logger): - self.logger = logger - self.tests = {} - - def evaluate(self, capfd): - ''' - for each process in self.tests run it using capfd - This function only execute the commands, get the output/err, - return them as {command: {'out':output,'err':err} ) - ''' - #logger.info('TestOVS Network ovs-vsctl') - #subprocess.call('ovs-vsctl show | grep dp0', shell = True) - pass - - def verify(self, tests_out): - ''' - receives dict {command: {'out':output,'err':err} ) - run comparisons of responses (output/err) and - expected_result of TESTS using command as key - and looking for the method and expected_output - if using method expected_output == output - associate dict {command: {'assert':True, 'result':output}} - else - associate dict {command: {'assert':False, 'result':err}} - returns dict with commands:assertions,results - No need to write assertion - ''' - pass - - def analyse(self, verify_out): - ''' - receives dict of verify_processes - check assertion values and write results to logger - example: - self.logger.info("OUTPUT\n %s", out) - self.logger.info("ERROR\n %s", err) - No need to write assertion - ''' - pass - - - def run_tests(): - ''' - just pass, the classes inheriting RFUnitTests will - overwrite this function - ''' - pass - -if __name__ == '__main__': - description = 'RFTest suite, to run the tests and determine the state of system' - epilog = 'Report bugs to: https://github.com/routeflow/RouteFlow/issues' - - #config = os.path.dirname(os.path.realpath(__file__)) + "/config.csv" - #islconf = os.path.dirname(os.path.realpath(__file__)) + "/islconf.csv" - - parser = argparse.ArgumentParser(description=description, epilog=epilog) - - #run pytest(y/n) - parser.add_argument('-p', '--pytest', default=False, type = bool, - help='Run tests with pytest(True/False)') - - #accept a list of test modules to be run. - #parser.add_argument('-tc', '--testcases', choices =['ovs','rfapps','mongo','containers'], - # help='Testcases to be run.choose form the list specified') - - parser.add_argument('-tc', '--testcases', type=json.loads, - help="Testcases to be run.enter on a dict format like :{'ovs':True, 'containers':False, 'rfapps':True}") - - #list of containers : either user gives his own container names(option:l) or chooses from the list(option:ln) - parser.add_argument('-l', '--lxc', nargs='*', - help='Lxc container name, should be given to verify, default not supported, zero or more container names accepted') - parser.add_argument('-ln', '--lxcnames', choices =['rfvm1','b1','b2','rfvmA','rfvmB'], - help='Lxc container name, should be given to verify, default not supported,to be choosen from the list') - - #list of rfapps - parser.add_argument('-rf', '--rfapps', choices = ['rfserver', 'rfproxy', 'rfclient'], - help='list of rfapps to be tested. select from the choice') - - #accept the mongodb port. default 27017 - parser.add_argument('-m', '--mongoport', default=27017, type = int, - help='port number to verify running of mongodb') - - #accept controller ports. defaults specified. - parser.add_argument('-c', '--controllerport1', default=6533, type = int, - help='rfproxy controller port1') - - parser.add_argument('-cc', '--controllerport2', default=6653, type = int, - help='rfproxy controller port2') - - args = parser.parse_args() - testsobj = Tests() - mydict = {} - #mydict = args.testcases - #testsobj.setTestsToRun(args.pytest, **mydict) #args.testcases will be a dictionary that is passed. - testsobj.setTestsToRun(True, args.testcases) #args.testcases will be a dictionary that is passed. - testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps ) - testsobj.runTests() diff --git a/rftest/tests/tt/test_a.py b/rftest/tests/tt/test_a.py deleted file mode 100644 index e69de29b..00000000 diff --git a/rftest/tests/tt/test_b.py b/rftest/tests/tt/test_b.py deleted file mode 100644 index e69de29b..00000000 From 5ca88635b188ea9287973e89c49175e524c5c4c3 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Mon, 17 Aug 2015 12:22:53 +0000 Subject: [PATCH 22/43] resolved findtests issues, logger issues identified --- rftest/tests/{test_Ovs.py => test_OVS.py} | 2 +- rftest/tests/test_RF.py | 60 +++++++++++------------ rftest/tests/test_RFApps.py | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) rename rftest/tests/{test_Ovs.py => test_OVS.py} (97%) diff --git a/rftest/tests/test_Ovs.py b/rftest/tests/test_OVS.py similarity index 97% rename from rftest/tests/test_Ovs.py rename to rftest/tests/test_OVS.py index 234f181a..b3f7b162 100644 --- a/rftest/tests/test_Ovs.py +++ b/rftest/tests/test_OVS.py @@ -12,7 +12,7 @@ class OVS(RFUnitTests): } def __init__(self, logger): - super().__init__(logger) + super(OVS, self).__init__(logger) def addTestsDefault(self): self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 852a4e9a..e116dd20 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -21,7 +21,7 @@ class Tests: CATALOGUE = { - 'test_Ovs.py':'OVS', + 'test_OVS':'OVS', 'test_Mongo':'Mongo', 'test_Containers':'Containers', 'test_RFApps':['RFserver','RFproxy','RFclient'] @@ -35,7 +35,7 @@ class Tests: } def __init__(self): - self.testsToRun = {'ovs':True, 'containers':False, 'rfapps':False} + self.testsToRun = {'OVS':True, 'Containers':False, 'RFApps':False} self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} @@ -139,6 +139,9 @@ def setup(self,output_dir): handler.setFormatter(formatter) self.logger.addHandler(handler) + self.logger.DEBUG("saikrishna") + self.logger.INFO("saikrishna") + def findTests(self): ''' for testsToRun create a function that reads self.testsToRun and do the following: @@ -148,38 +151,35 @@ def findTests(self): following self.testParams this dict will be used to be used in runTests function ''' - #TODO: remove the .py extension. else tests will not be found., Instead change the keys in CATELOGUE with .py extension. - #print matches #['test_Ovs.py', 'test_Containers.py', 'test_RFApps.py', 'test_RF.py', 'test_Connectivity.py', 'test_Mongo.py', 'test_RF.pyc'] - #print "creating test class object matches", obj #prints (0, 'test_Ovs.py') - #print self.testsToRun.keys() #prints ['ovs'] - #print Tests.CATALOGUE.keys() #prints ['test_Containers', 'test_Mongo', 'test_RFApps', 'test_Ovs.py'] - #if obj in Tests.CATALOGUE: - # print "success1" - #if Tests.CATALOGUE[obj] in self.testsToRun.keys(): - # print "success2" print "saikrishna findTests" path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests - print "saikrishna findTests %s"% (path) matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, 'test_*'): - matches.append(os.path.join(filename)) #filename will only return the filename for files even in subdirectory. - for obj in enumerate(matches): - #print "sai krishna matches %s"% (Tests.CATALOGUE.keys()[1]) - if obj in Tests.CATALOGUE.keys() and Tests.CATALOGUE[obj] in self.testsToRun.keys(): - #if obj in Tests.CATALOGUE.keys() and self.testsToRun.keys() in Tests.CATALOGUE.keys()[1]: - if self.testsToRun[obj] == True: - if type(Tests.CATALOGUE[obj]) is list: - print "creating test class object if" - for classes_ in Tests.CATALOGUE[obj]: #For each file, instantiate an object for the classes in it. - #for classes_ in Tests.CATALOGUE[obj]: - module = __import__(obj) - class_ = getattr(module, classes_) - self.setUpTests[obj] = class_(self.logger) - else: - module = __import__(obj) - class_ = getattr(module, Tests.CATALOGUE[obj]) - self.setUpTests[obj] = class_(self.logger) + matches.append(os.path.join(os.path.splitext(filename)[0])) + for testName,toRun in self.testsToRun.items(): + print testName,toRun, Tests.CATALOGUE.keys() + if toRun == True: + print "cond1" + for i in Tests.CATALOGUE.keys(): + if i.find(testName) != -1: + print "working conditions" + for obj in matches: + if obj.find(testName) != -1: + if type(Tests.CATALOGUE[obj]) is list: + print "creating test class object if its a list" + for classes_ in Tests.CATALOGUE[obj]: #For each file, instantiate an object for the classes in it. + module = __import__(obj) + class_ = getattr(module, classes_) + self.setUpTests[obj] = class_(self.logger) + else: + print "creating test class object if its not a list" + module = __import__(obj) + #class_ = getattr(module, Tests.CATALOGUE[obj]) + class_ = getattr(module, "OVS") + print class_ + self.logger.INFO("sai krishna") + self.setUpTests[obj] = class_(self.logger) def runTests(self): ''' @@ -344,7 +344,7 @@ def run_tests(self): #testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. #testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - kwargs = {'ovs':True} + kwargs = {'OVS':True} args = ("true",1) #testsobj.setTestsToRun(*args,**kwargs) #args.testcases will be a dictionary that is passed. testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index 2dd4b655..277e441d 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -9,7 +9,7 @@ class RFApps(RFUnitTests): TESTS = {} def __init__(self, logger): - super().__init__(logger) + super(RFApps, self).__init__(logger) def addTestsDefault(self): self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. From 14e64c062ebdded90b7696fa94ef17afccbe8789 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Tue, 18 Aug 2015 16:56:17 +0000 Subject: [PATCH 23/43] resolved many issues in running --- rftest/tests/Output.log | Bin 0 -> 2520 bytes rftest/tests/RFtest.log | 75 ++++++++++++++++++++++++ rftest/tests/test_Connectivity.py | 21 ++++--- rftest/tests/test_Containers.py | 6 +- rftest/tests/test_Mongo.py | 13 +++-- rftest/tests/test_OVS.py | 4 +- rftest/tests/test_RF.py | 93 +++++++++++++++++++----------- rftest/tests/test_RFApps.py | 20 ++++--- 8 files changed, 173 insertions(+), 59 deletions(-) create mode 100644 rftest/tests/Output.log create mode 100644 rftest/tests/RFtest.log diff --git a/rftest/tests/Output.log b/rftest/tests/Output.log new file mode 100644 index 0000000000000000000000000000000000000000..efda62dc3700ab88c8ac15f3f2838e9444e51ddc GIT binary patch literal 2520 zcmXpqFf`RQu+TL$S1>fTGB&g_GS@LRGgT?M$7rpI&QRn2exehPdd%~ literal 0 HcmV?d00001 diff --git a/rftest/tests/RFtest.log b/rftest/tests/RFtest.log new file mode 100644 index 00000000..7c38d29e --- /dev/null +++ b/rftest/tests/RFtest.log @@ -0,0 +1,75 @@ +Aug 17 12:54:32 root DEBUG saikrishna +Aug 17 12:54:32 root INFO saikrishna +Aug 17 12:54:32 root INFO sai krishna +Aug 17 12:55:08 root DEBUG saikrishna +Aug 17 12:55:08 root INFO saikrishna +Aug 17 12:55:08 root INFO sai krishna +Aug 17 12:55:08 root INFO sai krishna +Aug 17 12:56:03 root DEBUG saikrishna +Aug 17 12:56:03 root INFO saikrishna +Aug 17 12:56:03 root INFO sai krishna +Aug 17 12:56:03 root INFO sai krishna +Aug 17 12:57:05 root DEBUG saikrishna +Aug 17 12:57:05 root INFO saikrishna +Aug 17 12:57:05 root INFO sai krishna +Aug 17 12:57:05 root INFO sai krishna +Aug 17 12:57:31 root DEBUG saikrishna +Aug 17 12:57:31 root INFO saikrishna +Aug 17 12:57:31 root INFO sai krishna +Aug 17 12:57:31 root INFO sai krishna +Aug 17 12:57:31 root INFO Test OVS class Begin +Aug 17 13:04:16 root DEBUG saikrishna +Aug 17 13:04:16 root INFO saikrishna +Aug 17 13:04:16 root INFO sai krishna +Aug 17 13:04:16 root INFO sai krishna +Aug 17 13:04:16 root INFO Test OVS class Begin +Aug 17 13:06:21 root DEBUG saikrishna +Aug 17 13:06:21 root INFO saikrishna +Aug 17 13:06:21 root INFO sai krishna +Aug 17 13:06:21 root INFO sai krishna +Aug 17 13:06:21 root INFO Test OVS class Begin +Aug 17 13:10:51 root DEBUG saikrishna +Aug 17 13:10:51 root INFO saikrishna +Aug 17 13:10:51 root INFO sai krishna +Aug 17 13:10:51 root INFO sai krishna +Aug 17 13:10:51 root INFO Test OVS class Begin +Aug 17 13:12:25 root DEBUG saikrishna +Aug 17 13:12:25 root INFO saikrishna +Aug 17 13:12:25 root INFO sai krishna +Aug 17 13:12:25 root INFO sai krishna +Aug 17 13:12:25 root INFO Test OVS class Begin +Aug 17 13:16:20 root DEBUG saikrishna +Aug 17 13:16:20 root INFO saikrishna +Aug 17 13:16:20 root INFO sai krishna +Aug 17 13:16:20 root INFO sai krishna +Aug 17 13:16:20 root INFO Test OVS class Begin +Aug 17 13:17:56 root DEBUG saikrishna +Aug 17 13:17:56 root INFO saikrishna +Aug 17 13:17:56 root INFO sai krishna +Aug 17 13:17:56 root INFO sai krishna +Aug 17 13:17:56 root INFO Test OVS class Begin +Aug 17 13:18:44 root DEBUG saikrishna +Aug 17 13:18:44 root INFO saikrishna +Aug 17 13:18:44 root INFO sai krishna +Aug 17 13:18:44 root INFO sai krishna +Aug 17 13:18:44 root INFO Test OVS class Begin +Aug 17 13:20:07 root DEBUG saikrishna +Aug 17 13:20:07 root INFO saikrishna +Aug 17 13:20:07 root INFO sai krishna +Aug 17 13:20:07 root INFO sai krishna +Aug 17 13:20:07 root INFO Test OVS class Begin +Aug 17 13:23:19 root DEBUG saikrishna +Aug 17 13:23:19 root INFO saikrishna +Aug 17 13:23:19 root INFO sai krishna +Aug 17 13:23:19 root INFO sai krishna +Aug 17 13:23:19 root INFO Test OVS class Begin +Aug 17 13:24:51 root DEBUG saikrishna +Aug 17 13:24:51 root INFO saikrishna +Aug 17 13:24:51 root INFO sai krishna +Aug 17 13:24:51 root INFO sai krishna +Aug 17 13:24:51 root INFO Test OVS class Begin +Aug 17 13:31:27 root DEBUG saikrishna +Aug 17 13:31:27 root INFO saikrishna +Aug 17 13:31:27 root INFO sai krishna +Aug 17 13:31:27 root INFO sai krishna +Aug 17 13:31:27 root INFO Test OVS class Begin diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index 51f00122..f5d26691 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -1,16 +1,22 @@ +from test_RF import RFUnitTests +import subprocess -class Connectivity(RFUnitTests(): +#class Connectivity(RFUnitTests()): +class Connectivity(): TESTS = {} - def __init__(self, containerNames): + #def __init__(self, containerNames, logger): + def __init__(self,containerNames): + #super(Conncetivity, self).__init__(logger) self.containerRoutes = {} self.containerInterfaces = {} self.containerNames = containerNames #string to be defined by user (e.g., in rftest2 is rfvmA, B, ... self.tests = {} def getContainerRoutes(self, name): - cmd = "lxc-ps -n " + name + "route -n" + #cmd = "lxc-attach -n " + name + " -- /home route -n" + cmd = "route -n" sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = sp.communicate() return out,err @@ -74,8 +80,9 @@ def addTest(self, cmd, method, output, **kwargs): ''' add in self.tests new tests following the TEST structure ''' - self.tests[str(cmd)] = {'method':str(method), - 'output':str(output), kwargs} + #self.tests[str(cmd)] = {'method':str(method), + # 'output':str(output), kwargs} + pass def addTestsDefault(self): @@ -129,8 +136,8 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() - self.logger.getlogger("Test_connectivity") - self.logger.INFO("Test connectivity class Begin") + #self.logger.getlogger("Test_connectivity") + self.logger.info("Test connectivity class Begin") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_Containers.py b/rftest/tests/test_Containers.py index 393acc82..784d137f 100644 --- a/rftest/tests/test_Containers.py +++ b/rftest/tests/test_Containers.py @@ -9,7 +9,7 @@ class Containers(RFUnitTests): TESTS = {} def __init__(self, logger): - super().__init__(logger) + super(Containers, self).__init__(logger) def addTestsDefault(self): self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. @@ -40,8 +40,8 @@ def run_tests(self): self.addTestsDefault() cmd = self.setTestsParams("lxc-info -n","rfvmA") self.addTest(cmd,"find","state: RUNNING") - self.logger.getlogger("Test_containers") - self.logger.INFO("Test containers class Begin") + #self.logger.getlogger("Test_containers") + self.logger.info("Test containers class Begin") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_Mongo.py b/rftest/tests/test_Mongo.py index 958a63be..8503b1c9 100644 --- a/rftest/tests/test_Mongo.py +++ b/rftest/tests/test_Mongo.py @@ -11,7 +11,7 @@ class Mongo(RFUnitTests): } def __init__(self, logger): - super().__init__(logger) + super(Mongo, self).__init__(logger) def addTestsDefault(self): self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. @@ -28,8 +28,13 @@ def setTestsParams(self, cmd, param): Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests + netstat -plant | grep $mongoport ''' - return str(cmd) + str(param) + print cmd + print param + print "fuck you mongo" + #self.TESTS[str(cmd)] = + return str(cmd) + ' ' + str(param) def run_tests(self): ''' @@ -41,8 +46,8 @@ def run_tests(self): self.addTestsDefault() cmd = self.setTestsParams("netstat -plant | grep", 27017) self.addTest(cmd,"find","mongod") - self.logger.getlogger("Test_Mongod") - self.logger.INFO("Test Mongod class Begin") + #self.logger.getlogger("Test_Mongod") + self.logger.info("Test Mongod class Begin") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_OVS.py b/rftest/tests/test_OVS.py index b3f7b162..d8d35f48 100644 --- a/rftest/tests/test_OVS.py +++ b/rftest/tests/test_OVS.py @@ -44,8 +44,8 @@ def run_tests(self): self.addTestsDefault() self.addTest("ps aux | grep dp0","find","ovsdb-server") self.addTest("ps aux | grep dp0","find","ovs-vswitchd") - self.logger.getlogger("Test_OVS") - self.logger.INFO("Test OVS class Begin") + #self.logger = logging.getlogger("Test_OVS") + self.logger.info("Test OVS class Begin") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index e116dd20..b0852590 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -12,19 +12,20 @@ from subprocess import Popen, PIPE #from utils import DumpToFile -#logging.basicConfig( -# filename = 'RFtest.log', -# level=logging.INFO, -# format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', -# datefmt='%b %d %H:%M:%S' -# ) +logging.basicConfig( + filename = 'RFtest.log', + level=logging.INFO, + format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', + datefmt='%b %d %H:%M:%S' + ) class Tests: CATALOGUE = { 'test_OVS':'OVS', 'test_Mongo':'Mongo', 'test_Containers':'Containers', - 'test_RFApps':['RFserver','RFproxy','RFclient'] + 'test_RFApps':['RFServer','RFProxy','RFClient'], + 'test_Connectivity' : 'Connectivity' } LOGLEVEL = { 10 : 'DEBUG', @@ -36,7 +37,7 @@ class Tests: def __init__(self): self.testsToRun = {'OVS':True, 'Containers':False, 'RFApps':False} - self.testsParams = {'mongo':27017, 'containers':['rfvmA','rfvmB'], 'rfapp':['rfproxy','rfserver']} + self.testsParams = {'Mongo':27017, 'Containers':['rfvmA','rfvmB'], 'RFApps':['rfproxy','rfserver']} self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False @@ -70,8 +71,8 @@ def configureTests(self, **kwargs): if kwargs: self.testsParams.clear() for key,param in kwargs.items(): - if key in self.testsParams.keys(): - self.testsParams[key] = param + #if key in self.testsParams.keys(): + self.testsParams[key] = param def setTestsOutputFormat(self, **kwargs): ''' @@ -139,8 +140,8 @@ def setup(self,output_dir): handler.setFormatter(formatter) self.logger.addHandler(handler) - self.logger.DEBUG("saikrishna") - self.logger.INFO("saikrishna") + self.logger.debug("saikrishna") + self.logger.info("saikrishna") def findTests(self): ''' @@ -156,7 +157,8 @@ def findTests(self): matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, 'test_*'): - matches.append(os.path.join(os.path.splitext(filename)[0])) + if os.path.join(os.path.splitext(filename)[1]) == ".py": + matches.append(os.path.join(os.path.splitext(filename)[0])) for testName,toRun in self.testsToRun.items(): print testName,toRun, Tests.CATALOGUE.keys() if toRun == True: @@ -175,11 +177,13 @@ def findTests(self): else: print "creating test class object if its not a list" module = __import__(obj) - #class_ = getattr(module, Tests.CATALOGUE[obj]) - class_ = getattr(module, "OVS") + class_ = getattr(module, Tests.CATALOGUE[obj]) + #class_ = getattr(module, "OVS") print class_ - self.logger.INFO("sai krishna") self.setUpTests[obj] = class_(self.logger) + for confkey,confvalues in self.testsParams.items(): + if testName == confkey: + self.setUpTests[obj].setTestsParams("fill cmd from tests",confvalues) def runTests(self): ''' @@ -233,16 +237,17 @@ def evaluate(self): ''' print "saikrishna RFUnitTests evaluate" self.tests - # I think it is not required to return the dictionary since the object for each class is instantiated seperately. - #Everytime a new object calls the evaluate function, it will be followed by verify and analyse. - #Each time a new object calls the evaluate, a the dictionary will be cleared. #This approach is fine as long as we follow the non-threading approach. are we planning to use threads?? not in near future. if self.evaluateDictionary: self.evaluateDictionary.clear() + #print self.tests.items() for key,value in self.tests.items(): #out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. - sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) #Extract the values from value above and save it to our executuindict. + #Extract the values from value above and save it to our executuindict. + sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = sp.communicate() + #print out,err + #print "saikrishna evaluate out err" self.evaluateDictionary[key] = {'out' : str(out), 'err' : str(err), } @@ -266,17 +271,36 @@ def verify(self): 'out': 'sai krishnaalskdfkaskj dp0'},} ''' print "saikrishna RFUnitTests verify" - #key of self.evaluateDictionary is always equal to key of self.tests dictionary. This is how data structure is built.Hence tests[inputs] will work. + #key of self.evaluateDictionary is always equal to key of self.tests dictionary. + #This is how data structure is built.Hence tests[inputs] will work. + #TypeError : there should be a loop on outErrDict. then the error will go away. + #print self.evaluateDictionary.items() for cmdInput,outErrDict in self.evaluateDictionary.items(): - if outErrDict.values()[0]['out']: - if self.tests[inputs]['method'] == 'find': - if str(outErrDict.values()[0]['out']).find(self.tests[inputs]['output']) != -1: - verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0]['out'])} - else : - verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['out'])} - #if self.tests[inputs]['method'] == 'findfp': #This is a part of the function passed as argument from test_Connectivity file. - if outErrDict.values()[0]['err']: - verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['err'])} + if self.tests[cmdInput]['method'] == 'find': + if str(outErrDict.values()[0]).find(self.tests[cmdInput]['output']) != -1: + self.verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0])} + else : + self.verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0])} + #if self.tests[inputs]['method'] == 'findfp': + #This is a part of the function passed as argument from test_Connectivity file. + if outErrDict.values()[0]: + self.verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0])} + + + #for cmdInput,outErrDict in self.evaluateDictionary.items(): + # print "\n", outErrDict.values() + # print outErrDict.values()[0]['err'] + # if outErrDict.values()[0]['out'] != ' ': + # if self.tests[cmdInput]['method'] == 'find': + # if str(outErrDict.values()[0]['out']).find(self.tests[cmdInput]['output']) != -1: + # self.verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0])} + # verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0]['out'])} + # else : + # verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['out'])} + # #if self.tests[inputs]['method'] == 'findfp': + # #This is a part of the function passed as argument from test_Connectivity file. + # if outErrDict.values()[0]['err']: + # self.verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['err'])} def analyse(self): ''' @@ -288,11 +312,11 @@ def analyse(self): No need to write assertion ''' print "saikrishna RFUnitTests analyse" - for keys,values in verifyDictionary.items(): + for keys,values in self.verifyDictionary.items(): if values['assert'] == True: - self.logger.info("OUTPUT\n %s", values['result']) + self.logger.info("OUTPUT %s", values['result']) elif values['assert'] == False: - self.logger.info("ERROR\n %s", values['result']) + self.logger.error("ERROR %s", values['result']) def run_tests(self): ''' @@ -344,12 +368,13 @@ def run_tests(self): #testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. #testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - kwargs = {'OVS':True} + kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'Connectivity':True} args = ("true",1) #testsobj.setTestsToRun(*args,**kwargs) #args.testcases will be a dictionary that is passed. testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. #testsobj.setTestsToRun() #args.testcases will be a dictionary that is passed. #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) + testsobj.configureTests(mongo = 5056, containers = 'rfvmA', connectivity = 'rfvm1')#, rfapp = args.rfapps) testsobj.setTestsOutputFormat() testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index 277e441d..417af04f 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -38,10 +38,10 @@ def run_tests(self): ''' pass -class RFserver(RFApps): +class RFServer(RFApps): def __init__(self, logger): - super(RFserver, self).__init__(logger) + super(RFServer, self).__init__(logger) def run_tests(self): ''' @@ -52,17 +52,19 @@ def run_tests(self): ''' self.addTestsDefault() self.addTest("ps aux | grep rfserver","find","python ./rfserver/rfserver/py") - self.logger.getlogger("Test_rfserver") - self.logger.INFO("Test rfserver class Begin") + #self.logger.getlogger("Test_rfserver") + self.logger.info("Test rfserver class Begin") self.evaluate() self.verify() self.analyse() -class RFproxy(RFApps): +class RFProxy(RFApps): def __init__(self, logger): - super(RFproxy, self).__init__(logger, port=6653) - self.port = port + super(RFProxy, self).__init__(logger) + self.port = 6653 + #super(RFproxy, self).__init__(logger, port=6653) + #self.port = port #ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py" def setTestsParams(self, cmd, param): @@ -97,8 +99,8 @@ def run_tests(self): cmd = self.setTestsParams("netstat -plant | grep", self.port) self.addTest(cmd,"find",output) - self.logger.getlogger("Test_rfproxy") - self.logger.INFO("Test rfproxy class Begin") + #self.logger.getlogger("Test_rfproxy") + self.logger.info("Test rfproxy class Begin") self.evaluate() self.verify() self.analyse() From 55541ef3dfb082682a519b731aa2099d02743c6e Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 21 Aug 2015 12:12:49 +0000 Subject: [PATCH 24/43] Documentation for running testcases has been added. --- ovs_init.sh | 11 ++++ rftest/tests/README.md | 104 ++++++++++++++++++++++++++++++ rftest/tests/test_Connectivity.py | 37 +++++++++-- 3 files changed, 146 insertions(+), 6 deletions(-) create mode 100755 ovs_init.sh create mode 100644 rftest/tests/README.md diff --git a/ovs_init.sh b/ovs_init.sh new file mode 100755 index 00000000..5de57fe2 --- /dev/null +++ b/ovs_init.sh @@ -0,0 +1,11 @@ +sudo ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \ + --remote=db:Open_vSwitch,Open_vSwitch,manager_options \ + --private-key=db:Open_vSwitch,SSL,private_key \ + --certificate=db:Open_vSwitch,SSL,certificate \ + --log-file=/var/log/openvswitch/ovs-vswitchd.log \ + --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \ + -vsyslog:dbg -vfile:dbg --pidfile --detach +sudo ovs-vsctl --no-wait init +sudo ovs-vswitchd --pidfile --detach \ + --log-file=/var/log/openvswitch/ovs-vswitchd.log \ + -vconsole:err -vsyslog:info -vfile:info diff --git a/rftest/tests/README.md b/rftest/tests/README.md new file mode 100644 index 00000000..0ba6b435 --- /dev/null +++ b/rftest/tests/README.md @@ -0,0 +1,104 @@ +rftest/tests +=========== + +RouteFlow's Unified testing framework supports two functionalities: +1. To run the test and get to know the health of routeflow components. +2. Extend the testcases with ease to tests the new features. + +Dependencies +----------- + +* RouteFlow +* ryu-rfproxy +* 3.5 or 3.8 Generic kernal.(For Connectivity and Topology tests to run) + +Building +-------- + +The testing framework requires RouteFlow and rftest to run. The usual way to install RouteFlow and +all of its dependencies is as follows: + + + Requirements system Ubuntu 12.04 updated and git installed. +```apt-get update & apt-get upgrade & apt-get install git``` + + + Clone RouteFlow + +```git clone https://github.com/raphaelvrosa/RouteFlow``` + + + Build all the required components of RouteFlow + +```cd RouteFlow``` + +```git checkout vandervecken``` + +```./build.sh -c -z -n -i ryu``` + +Running +------- + +RouteFlow usually supplies a script to run all of the components in the +correct order. If you want to run rftests, start rftest1 or rftest2 first followed by +running test cases. Below are the commands for the same. + + + Prior to running the tests, OpenVswitch service should be running. Run the following +script to bring up the service: +``` cd RouteFlow/``` +```./ovs-init.sh``` + + + Run the test, either rftest1 or rftest2: + +```cd rftest``` + +```sudo ./rftest1 -z --ryu``` + +(or) + +```sudo ./rftest2 +sudo mn --custom topo-4sw-4host.py --topo rftest2 --controller=remote,ip=127.0.0.1,port=6633 --switch=ovsk,protocols=OpenFlow13 --pre ipconf +``` + + + Now open a new terminal and run the testcases: + +```cd RouteFlow/rftest/tests/``` + +```python test_RF.py``` + + + This will run all the three type of testcases. + + Basic tests + + Connectivity tests + + Topology tests. + +Checking the output of testcases: +-------------------------------- + +The output of the testing framework will be updated and saved specific to each run of the command +``` python test_RF.py``` The output is stored to file : ```RouteFlow/rftest/tests/Output.log``` + +Logs will persistantly be stored in the file : ```RouteFlow/rftest/tests/RFtest.log``` + +Extend testcases/Implement New testcases +---------------------------------------- + + +FAQ +--- + +Q. When I run "sudo ./rftest1 -z --ryu" followed by "python test_RF.py", +I get messages about lxc-attach faled +```lxc-attach: No such file or directory - failed to open '/proc/19731/ns/pid' +lxc-attach: failed to enter the namespace``` + +A. lxc-attach requires features that are not present in the native 12.04 kernel (3.5). +You need at least 3.8 which IIRC is available in the backport. The fix is tested on 3.8, +Hence it is suggested you upgrade to 3.8 + +To fix this issue, I ran the following command to get 3.8: +```sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring``` + +or you can run the following command to get 3.5 +```sudo apt-get install linux-image-generic-lts-quantal linux-headers-generic-lts-quantal``` + +License +------- + +This project uses the Apache License version 2.0. See LICENSE for more details. diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index f5d26691..40eb1b2f 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -16,7 +16,7 @@ def __init__(self,containerNames): def getContainerRoutes(self, name): #cmd = "lxc-attach -n " + name + " -- /home route -n" - cmd = "route -n" + cmd = "sudo lxc-attach -n" + name + " -- /sbin/route -n" sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = sp.communicate() return out,err @@ -38,16 +38,41 @@ def parseContainerRoutes(self, name): 0.0.0.0 172.31.2.1 0.0.0.0 UG 100 0 0 eth0 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 + In [36]: out1.splitlines(True)[2:] + Out[36]: + ['0.0.0.0 172.31.1.1 0.0.0.0 UG 100 0 0 eth0\n', + '172.31.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0\n'] + +> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' +172.31.1.2 +> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 +172.31.1.2 + +> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' +eth0 +> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' +eth0 +lo ''' - out = getContainerRoutes(name) - for line in out: - print line #handle storing of only routes in the self.containerRoutes dictionary. + out,err = getContainerRoutes(name) + if out != '': + self.containerRoutes[name] = out.splitlines(True)[2:] + elif err != '': + self.logger.error(name, err) + #handle storing of only routes in the self.containerRoutes dictionary. + #out = getContainerRoutes(name) + #for line in out: + # print line #handle storing of only routes in the self.containerRoutes dictionary. def getContainerInterfaces(self, name): - cmd = "lxc-ps -n " + name + "ifconfig" + cmd = "sudo lxc-attach -n" + name + " -- /sbin/ifconfig" + sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + out,err = sp.communicate() + return out,err + #cmd = "lxc-ps -n " + name + "ifconfig" - return subprocess.call(cmd) + #return subprocess.call(cmd) def parseContainerInterfaces(self, name): ''' From 533d3213891fb7b813fe3dac1d86d83d2d21199e Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 21 Aug 2015 17:54:34 +0530 Subject: [PATCH 25/43] Update README.md Formatting issues resolved. --- rftest/tests/README.md | 59 +++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index 0ba6b435..ae2b34b1 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -2,8 +2,8 @@ rftest/tests =========== RouteFlow's Unified testing framework supports two functionalities: -1. To run the test and get to know the health of routeflow components. -2. Extend the testcases with ease to tests the new features. +* To run the test and get to know the health of routeflow components. +* Extend the testcases with ease to tests the new features. Dependencies ----------- @@ -18,20 +18,18 @@ Building The testing framework requires RouteFlow and rftest to run. The usual way to install RouteFlow and all of its dependencies is as follows: - + Requirements system Ubuntu 12.04 updated and git installed. +* Requirements system Ubuntu 12.04 updated and git installed. ```apt-get update & apt-get upgrade & apt-get install git``` - + Clone RouteFlow +* Clone RouteFlow ```git clone https://github.com/raphaelvrosa/RouteFlow``` - + Build all the required components of RouteFlow +* Build all the required components of RouteFlow -```cd RouteFlow``` - -```git checkout vandervecken``` - -```./build.sh -c -z -n -i ryu``` + + ``` cd RouteFlow ``` + + ``` git checkout vandervecken ``` + + ```./build.sh -c -z -n -i ryu`` Running ------- @@ -40,41 +38,36 @@ RouteFlow usually supplies a script to run all of the components in the correct order. If you want to run rftests, start rftest1 or rftest2 first followed by running test cases. Below are the commands for the same. - + Prior to running the tests, OpenVswitch service should be running. Run the following +* Prior to running the tests, OpenVswitch service should be running. Run the following script to bring up the service: -``` cd RouteFlow/``` -```./ovs-init.sh``` - - + Run the test, either rftest1 or rftest2: + + ``` cd RouteFlow/``` + + ```./ovs-init.sh``` -```cd rftest``` +* Run the test, either rftest1 or rftest2: -```sudo ./rftest1 -z --ryu``` - + + ```cd rftest``` + + ```sudo ./rftest1 -z --ryu``` (or) + + ```sudo ./rftest2 ``` + + ``` sudo mn --custom topo-4sw-4host.py --topo rftest2 --controller=remote,ip=127.0.0.1,port=6633 --switch=ovsk,protocols=OpenFlow13 --pre ipconf ``` -```sudo ./rftest2 -sudo mn --custom topo-4sw-4host.py --topo rftest2 --controller=remote,ip=127.0.0.1,port=6633 --switch=ovsk,protocols=OpenFlow13 --pre ipconf -``` - - + Now open a new terminal and run the testcases: - -```cd RouteFlow/rftest/tests/``` +* Now open a new terminal and run the testcases: -```python test_RF.py``` + +```cd RouteFlow/rftest/tests/``` + + ```python test_RF.py``` - + This will run all the three type of testcases. - + Basic tests - + Connectivity tests - + Topology tests. +* This will run all the three type of testcases. + + Basic tests + + Connectivity tests + + Topology tests. Checking the output of testcases: -------------------------------- -The output of the testing framework will be updated and saved specific to each run of the command -``` python test_RF.py``` The output is stored to file : ```RouteFlow/rftest/tests/Output.log``` +* The output of the testing framework will be updated and saved specific to each run of the command + + ``` python test_RF.py``` The output is stored to file : ```RouteFlow/rftest/tests/Output.log``` -Logs will persistantly be stored in the file : ```RouteFlow/rftest/tests/RFtest.log``` +* Logs will persistantly be stored in the file : ```RouteFlow/rftest/tests/RFtest.log``` Extend testcases/Implement New testcases ---------------------------------------- From 397b7f8669d1d4fd0eb6ab4eef5b09a8715948a2 Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 21 Aug 2015 17:58:22 +0530 Subject: [PATCH 26/43] Update README.md --- rftest/tests/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index ae2b34b1..d2889168 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -19,17 +19,16 @@ The testing framework requires RouteFlow and rftest to run. The usual way to ins all of its dependencies is as follows: * Requirements system Ubuntu 12.04 updated and git installed. -```apt-get update & apt-get upgrade & apt-get install git``` + + ```apt-get update & apt-get upgrade & apt-get install git``` * Clone RouteFlow - -```git clone https://github.com/raphaelvrosa/RouteFlow``` + + ```git clone https://github.com/raphaelvrosa/RouteFlow``` * Build all the required components of RouteFlow + ``` cd RouteFlow ``` + ``` git checkout vandervecken ``` - + ```./build.sh -c -z -n -i ryu`` + + ``` ./build.sh -c -z -n -i ryu ``` Running ------- @@ -40,6 +39,7 @@ running test cases. Below are the commands for the same. * Prior to running the tests, OpenVswitch service should be running. Run the following script to bring up the service: + + ``` cd RouteFlow/``` + ```./ovs-init.sh``` @@ -53,7 +53,7 @@ script to bring up the service: * Now open a new terminal and run the testcases: - +```cd RouteFlow/rftest/tests/``` + + ```cd RouteFlow/rftest/tests/``` + ```python test_RF.py``` * This will run all the three type of testcases. From 636bd3f2775c16a2b8ee0bb2ca3d5f08bae2e4b7 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 21 Aug 2015 12:36:18 +0000 Subject: [PATCH 27/43] sample log files --- rftest/tests/Error.log | 5 +++++ rftest/tests/Output.log | Bin 2520 -> 484 bytes rftest/tests/RFtest.log | 9 +++++++++ 3 files changed, 14 insertions(+) create mode 100644 rftest/tests/Error.log diff --git a/rftest/tests/Error.log b/rftest/tests/Error.log new file mode 100644 index 00000000..69ab0c7d --- /dev/null +++ b/rftest/tests/Error.log @@ -0,0 +1,5 @@ +2015-08-18 18:24:55,780 root ERROR ERROR lxc-info: 'rfvmA' is not running + +2015-08-18 18:24:55,793 root ERROR ERROR +2015-08-18 18:24:55,793 root ERROR ERROR (No info could be read for "-p": geteuid()=1000 but you should be root.) + diff --git a/rftest/tests/Output.log b/rftest/tests/Output.log index efda62dc3700ab88c8ac15f3f2838e9444e51ddc..66adb7342c72863bed47a56f2461d53bef578260 100644 GIT binary patch literal 484 zcma)(-D<)>5QXpa6bElqFlCb(je%ZNu+WPt1YclHHgPe|$o@$C_Kg-qFp|#I49q!v zXC@X=5{fj8QbcJMPqQSM%x4Q2W{11_Tz_n?qm}w#wB3^o;x6rc_FTJF)}df>Qj;KJ*- literal 2520 zcmXpqFf`RQu+TL$S1>fTGB&g_GS@LRGgT?M$7rpI&QRn2exehPdd%~ diff --git a/rftest/tests/RFtest.log b/rftest/tests/RFtest.log index 7c38d29e..5a22de3c 100644 --- a/rftest/tests/RFtest.log +++ b/rftest/tests/RFtest.log @@ -73,3 +73,12 @@ Aug 17 13:31:27 root INFO saikrishna Aug 17 13:31:27 root INFO sai krishna Aug 17 13:31:27 root INFO sai krishna Aug 17 13:31:27 root INFO Test OVS class Begin +Aug 18 18:24:55 root DEBUG saikrishna +Aug 18 18:24:55 root INFO saikrishna +Aug 18 18:24:55 root INFO Test containers class Begin +Aug 18 18:24:55 root ERROR ERROR lxc-info: 'rfvmA' is not running + +Aug 18 18:24:55 root INFO Test Mongod class Begin +Aug 18 18:24:55 root ERROR ERROR +Aug 18 18:24:55 root ERROR ERROR (No info could be read for "-p": geteuid()=1000 but you should be root.) + From 93e5ba4d69cfa7466b612930560b3071c2b5af08 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 21 Aug 2015 13:50:43 +0000 Subject: [PATCH 28/43] removed unwanted files --- rftest/tests/utils.py | 54 ------------------------------------------- 1 file changed, 54 deletions(-) delete mode 100644 rftest/tests/utils.py diff --git a/rftest/tests/utils.py b/rftest/tests/utils.py deleted file mode 100644 index beda39df..00000000 --- a/rftest/tests/utils.py +++ /dev/null @@ -1,54 +0,0 @@ -import subprocess -import os -import json -from subprocess import call - -class DumpToFile(object): - - def __init__(self): - self.tangerine = "And now a thousand years between" - - """ - Command to execute the os commands required. Runs the commands in background and - capture the output. - command : Command to run. - filepath : location to store output file. should not be appended by "/" at the end. - filename : name of the output file to store. - filetype : to store output in the file format specified. - - """ - #def execute(self,command,filepath,filename,filetype): - def execute(self,command): - subprocess.call(command, shell = True) #Popen(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE) - #popen = subprocess.Popen(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE) - #lines_iterator = iter(popen.stdout.readline, b"") - #lines_iterator_error = iter(popen.stderr.readline, c"") - - # if filetype == "json": - # dumpfile = filepath+"/output/" + filename + ".output" + ".json" - # try: - # os.remove(dumpfile) - # except OSError: - # pass - # with open(dumpfile, "a") as jsonfile: - # for line in lines_iterator: - # json.dump(line,jsonfile) - # elif filetype == "txt": - # dumpfile = filepath+"/output/" + filename + ".output" + ".txt" - # try: - # os.remove(dumpfile) - # except OSError: - # pass - # with open(dumpfile, "a") as txtfile: - # for line in lines_iterator: - # txtfile.write(line) - - - ## dumpfile1 = filepath+"/" + filename + ".error" - ## try: - ## os.remove(dumpfile1) - # except OSError: - # pass - # with open(dumpfile1, "a") as jsonfile: - # for line in lines_iterator_error: - # json.dump(line,jsonfile) From f37a64be9ed2d162ab446fc63e14599039c6efcf Mon Sep 17 00:00:00 2001 From: sai krishna Date: Sat, 22 Aug 2015 15:33:42 +0530 Subject: [PATCH 29/43] Update README.md --- rftest/tests/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index d2889168..03b197c3 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -91,6 +91,15 @@ To fix this issue, I ran the following command to get 3.8: or you can run the following command to get 3.5 ```sudo apt-get install linux-image-generic-lts-quantal linux-headers-generic-lts-quantal``` +Issues +------ + +* findTests() function in test_RF.py should be updated with parameters from configureTests dictionary +* Currently Logging server logs only to RFtest.log. Should resolve the issue so that logs will be printed into Debug.log, Output.log and Error.log accordingly. +* This can possibly be a future task. But using argparse to accept huge number of parameters doesnot make sense and make the code clumsy. So I am planning to include a josn file that holds all the comman line parameters and will be used to initialise Tests class. +* Currently tests_connectivity classes's constructor is not able to take the desired number of arguments. Should fix this issue. +* As suggested, a customFind function for ping needs to be implemented and incorporated into findTests() defnition. + License ------- From 38c7d415d1e32e97c601fb6e12e3e29a60935d23 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Sat, 22 Aug 2015 10:14:25 +0000 Subject: [PATCH 30/43] updates and issues logged --- rftest/tests/RFtest.log | 107 +++++++----------------------- rftest/tests/test_Connectivity.py | 2 +- rftest/tests/test_Containers.py | 6 +- rftest/tests/test_Mongo.py | 6 +- rftest/tests/test_OVS.py | 13 ++-- rftest/tests/test_RF.py | 105 ++++++++++++++++++----------- rftest/tests/test_RFApps.py | 9 +-- 7 files changed, 115 insertions(+), 133 deletions(-) diff --git a/rftest/tests/RFtest.log b/rftest/tests/RFtest.log index 5a22de3c..3258255e 100644 --- a/rftest/tests/RFtest.log +++ b/rftest/tests/RFtest.log @@ -1,84 +1,27 @@ -Aug 17 12:54:32 root DEBUG saikrishna -Aug 17 12:54:32 root INFO saikrishna -Aug 17 12:54:32 root INFO sai krishna -Aug 17 12:55:08 root DEBUG saikrishna -Aug 17 12:55:08 root INFO saikrishna -Aug 17 12:55:08 root INFO sai krishna -Aug 17 12:55:08 root INFO sai krishna -Aug 17 12:56:03 root DEBUG saikrishna -Aug 17 12:56:03 root INFO saikrishna -Aug 17 12:56:03 root INFO sai krishna -Aug 17 12:56:03 root INFO sai krishna -Aug 17 12:57:05 root DEBUG saikrishna -Aug 17 12:57:05 root INFO saikrishna -Aug 17 12:57:05 root INFO sai krishna -Aug 17 12:57:05 root INFO sai krishna -Aug 17 12:57:31 root DEBUG saikrishna -Aug 17 12:57:31 root INFO saikrishna -Aug 17 12:57:31 root INFO sai krishna -Aug 17 12:57:31 root INFO sai krishna -Aug 17 12:57:31 root INFO Test OVS class Begin -Aug 17 13:04:16 root DEBUG saikrishna -Aug 17 13:04:16 root INFO saikrishna -Aug 17 13:04:16 root INFO sai krishna -Aug 17 13:04:16 root INFO sai krishna -Aug 17 13:04:16 root INFO Test OVS class Begin -Aug 17 13:06:21 root DEBUG saikrishna -Aug 17 13:06:21 root INFO saikrishna -Aug 17 13:06:21 root INFO sai krishna -Aug 17 13:06:21 root INFO sai krishna -Aug 17 13:06:21 root INFO Test OVS class Begin -Aug 17 13:10:51 root DEBUG saikrishna -Aug 17 13:10:51 root INFO saikrishna -Aug 17 13:10:51 root INFO sai krishna -Aug 17 13:10:51 root INFO sai krishna -Aug 17 13:10:51 root INFO Test OVS class Begin -Aug 17 13:12:25 root DEBUG saikrishna -Aug 17 13:12:25 root INFO saikrishna -Aug 17 13:12:25 root INFO sai krishna -Aug 17 13:12:25 root INFO sai krishna -Aug 17 13:12:25 root INFO Test OVS class Begin -Aug 17 13:16:20 root DEBUG saikrishna -Aug 17 13:16:20 root INFO saikrishna -Aug 17 13:16:20 root INFO sai krishna -Aug 17 13:16:20 root INFO sai krishna -Aug 17 13:16:20 root INFO Test OVS class Begin -Aug 17 13:17:56 root DEBUG saikrishna -Aug 17 13:17:56 root INFO saikrishna -Aug 17 13:17:56 root INFO sai krishna -Aug 17 13:17:56 root INFO sai krishna -Aug 17 13:17:56 root INFO Test OVS class Begin -Aug 17 13:18:44 root DEBUG saikrishna -Aug 17 13:18:44 root INFO saikrishna -Aug 17 13:18:44 root INFO sai krishna -Aug 17 13:18:44 root INFO sai krishna -Aug 17 13:18:44 root INFO Test OVS class Begin -Aug 17 13:20:07 root DEBUG saikrishna -Aug 17 13:20:07 root INFO saikrishna -Aug 17 13:20:07 root INFO sai krishna -Aug 17 13:20:07 root INFO sai krishna -Aug 17 13:20:07 root INFO Test OVS class Begin -Aug 17 13:23:19 root DEBUG saikrishna -Aug 17 13:23:19 root INFO saikrishna -Aug 17 13:23:19 root INFO sai krishna -Aug 17 13:23:19 root INFO sai krishna -Aug 17 13:23:19 root INFO Test OVS class Begin -Aug 17 13:24:51 root DEBUG saikrishna -Aug 17 13:24:51 root INFO saikrishna -Aug 17 13:24:51 root INFO sai krishna -Aug 17 13:24:51 root INFO sai krishna -Aug 17 13:24:51 root INFO Test OVS class Begin -Aug 17 13:31:27 root DEBUG saikrishna -Aug 17 13:31:27 root INFO saikrishna -Aug 17 13:31:27 root INFO sai krishna -Aug 17 13:31:27 root INFO sai krishna -Aug 17 13:31:27 root INFO Test OVS class Begin -Aug 18 18:24:55 root DEBUG saikrishna -Aug 18 18:24:55 root INFO saikrishna -Aug 18 18:24:55 root INFO Test containers class Begin -Aug 18 18:24:55 root ERROR ERROR lxc-info: 'rfvmA' is not running +Aug 22 10:08:06 Test_Containers INFO -Aug 18 18:24:55 root INFO Test Mongod class Begin -Aug 18 18:24:55 root ERROR ERROR -Aug 18 18:24:55 root ERROR ERROR (No info could be read for "-p": geteuid()=1000 but you should be root.) +Aug 22 10:08:06 Test_Containers INFO =============Test containers class Begin================== +Aug 22 10:08:06 Test_Containers INFO running test : lxc-info -nrfvmA +Aug 22 10:08:06 Test_Containers INFO verifying test : lxc-info -nrfvmA +Aug 22 10:08:06 Test_Containers INFO analysing test : lxc-info -nrfvmA +Aug 22 10:08:06 Test_Containers INFO testcase with command lxc-info -nrfvmA FAILED +Aug 22 10:08:06 Test_Mongo INFO +Aug 22 10:08:06 Test_Mongo INFO =========Test Mongod class Begin============== +Aug 22 10:08:06 Test_Mongo INFO running test : ps aux | grep mongo +Aug 22 10:08:06 Test_Mongo INFO running test : netstat -plant | grep 27017 +Aug 22 10:08:06 Test_Mongo INFO verifying test : ps aux | grep mongo +Aug 22 10:08:06 Test_Mongo INFO verifying test : netstat -plant | grep 27017 +Aug 22 10:08:06 Test_Mongo INFO analysing test : netstat -plant | grep 27017 +Aug 22 10:08:06 Test_Mongo INFO testcase with command netstat -plant | grep 27017 FAILED +Aug 22 10:08:06 Test_OVS INFO + +Aug 22 10:08:06 Test_OVS INFO ==========Test OVS class Begin=============== +Aug 22 10:08:06 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 +Aug 22 10:08:06 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 +Aug 22 10:08:06 Test_OVS INFO running test : ps aux | grep dp0 +Aug 22 10:08:06 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 +Aug 22 10:08:06 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 +Aug 22 10:08:06 Test_OVS INFO verifying test : ps aux | grep dp0 +Aug 22 10:08:06 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 +Aug 22 10:08:06 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 FAILED diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index 40eb1b2f..7f8964da 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -161,7 +161,7 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() - #self.logger.getlogger("Test_connectivity") + self.logger = logging.getLogger("Test_Connectivity") self.logger.info("Test connectivity class Begin") self.evaluate() self.verify() diff --git a/rftest/tests/test_Containers.py b/rftest/tests/test_Containers.py index 784d137f..6c75abb2 100644 --- a/rftest/tests/test_Containers.py +++ b/rftest/tests/test_Containers.py @@ -1,3 +1,4 @@ +import logging from test_RF import RFUnitTests class Containers(RFUnitTests): @@ -40,8 +41,9 @@ def run_tests(self): self.addTestsDefault() cmd = self.setTestsParams("lxc-info -n","rfvmA") self.addTest(cmd,"find","state: RUNNING") - #self.logger.getlogger("Test_containers") - self.logger.info("Test containers class Begin") + self.logger = logging.getLogger("Test_Containers") + self.logger.info("\n") + self.logger.info("=============Test containers class Begin==================") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_Mongo.py b/rftest/tests/test_Mongo.py index 8503b1c9..23f1d210 100644 --- a/rftest/tests/test_Mongo.py +++ b/rftest/tests/test_Mongo.py @@ -1,3 +1,4 @@ +import logging from test_RF import RFUnitTests class Mongo(RFUnitTests): @@ -46,8 +47,9 @@ def run_tests(self): self.addTestsDefault() cmd = self.setTestsParams("netstat -plant | grep", 27017) self.addTest(cmd,"find","mongod") - #self.logger.getlogger("Test_Mongod") - self.logger.info("Test Mongod class Begin") + self.logger = logging.getLogger("Test_Mongo") + self.logger.info("\n") + self.logger.info("=========Test Mongod class Begin==============") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_OVS.py b/rftest/tests/test_OVS.py index d8d35f48..73860bcf 100644 --- a/rftest/tests/test_OVS.py +++ b/rftest/tests/test_OVS.py @@ -1,3 +1,4 @@ +import logging from test_RF import RFUnitTests class OVS(RFUnitTests): @@ -7,8 +8,8 @@ class OVS(RFUnitTests): the method of evaluation of the command and the expected output ''' TESTS = { - 'ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, - 'ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, + 'sudo ovs-vsctl show | grep dp0':{'method':'find', 'output':'dp0'}, + 'sudo ovs-dpctl show | grep dp0':{'method':'find', 'output':'dp0'}, } def __init__(self, logger): @@ -44,8 +45,12 @@ def run_tests(self): self.addTestsDefault() self.addTest("ps aux | grep dp0","find","ovsdb-server") self.addTest("ps aux | grep dp0","find","ovs-vswitchd") - #self.logger = logging.getlogger("Test_OVS") - self.logger.info("Test OVS class Begin") + print "\n" + print self.tests + print "\n" + self.logger = logging.getLogger("Test_OVS") + self.logger.info("\n") + self.logger.info("==========Test OVS class Begin===============") self.evaluate() self.verify() self.analyse() diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index b0852590..15fc56cc 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -1,20 +1,21 @@ import sys import time import argparse -#import unittest import os -#import pytest import logging import subprocess import json import fnmatch +import logging.handlers +#import unittest +#import pytest from subprocess import Popen, PIPE -#from utils import DumpToFile logging.basicConfig( filename = 'RFtest.log', - level=logging.INFO, + #level=logging.INFO, + level=logging.DEBUG, format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', datefmt='%b %d %H:%M:%S' ) @@ -123,25 +124,30 @@ def setup(self,output_dir): ''' print "saikrishna setup" - self.logger = logging.getLogger() - self.logger.setLevel(logging.DEBUG) + self.logger = logging.getLogger("test_RF Generic") + #self.logger.setLevel(logging.DEBUG) + formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") #INFO handler - handler = logging.FileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true") + handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true", maxBytes=20, backupCount=5) handler.setLevel(logging.INFO) - formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") + handler.setFormatter(formatter) + self.logger.addHandler(handler) + + #DEBUG handler + handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Debug.log"),"w", encoding=None, delay="true", maxBytes=20, backupCount=5) + handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) self.logger.addHandler(handler) #ERROR handler - handler = logging.FileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true") + handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true", maxBytes=20, backupCount=5) handler.setLevel(logging.ERROR) - formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") handler.setFormatter(formatter) self.logger.addHandler(handler) - self.logger.debug("saikrishna") - self.logger.info("saikrishna") + #self.logger.debug("saikrishna") + #self.logger.info("saikrishna") def findTests(self): ''' @@ -152,30 +158,32 @@ def findTests(self): following self.testParams this dict will be used to be used in runTests function ''' - print "saikrishna findTests" + #self.logger.debug("findtests()") + + # Find all the files starting with test_* and store output to 'matches' list. path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, 'test_*'): if os.path.join(os.path.splitext(filename)[1]) == ".py": matches.append(os.path.join(os.path.splitext(filename)[0])) + + # Check if class is marked to run tests + # If so, Create an object for the corresponding class, by referring to CATALOGUE for testName,toRun in self.testsToRun.items(): print testName,toRun, Tests.CATALOGUE.keys() if toRun == True: - print "cond1" + #self.logger.debug("%s to run", testName) for i in Tests.CATALOGUE.keys(): if i.find(testName) != -1: - print "working conditions" for obj in matches: if obj.find(testName) != -1: if type(Tests.CATALOGUE[obj]) is list: - print "creating test class object if its a list" for classes_ in Tests.CATALOGUE[obj]: #For each file, instantiate an object for the classes in it. module = __import__(obj) class_ = getattr(module, classes_) self.setUpTests[obj] = class_(self.logger) else: - print "creating test class object if its not a list" module = __import__(obj) class_ = getattr(module, Tests.CATALOGUE[obj]) #class_ = getattr(module, "OVS") @@ -235,19 +243,16 @@ def evaluate(self): This function only execute the commands, get the output/err, return them as {command: {'out':output,'err':err} ) ''' - print "saikrishna RFUnitTests evaluate" self.tests #This approach is fine as long as we follow the non-threading approach. are we planning to use threads?? not in near future. if self.evaluateDictionary: self.evaluateDictionary.clear() - #print self.tests.items() for key,value in self.tests.items(): - #out,err = subprocess.call(key,shell = True) #Extract the values from value above and save it to our executuindict. - #Extract the values from value above and save it to our executuindict. + self.logger.info("running test : %s", key) sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = sp.communicate() - #print out,err - #print "saikrishna evaluate out err" + #self.logger.debug("evaluate function() output : %s", out) + #self.logger.debug("evaluate function() error: %s", err) self.evaluateDictionary[key] = {'out' : str(out), 'err' : str(err), } @@ -270,22 +275,39 @@ def verify(self): {'ovs-vsctl show | grep dp0': {'err': 'krishna sai klfhahsd dp01', 'out': 'sai krishnaalskdfkaskj dp0'},} ''' - print "saikrishna RFUnitTests verify" + + #self.logger.info("verify function()") + #key of self.evaluateDictionary is always equal to key of self.tests dictionary. #This is how data structure is built.Hence tests[inputs] will work. #TypeError : there should be a loop on outErrDict. then the error will go away. #print self.evaluateDictionary.items() + #self.logger.debug("begin evaluateDictionary") + #self.logger.debug(self.evaluateDictionary) + #self.logger.debug("end evaluateDictionary") + for cmdInput,outErrDict in self.evaluateDictionary.items(): - if self.tests[cmdInput]['method'] == 'find': - if str(outErrDict.values()[0]).find(self.tests[cmdInput]['output']) != -1: - self.verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0])} - else : - self.verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0])} - #if self.tests[inputs]['method'] == 'findfp': - #This is a part of the function passed as argument from test_Connectivity file. - if outErrDict.values()[0]: - self.verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0])} - + self.logger.info("verifying test : %s", cmdInput) + print "1" + for keys,values in outErrDict.items(): + # Logic: If err value exists, do not check for out + # If out exists, run method and validate tests['output'] with 'out' of outErrDict. + # Fill in assert:True if 'output'.method = 'out' + # else, assert:False. + print '2' + if keys == 'err' and values != '': + print '3' + self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} + elif keys == 'out' and values != '': + print '4' + if self.tests[cmdInput]['method'] == 'find': + print '5' + if values.find(self.tests[cmdInput]['output']) != -1: + print '6' + self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} + #else : TODO case for custom find function. + #if self.tests[inputs]['method'] == 'findfp': + #This is a part of the function passed as argument from test_Connectivity file. #for cmdInput,outErrDict in self.evaluateDictionary.items(): # print "\n", outErrDict.values() @@ -311,12 +333,18 @@ def analyse(self): self.logger.info("ERROR\n %s", err) No need to write assertion ''' - print "saikrishna RFUnitTests analyse" + #self.logger.info("function analyse() ") + #self.logger.debug(self.verifyDictionary) + #self.logger.info("End verifyDictionary ") for keys,values in self.verifyDictionary.items(): + self.logger.info("analysing test : %s", keys) if values['assert'] == True: - self.logger.info("OUTPUT %s", values['result']) + self.logger.info("testcase with command %s PASSED ", keys) + #self.logger.info("%s OUTPUT %s", keys, values['result']) elif values['assert'] == False: - self.logger.error("ERROR %s", values['result']) + self.logger.info("testcase with command %s FAILED ", keys) + #self.logger.error("%s ERROR %s", keys, values) + #self.logger.error("%s ERROR %s", keys, values['resuly']) def run_tests(self): ''' @@ -368,7 +396,8 @@ def run_tests(self): #testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. #testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'Connectivity':True} + #kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'Connectivity':True} + kwargs = {'OVS':True,'Containers':True, 'Mongo':True} args = ("true",1) #testsobj.setTestsToRun(*args,**kwargs) #args.testcases will be a dictionary that is passed. testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index 417af04f..623f26bb 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -1,3 +1,4 @@ +import logging from test_RF import RFUnitTests class RFApps(RFUnitTests): @@ -52,8 +53,8 @@ def run_tests(self): ''' self.addTestsDefault() self.addTest("ps aux | grep rfserver","find","python ./rfserver/rfserver/py") - #self.logger.getlogger("Test_rfserver") - self.logger.info("Test rfserver class Begin") + self.logger = logging.getLogger("Test_RFServer") + self.logger.info("\n =============Test rfserver class Begin================") self.evaluate() self.verify() self.analyse() @@ -99,8 +100,8 @@ def run_tests(self): cmd = self.setTestsParams("netstat -plant | grep", self.port) self.addTest(cmd,"find",output) - #self.logger.getlogger("Test_rfproxy") - self.logger.info("Test rfproxy class Begin") + self.logger = logging.getLogger("Test_RFProxy") + self.logger.info("\n ================Test rfproxy class Begin==================") self.evaluate() self.verify() self.analyse() From 875cc85f8e61dfd8ba81c5bcb42eb9718dc262f1 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Sat, 22 Aug 2015 10:34:13 +0000 Subject: [PATCH 31/43] sample log --- rftest/tests/RFtest.log | 50 +++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/rftest/tests/RFtest.log b/rftest/tests/RFtest.log index 3258255e..d4143df5 100644 --- a/rftest/tests/RFtest.log +++ b/rftest/tests/RFtest.log @@ -1,27 +1,29 @@ -Aug 22 10:08:06 Test_Containers INFO +Aug 22 10:30:50 Test_Containers INFO -Aug 22 10:08:06 Test_Containers INFO =============Test containers class Begin================== -Aug 22 10:08:06 Test_Containers INFO running test : lxc-info -nrfvmA -Aug 22 10:08:06 Test_Containers INFO verifying test : lxc-info -nrfvmA -Aug 22 10:08:06 Test_Containers INFO analysing test : lxc-info -nrfvmA -Aug 22 10:08:06 Test_Containers INFO testcase with command lxc-info -nrfvmA FAILED -Aug 22 10:08:06 Test_Mongo INFO +Aug 22 10:30:50 Test_Containers INFO =============Test containers class Begin================== +Aug 22 10:30:50 Test_Containers INFO running test : lxc-info -nrfvmA +Aug 22 10:30:50 Test_Containers INFO verifying test : lxc-info -nrfvmA +Aug 22 10:30:50 Test_Containers INFO analysing test : lxc-info -nrfvmA +Aug 22 10:30:50 Test_Containers INFO testcase with command lxc-info -nrfvmA FAILED +Aug 22 10:30:50 Test_Mongo INFO -Aug 22 10:08:06 Test_Mongo INFO =========Test Mongod class Begin============== -Aug 22 10:08:06 Test_Mongo INFO running test : ps aux | grep mongo -Aug 22 10:08:06 Test_Mongo INFO running test : netstat -plant | grep 27017 -Aug 22 10:08:06 Test_Mongo INFO verifying test : ps aux | grep mongo -Aug 22 10:08:06 Test_Mongo INFO verifying test : netstat -plant | grep 27017 -Aug 22 10:08:06 Test_Mongo INFO analysing test : netstat -plant | grep 27017 -Aug 22 10:08:06 Test_Mongo INFO testcase with command netstat -plant | grep 27017 FAILED -Aug 22 10:08:06 Test_OVS INFO +Aug 22 10:30:50 Test_Mongo INFO =========Test Mongod class Begin============== +Aug 22 10:30:50 Test_Mongo INFO running test : ps aux | grep mongo +Aug 22 10:30:50 Test_Mongo INFO running test : netstat -plant | grep 27017 +Aug 22 10:30:50 Test_Mongo INFO verifying test : ps aux | grep mongo +Aug 22 10:30:50 Test_Mongo INFO verifying test : netstat -plant | grep 27017 +Aug 22 10:30:50 Test_Mongo INFO analysing test : netstat -plant | grep 27017 +Aug 22 10:30:50 Test_Mongo INFO testcase with command netstat -plant | grep 27017 FAILED +Aug 22 10:30:50 Test_OVS INFO -Aug 22 10:08:06 Test_OVS INFO ==========Test OVS class Begin=============== -Aug 22 10:08:06 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 -Aug 22 10:08:06 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 -Aug 22 10:08:06 Test_OVS INFO running test : ps aux | grep dp0 -Aug 22 10:08:06 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 -Aug 22 10:08:06 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 -Aug 22 10:08:06 Test_OVS INFO verifying test : ps aux | grep dp0 -Aug 22 10:08:06 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 -Aug 22 10:08:06 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 FAILED +Aug 22 10:30:50 Test_OVS INFO ==========Test OVS class Begin=============== +Aug 22 10:30:50 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 +Aug 22 10:30:50 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 +Aug 22 10:30:50 Test_OVS INFO running test : ps aux | grep dp0 +Aug 22 10:30:50 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 +Aug 22 10:30:50 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 +Aug 22 10:30:50 Test_OVS INFO verifying test : ps aux | grep dp0 +Aug 22 10:30:50 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 +Aug 22 10:30:50 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 PASSED +Aug 22 10:30:50 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 +Aug 22 10:30:50 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 PASSED From 611daee3219150d15e35bb6405a7299126138d11 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Wed, 26 Aug 2015 09:59:43 +0000 Subject: [PATCH 32/43] fixed the test required test cases for containers basic tests --- rftest/tests/test_Containers.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/rftest/tests/test_Containers.py b/rftest/tests/test_Containers.py index 6c75abb2..33d4f4e6 100644 --- a/rftest/tests/test_Containers.py +++ b/rftest/tests/test_Containers.py @@ -1,4 +1,5 @@ import logging +import subprocess from test_RF import RFUnitTests class Containers(RFUnitTests): @@ -30,6 +31,19 @@ def setTestsParams(self, cmd, param): ''' return str(cmd) + str(param) + def evaluate(self): + for key,value in self.tests.items(): + self.logger.info("running test : %s", key) + sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + out,err = sp.communicate() + if out != '': + self.logger.info("testcase with command %s PASSED ", key) + self.logger.info("Status of containers") + self.logger.info("\n\n %s", out) + elif err != '': + self.logger.info("testcase with command %s FAILED ", key) + self.logger.error("Status of containers could not be found") + self.logger.error(err) def run_tests(self): ''' @@ -39,11 +53,12 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() - cmd = self.setTestsParams("lxc-info -n","rfvmA") + cmd = self.setTestsParams("sudo lxc-list"," -s") self.addTest(cmd,"find","state: RUNNING") self.logger = logging.getLogger("Test_Containers") self.logger.info("\n") self.logger.info("=============Test containers class Begin==================") + self.logger.info("+++ This test case is to list down the currently 'running', 'stopped' and 'Frozen' list of containers +++") self.evaluate() - self.verify() - self.analyse() + #self.verify() + #self.analyse() From 1b04bb820884e7702dee7676b1e5727bc41c8cf3 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Wed, 26 Aug 2015 11:24:10 +0000 Subject: [PATCH 33/43] Minor fixes --- rftest/tests/test_Containers.py | 7 +++-- rftest/tests/test_Mongo.py | 10 +++---- rftest/tests/test_RF.py | 51 ++++++++++++++------------------- rftest/tests/test_RFApps.py | 12 +++++--- 4 files changed, 38 insertions(+), 42 deletions(-) diff --git a/rftest/tests/test_Containers.py b/rftest/tests/test_Containers.py index 33d4f4e6..131fdeb7 100644 --- a/rftest/tests/test_Containers.py +++ b/rftest/tests/test_Containers.py @@ -12,6 +12,7 @@ class Containers(RFUnitTests): def __init__(self, logger): super(Containers, self).__init__(logger) + self.containerNames = [] def addTestsDefault(self): self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. @@ -23,13 +24,13 @@ def addTest(self, cmd, method, output): self.TESTS[str(cmd)] = {'method':str(method), 'output':str(output),} - def setTestsParams(self, cmd, param): + def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests ''' - return str(cmd) + str(param) + self.containerNames = param def evaluate(self): for key,value in self.tests.items(): @@ -53,7 +54,7 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() - cmd = self.setTestsParams("sudo lxc-list"," -s") + cmd = "sudo lxc-list -s" self.addTest(cmd,"find","state: RUNNING") self.logger = logging.getLogger("Test_Containers") self.logger.info("\n") diff --git a/rftest/tests/test_Mongo.py b/rftest/tests/test_Mongo.py index 23f1d210..9ebbbe2b 100644 --- a/rftest/tests/test_Mongo.py +++ b/rftest/tests/test_Mongo.py @@ -13,6 +13,7 @@ class Mongo(RFUnitTests): def __init__(self, logger): super(Mongo, self).__init__(logger) + self.port = 27017 def addTestsDefault(self): self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. @@ -24,18 +25,15 @@ def addTest(self, cmd, method, output): self.TESTS[str(cmd)] = {'method':str(method), 'output':str(output),} - def setTestsParams(self, cmd, param): + def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests netstat -plant | grep $mongoport ''' - print cmd - print param print "fuck you mongo" - #self.TESTS[str(cmd)] = - return str(cmd) + ' ' + str(param) + self.port = param def run_tests(self): ''' @@ -45,7 +43,7 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() - cmd = self.setTestsParams("netstat -plant | grep", 27017) + cmd = "sudo netstat -plant | grep " + str(self.port) self.addTest(cmd,"find","mongod") self.logger = logging.getLogger("Test_Mongo") self.logger.info("\n") diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 15fc56cc..c787abad 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -185,13 +185,16 @@ def findTests(self): self.setUpTests[obj] = class_(self.logger) else: module = __import__(obj) + #getattr(li, "pop") is the same as calling li.pop class_ = getattr(module, Tests.CATALOGUE[obj]) #class_ = getattr(module, "OVS") print class_ self.setUpTests[obj] = class_(self.logger) for confkey,confvalues in self.testsParams.items(): + print "saisaisai %s %s", confkey , testName if testName == confkey: - self.setUpTests[obj].setTestsParams("fill cmd from tests",confvalues) + print "Mongoooo %s", confkey + self.setUpTests[obj].setTestsParams(confvalues) def runTests(self): ''' @@ -280,7 +283,6 @@ def verify(self): #key of self.evaluateDictionary is always equal to key of self.tests dictionary. #This is how data structure is built.Hence tests[inputs] will work. - #TypeError : there should be a loop on outErrDict. then the error will go away. #print self.evaluateDictionary.items() #self.logger.debug("begin evaluateDictionary") #self.logger.debug(self.evaluateDictionary) @@ -288,6 +290,7 @@ def verify(self): for cmdInput,outErrDict in self.evaluateDictionary.items(): self.logger.info("verifying test : %s", cmdInput) + print "verifying test : %s", cmdInput print "1" for keys,values in outErrDict.items(): # Logic: If err value exists, do not check for out @@ -295,34 +298,24 @@ def verify(self): # Fill in assert:True if 'output'.method = 'out' # else, assert:False. print '2' - if keys == 'err' and values != '': + #if keys == 'err' and values != '': + if keys == 'err': print '3' self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} - elif keys == 'out' and values != '': - print '4' - if self.tests[cmdInput]['method'] == 'find': - print '5' - if values.find(self.tests[cmdInput]['output']) != -1: - print '6' - self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} - #else : TODO case for custom find function. - #if self.tests[inputs]['method'] == 'findfp': - #This is a part of the function passed as argument from test_Connectivity file. - - #for cmdInput,outErrDict in self.evaluateDictionary.items(): - # print "\n", outErrDict.values() - # print outErrDict.values()[0]['err'] - # if outErrDict.values()[0]['out'] != ' ': - # if self.tests[cmdInput]['method'] == 'find': - # if str(outErrDict.values()[0]['out']).find(self.tests[cmdInput]['output']) != -1: - # self.verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0])} - # verifyDictionary[cmdInput] = {'assert':True, 'result':str(outErrDict.values()[0]['out'])} - # else : - # verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['out'])} - # #if self.tests[inputs]['method'] == 'findfp': - # #This is a part of the function passed as argument from test_Connectivity file. - # if outErrDict.values()[0]['err']: - # self.verifyDictionary[cmdInput] = {'assert':False, 'result':str(outErrDict.values()[0]['err'])} + elif keys == 'out' : + if values == '': + print 3.5 + self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} + else: + print '4' + if self.tests[cmdInput]['method'] == 'find': + print '5' + if values.find(self.tests[cmdInput]['output']) != -1: + print '6' + self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} + #else : TODO case for custom find function. + #if self.tests[inputs]['method'] == 'findfp': + #This is a part of the function passed as argument from test_Connectivity file. def analyse(self): ''' @@ -403,7 +396,7 @@ def run_tests(self): testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. #testsobj.setTestsToRun() #args.testcases will be a dictionary that is passed. #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - testsobj.configureTests(mongo = 5056, containers = 'rfvmA', connectivity = 'rfvm1')#, rfapp = args.rfapps) + testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1')#, rfapp = args.rfapps) testsobj.setTestsOutputFormat() testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index 623f26bb..9d94a740 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -22,13 +22,16 @@ def addTest(self, cmd, method, output): self.TESTS[str(cmd)] = {'method':str(method), 'output':str(output),} - def setTestsParams(self, cmd, param): + #def setTestsParams(self, cmd, param): + def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests ''' - return str(cmd) + str(param) + #return str(cmd) + str(param) + #self.port = param + pass def run_tests(self): ''' @@ -68,13 +71,14 @@ def __init__(self, logger): #self.port = port #ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py" - def setTestsParams(self, cmd, param): + def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests ''' - return str(cmd) + str(param) + #return str(cmd) + str(param) + self.port = param def setTestsOutputs(self, output1, output2, param): ''' From f293e31d1175431e836708706d7636d680b63290 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Wed, 26 Aug 2015 14:42:03 +0000 Subject: [PATCH 34/43] fixes in RFApps --- rftest/tests/RFtest.log | 141 ++++++++++++++++++++++++++++-------- rftest/tests/test_Mongo.py | 1 - rftest/tests/test_RF.py | 11 +-- rftest/tests/test_RFApps.py | 64 ++++++++++++++-- 4 files changed, 175 insertions(+), 42 deletions(-) diff --git a/rftest/tests/RFtest.log b/rftest/tests/RFtest.log index d4143df5..e154aab7 100644 --- a/rftest/tests/RFtest.log +++ b/rftest/tests/RFtest.log @@ -1,29 +1,112 @@ -Aug 22 10:30:50 Test_Containers INFO - -Aug 22 10:30:50 Test_Containers INFO =============Test containers class Begin================== -Aug 22 10:30:50 Test_Containers INFO running test : lxc-info -nrfvmA -Aug 22 10:30:50 Test_Containers INFO verifying test : lxc-info -nrfvmA -Aug 22 10:30:50 Test_Containers INFO analysing test : lxc-info -nrfvmA -Aug 22 10:30:50 Test_Containers INFO testcase with command lxc-info -nrfvmA FAILED -Aug 22 10:30:50 Test_Mongo INFO - -Aug 22 10:30:50 Test_Mongo INFO =========Test Mongod class Begin============== -Aug 22 10:30:50 Test_Mongo INFO running test : ps aux | grep mongo -Aug 22 10:30:50 Test_Mongo INFO running test : netstat -plant | grep 27017 -Aug 22 10:30:50 Test_Mongo INFO verifying test : ps aux | grep mongo -Aug 22 10:30:50 Test_Mongo INFO verifying test : netstat -plant | grep 27017 -Aug 22 10:30:50 Test_Mongo INFO analysing test : netstat -plant | grep 27017 -Aug 22 10:30:50 Test_Mongo INFO testcase with command netstat -plant | grep 27017 FAILED -Aug 22 10:30:50 Test_OVS INFO - -Aug 22 10:30:50 Test_OVS INFO ==========Test OVS class Begin=============== -Aug 22 10:30:50 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 -Aug 22 10:30:50 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 -Aug 22 10:30:50 Test_OVS INFO running test : ps aux | grep dp0 -Aug 22 10:30:50 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 -Aug 22 10:30:50 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 -Aug 22 10:30:50 Test_OVS INFO verifying test : ps aux | grep dp0 -Aug 22 10:30:50 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 -Aug 22 10:30:50 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 PASSED -Aug 22 10:30:50 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 -Aug 22 10:30:50 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 PASSED +Aug 26 14:40:52 Test_RFProxy INFO + +Aug 26 14:40:52 Test_RFProxy INFO ================Test rfproxy class Begin================== +Aug 26 14:40:52 Test_RFProxy INFO running test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFProxy INFO running test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFProxy INFO verifying test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFProxy INFO verifying test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFProxy INFO analysing test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFProxy INFO testcase with command netstat -plant | grep6653 FAILED +Aug 26 14:40:52 Test_RFProxy INFO analysing test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFProxy INFO testcase with command ps aux | grep rfproxy FAILED +Aug 26 14:40:52 Test_Containers INFO + +Aug 26 14:40:52 Test_Containers INFO =============Test containers class Begin================== +Aug 26 14:40:52 Test_Containers INFO +++ This test case is to list down the currently 'running', 'stopped' and 'Frozen' list of containers +++ +Aug 26 14:40:52 Test_Containers INFO running test : sudo lxc-list -s +Aug 26 14:40:52 Test_Containers INFO testcase with command sudo lxc-list -s PASSED +Aug 26 14:40:52 Test_Containers INFO Status of containers +Aug 26 14:40:52 Test_Containers INFO + + RUNNING + b1 + b2 + rfvm1 + +FROZEN + +STOPPED + base + my-container + rfvmA + rfvmB + rfvmC + rfvmD + + +Aug 26 14:40:52 Test_RFClient INFO + +Aug 26 14:40:52 Test_RFClient INFO ================Test rfclient class Begin================== +Aug 26 14:40:52 Test_RFClient INFO running test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFClient INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO running test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFClient INFO verifying test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFClient INFO verifying test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFClient INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO analysing test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFClient INFO testcase with command netstat -plant | grep6653 FAILED +Aug 26 14:40:52 Test_RFClient INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient PASSED +Aug 26 14:40:52 Test_RFClient INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED +Aug 26 14:40:52 Test_RFClient INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFClient INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED +Aug 26 14:40:52 Test_RFClient INFO analysing test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFClient INFO testcase with command ps aux | grep rfproxy FAILED +Aug 26 14:40:52 Test_OVS INFO + +Aug 26 14:40:52 Test_OVS INFO ==========Test OVS class Begin=============== +Aug 26 14:40:52 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 +Aug 26 14:40:52 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 +Aug 26 14:40:52 Test_OVS INFO running test : ps aux | grep dp0 +Aug 26 14:40:52 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 +Aug 26 14:40:52 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 +Aug 26 14:40:52 Test_OVS INFO verifying test : ps aux | grep dp0 +Aug 26 14:40:52 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 +Aug 26 14:40:52 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 PASSED +Aug 26 14:40:52 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 +Aug 26 14:40:52 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 PASSED +Aug 26 14:40:52 Test_OVS INFO analysing test : ps aux | grep dp0 +Aug 26 14:40:52 Test_OVS INFO testcase with command ps aux | grep dp0 FAILED +Aug 26 14:40:52 Test_RFServer INFO + +Aug 26 14:40:52 Test_RFServer INFO =============Test rfserver class Begin================ +Aug 26 14:40:52 Test_RFServer INFO running test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFServer INFO running test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFServer INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO running test : ps aux | grep rfserver +Aug 26 14:40:52 Test_RFServer INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO verifying test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFServer INFO verifying test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFServer INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO verifying test : ps aux | grep rfserver +Aug 26 14:40:52 Test_RFServer INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO analysing test : ps aux | grep rfproxy +Aug 26 14:40:52 Test_RFServer INFO testcase with command ps aux | grep rfproxy FAILED +Aug 26 14:40:52 Test_RFServer INFO analysing test : netstat -plant | grep6653 +Aug 26 14:40:52 Test_RFServer INFO testcase with command netstat -plant | grep6653 FAILED +Aug 26 14:40:52 Test_RFServer INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient PASSED +Aug 26 14:40:52 Test_RFServer INFO analysing test : ps aux | grep rfserver +Aug 26 14:40:52 Test_RFServer INFO testcase with command ps aux | grep rfserver FAILED +Aug 26 14:40:52 Test_RFServer INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED +Aug 26 14:40:52 Test_RFServer INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 26 14:40:52 Test_RFServer INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED +Aug 26 14:40:52 Test_Mongo INFO + +Aug 26 14:40:52 Test_Mongo INFO =========Test Mongod class Begin============== +Aug 26 14:40:52 Test_Mongo INFO running test : ps aux | grep mongo +Aug 26 14:40:52 Test_Mongo INFO running test : sudo netstat -plant | grep 5056 +Aug 26 14:40:52 Test_Mongo INFO verifying test : ps aux | grep mongo +Aug 26 14:40:52 Test_Mongo INFO verifying test : sudo netstat -plant | grep 5056 +Aug 26 14:40:52 Test_Mongo INFO analysing test : ps aux | grep mongo +Aug 26 14:40:52 Test_Mongo INFO testcase with command ps aux | grep mongo FAILED +Aug 26 14:40:52 Test_Mongo INFO analysing test : sudo netstat -plant | grep 5056 +Aug 26 14:40:52 Test_Mongo INFO testcase with command sudo netstat -plant | grep 5056 FAILED diff --git a/rftest/tests/test_Mongo.py b/rftest/tests/test_Mongo.py index 9ebbbe2b..ae5fbb78 100644 --- a/rftest/tests/test_Mongo.py +++ b/rftest/tests/test_Mongo.py @@ -32,7 +32,6 @@ def setTestsParams(self, param): In this case modify cmd in self.tests netstat -plant | grep $mongoport ''' - print "fuck you mongo" self.port = param def run_tests(self): diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index c787abad..824f6962 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -38,7 +38,7 @@ class Tests: def __init__(self): self.testsToRun = {'OVS':True, 'Containers':False, 'RFApps':False} - self.testsParams = {'Mongo':27017, 'Containers':['rfvmA','rfvmB'], 'RFApps':['rfproxy','rfserver']} + self.testsParams = {'Mongo':27017, 'Containers':['rfvmA','rfvmB']}#, 'RFApps':['rfproxy','rfserver']} self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False @@ -182,16 +182,14 @@ def findTests(self): for classes_ in Tests.CATALOGUE[obj]: #For each file, instantiate an object for the classes in it. module = __import__(obj) class_ = getattr(module, classes_) - self.setUpTests[obj] = class_(self.logger) + self.setUpTests[classes_] = class_(self.logger) else: module = __import__(obj) #getattr(li, "pop") is the same as calling li.pop class_ = getattr(module, Tests.CATALOGUE[obj]) - #class_ = getattr(module, "OVS") print class_ self.setUpTests[obj] = class_(self.logger) for confkey,confvalues in self.testsParams.items(): - print "saisaisai %s %s", confkey , testName if testName == confkey: print "Mongoooo %s", confkey self.setUpTests[obj].setTestsParams(confvalues) @@ -214,7 +212,6 @@ def runTests(self): # if tests == True: # logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. - print "saikrishna runTests" for test in self.setUpTests.keys(): self.setUpTests[test].run_tests() @@ -390,13 +387,13 @@ def run_tests(self): #testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) #kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'Connectivity':True} - kwargs = {'OVS':True,'Containers':True, 'Mongo':True} + kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True} args = ("true",1) #testsobj.setTestsToRun(*args,**kwargs) #args.testcases will be a dictionary that is passed. testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. #testsobj.setTestsToRun() #args.testcases will be a dictionary that is passed. #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1')#, rfapp = args.rfapps) + testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1', RFApps = ['RFServer','RFClient','RFProxy']) testsobj.setTestsOutputFormat() testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index 9d94a740..4749b76e 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -13,7 +13,7 @@ def __init__(self, logger): super(RFApps, self).__init__(logger) def addTestsDefault(self): - self.tests = self.TESTS #tests is initialised in RFUnitTests class and should be filled in here. + self.tests = self.TESTS def addTest(self, cmd, method, output): ''' @@ -30,7 +30,6 @@ def setTestsParams(self, param): In this case modify cmd in self.tests ''' #return str(cmd) + str(param) - #self.port = param pass def run_tests(self): @@ -47,6 +46,15 @@ class RFServer(RFApps): def __init__(self, logger): super(RFServer, self).__init__(logger) + def setTestsParams(self, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + #return str(cmd) + str(param) + pass + def run_tests(self): ''' basically runs methods inherited with self.tests attribute @@ -54,10 +62,12 @@ def run_tests(self): self.verify self.analyse ''' + print "Fuck you RFserver" self.addTestsDefault() self.addTest("ps aux | grep rfserver","find","python ./rfserver/rfserver/py") self.logger = logging.getLogger("Test_RFServer") - self.logger.info("\n =============Test rfserver class Begin================") + self.logger.info("\n") + self.logger.info("=============Test rfserver class Begin================") self.evaluate() self.verify() self.analyse() @@ -101,11 +111,55 @@ def run_tests(self): self.addTest("ps aux | grep rfproxy","find",output) output = self.setTestsOutputs('127.0.0.1:', '', self.port) - cmd = self.setTestsParams("netstat -plant | grep", self.port) + cmd = "netstat -plant | grep" + str(self.port) self.addTest(cmd,"find",output) self.logger = logging.getLogger("Test_RFProxy") - self.logger.info("\n ================Test rfproxy class Begin==================") + self.logger.info("\n") + self.logger.info("================Test rfproxy class Begin==================") self.evaluate() self.verify() self.analyse() + +class RFClient(RFApps): + + def __init__(self,logger): + super(RFClient, self).__init__(logger) + + + def setTestsParams(self, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + + In this case modify cmd in self.tests by adding list of tests with vm names + ''' + #return str(cmd) + str(param) + self.port = param + + def run_tests(self): + ''' + basically runs methods inherited with self.tests attribute + self.evaluate + self.verify + self.analyse + ''' + self.addTestsDefault() + + cmd = "sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient" + self.addTest(cmd,"find","rfclient") + + cmd = "sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient" + self.addTest(cmd,"find","") + + cmd = "sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient" + self.addTest(cmd,"find","") + + self.logger = logging.getLogger("Test_RFClient") + self.logger.info("\n") + self.logger.info("================Test rfclient class Begin==================") + self.evaluate() + self.verify() + self.analyse() + + From 42842effd30f94d6a9634d5686e9ec03a294a299 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 27 Aug 2015 06:52:10 +0000 Subject: [PATCH 35/43] fixes --- rftest/tests/test_Connectivity.py | 34 ++++++++-------- rftest/tests/test_OVS.py | 5 +-- rftest/tests/test_RF.py | 66 ++++++------------------------- rftest/tests/test_RFApps.py | 20 +++++----- 4 files changed, 40 insertions(+), 85 deletions(-) diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index 7f8964da..923711f1 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -1,14 +1,12 @@ from test_RF import RFUnitTests import subprocess -#class Connectivity(RFUnitTests()): -class Connectivity(): +class Connectivity(RFUnitTests()): TESTS = {} - #def __init__(self, containerNames, logger): - def __init__(self,containerNames): - #super(Conncetivity, self).__init__(logger) + def __init__(self, logger, containerNames = 'rfvm1'): + super(Conncetivity, self).__init__(logger) self.containerRoutes = {} self.containerInterfaces = {} self.containerNames = containerNames #string to be defined by user (e.g., in rftest2 is rfvmA, B, ... @@ -20,8 +18,6 @@ def getContainerRoutes(self, name): sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = sp.communicate() return out,err - #return sp.stdout This is a file pointer. No use in using this method - #return subprocess.call(cmd) def parseContainerRoutes(self, name): ''' @@ -43,16 +39,18 @@ def parseContainerRoutes(self, name): ['0.0.0.0 172.31.1.1 0.0.0.0 UG 100 0 0 eth0\n', '172.31.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0\n'] -> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' -172.31.1.2 -> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 -172.31.1.2 + > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' + 172.31.1.2 + + > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 + 172.31.1.2 + + > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' + eth0 -> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' -eth0 -> tests git:(vandervecken) ✗ sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' -eth0 -lo + > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' + eth0 + lo ''' out,err = getContainerRoutes(name) if out != '': @@ -71,7 +69,6 @@ def getContainerInterfaces(self, name): out,err = sp.communicate() return out,err #cmd = "lxc-ps -n " + name + "ifconfig" - #return subprocess.call(cmd) def parseContainerInterfaces(self, name): @@ -147,6 +144,9 @@ def addTestsDefault(self): I put kwargs in addTest to insert the containers name and target so it can be logged in the analyse function ''' + + self.TESTS.clear() + def findFunction(self): ''' find packet loss percentage in ping result diff --git a/rftest/tests/test_OVS.py b/rftest/tests/test_OVS.py index 73860bcf..8da4f384 100644 --- a/rftest/tests/test_OVS.py +++ b/rftest/tests/test_OVS.py @@ -41,13 +41,10 @@ def run_tests(self): self.verify self.analyse ''' - print "sai OVS runtests" + self.addTestsDefault() self.addTest("ps aux | grep dp0","find","ovsdb-server") self.addTest("ps aux | grep dp0","find","ovs-vswitchd") - print "\n" - print self.tests - print "\n" self.logger = logging.getLogger("Test_OVS") self.logger.info("\n") self.logger.info("==========Test OVS class Begin===============") diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 824f6962..2d4ed61a 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -14,10 +14,10 @@ logging.basicConfig( filename = 'RFtest.log', - #level=logging.INFO, level=logging.DEBUG, format='%(asctime)s %(name)-15s %(levelname)-8s %(message)s', - datefmt='%b %d %H:%M:%S' + datefmt='%b %d %H:%M:%S', + mode = 'w' ) class Tests: @@ -45,7 +45,6 @@ def __init__(self): self.setUpTests = {} self.logger = None - #def setTestsToRun(self, *kargs, **kwargs): def setTestsToRun(self, **kwargs): ''' fill self.testsToRun with proper arguments @@ -53,10 +52,6 @@ def setTestsToRun(self, **kwargs): define if tests will use pytest or not (self.use_pytest = False) ''' - print "saikrishna setTestsToRun" - print "%s"%( kwargs) - #if 'pytest' in kwargs.keys(): - # self.use_pytest = kwargs['pytest'] #If kwargs is empty leave the testsToRun dictionary with default arguments. if kwargs: @@ -68,7 +63,6 @@ def configureTests(self, **kwargs): ''' fill self.testsParams with arguments ''' - print "saikrishna configureTests" if kwargs: self.testsParams.clear() for key,param in kwargs.items(): @@ -82,7 +76,6 @@ def setTestsOutputFormat(self, **kwargs): in json or raw formats (first let`s save in raw format) ''' - print "saikrishna setTestsOutputFormat" self.outputFormat.clear() if kwargs: for key,formats in kwargs.items(): @@ -94,7 +87,6 @@ def setTestsOutputModes(self, **kwargs): in json or raw formats (first let`s save in raw format) ''' - print "saikrishna setTestsOutputModes" self.outputModes.clear() if kwargs: for key,modes in kwargs.items(): @@ -123,32 +115,29 @@ def setup(self,output_dir): :output_dir: directory to store logs ''' - print "saikrishna setup" - self.logger = logging.getLogger("test_RF Generic") + self.logger = logging.getLogger(__name__) #self.logger.setLevel(logging.DEBUG) formatter = logging.Formatter("%(asctime)s %(name)-15s %(levelname)-8s %(message)s") #INFO handler - handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Output.log"),"w", encoding=None, delay="true", maxBytes=20, backupCount=5) + #handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Output.log"),"a", encoding=None, delay="true", maxBytes=20, backupCount=5) + handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Output.log"),"a") handler.setLevel(logging.INFO) handler.setFormatter(formatter) self.logger.addHandler(handler) #DEBUG handler - handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Debug.log"),"w", encoding=None, delay="true", maxBytes=20, backupCount=5) + handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Debug.log"),"a", encoding=None, delay="true", maxBytes=20, backupCount=5) handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) self.logger.addHandler(handler) #ERROR handler - handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Error.log"),"w", encoding=None, delay="true", maxBytes=20, backupCount=5) + handler = logging.handlers.RotatingFileHandler(os.path.join(output_dir, "Error.log"),"a", encoding=None, delay="true", maxBytes=20, backupCount=5) handler.setLevel(logging.ERROR) handler.setFormatter(formatter) self.logger.addHandler(handler) - #self.logger.debug("saikrishna") - #self.logger.info("saikrishna") - def findTests(self): ''' for testsToRun create a function that reads self.testsToRun and do the following: @@ -158,7 +147,6 @@ def findTests(self): following self.testParams this dict will be used to be used in runTests function ''' - #self.logger.debug("findtests()") # Find all the files starting with test_* and store output to 'matches' list. path = os.getcwd()# This should be pointing to /RouteFlow/rftest/tests @@ -171,9 +159,7 @@ def findTests(self): # Check if class is marked to run tests # If so, Create an object for the corresponding class, by referring to CATALOGUE for testName,toRun in self.testsToRun.items(): - print testName,toRun, Tests.CATALOGUE.keys() if toRun == True: - #self.logger.debug("%s to run", testName) for i in Tests.CATALOGUE.keys(): if i.find(testName) != -1: for obj in matches: @@ -187,11 +173,9 @@ def findTests(self): module = __import__(obj) #getattr(li, "pop") is the same as calling li.pop class_ = getattr(module, Tests.CATALOGUE[obj]) - print class_ self.setUpTests[obj] = class_(self.logger) for confkey,confvalues in self.testsParams.items(): if testName == confkey: - print "Mongoooo %s", confkey self.setUpTests[obj].setTestsParams(confvalues) def runTests(self): @@ -205,12 +189,6 @@ def runTests(self): Using dict returned by setup (ex: self.setupTests) call run_tests for each object ''' - # call setup with the name argument. This name will be used by logger. - # The name is an itervalue from self.testsToRun dictionary. Pass each key as an argument, if the corresponding value is true. - #self.setTestsOutputModes() #This line is temporary. remove it if it is visible in vandervecken. - #for key,tests in self.testsToRun.items(): - # if tests == True: - # logger = self.setup(str(key),os.getcwd()) #pass this logger as an argument while calling tests. for test in self.setUpTests.keys(): self.setUpTests[test].run_tests() @@ -247,12 +225,11 @@ def evaluate(self): #This approach is fine as long as we follow the non-threading approach. are we planning to use threads?? not in near future. if self.evaluateDictionary: self.evaluateDictionary.clear() + self.logger.info("\n") for key,value in self.tests.items(): self.logger.info("running test : %s", key) sp = subprocess.Popen(key,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = sp.communicate() - #self.logger.debug("evaluate function() output : %s", out) - #self.logger.debug("evaluate function() error: %s", err) self.evaluateDictionary[key] = {'out' : str(out), 'err' : str(err), } @@ -276,26 +253,18 @@ def verify(self): 'out': 'sai krishnaalskdfkaskj dp0'},} ''' - #self.logger.info("verify function()") - #key of self.evaluateDictionary is always equal to key of self.tests dictionary. #This is how data structure is built.Hence tests[inputs] will work. - #print self.evaluateDictionary.items() - #self.logger.debug("begin evaluateDictionary") - #self.logger.debug(self.evaluateDictionary) - #self.logger.debug("end evaluateDictionary") + self.logger.info("\n") for cmdInput,outErrDict in self.evaluateDictionary.items(): self.logger.info("verifying test : %s", cmdInput) - print "verifying test : %s", cmdInput - print "1" for keys,values in outErrDict.items(): # Logic: If err value exists, do not check for out # If out exists, run method and validate tests['output'] with 'out' of outErrDict. # Fill in assert:True if 'output'.method = 'out' # else, assert:False. print '2' - #if keys == 'err' and values != '': if keys == 'err': print '3' self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} @@ -323,18 +292,13 @@ def analyse(self): self.logger.info("ERROR\n %s", err) No need to write assertion ''' - #self.logger.info("function analyse() ") - #self.logger.debug(self.verifyDictionary) - #self.logger.info("End verifyDictionary ") + self.logger.info("\n") for keys,values in self.verifyDictionary.items(): self.logger.info("analysing test : %s", keys) if values['assert'] == True: self.logger.info("testcase with command %s PASSED ", keys) - #self.logger.info("%s OUTPUT %s", keys, values['result']) elif values['assert'] == False: self.logger.info("testcase with command %s FAILED ", keys) - #self.logger.error("%s ERROR %s", keys, values) - #self.logger.error("%s ERROR %s", keys, values['resuly']) def run_tests(self): ''' @@ -383,20 +347,16 @@ def run_tests(self): args = parser.parse_args() testsobj = Tests() - #testsobj.setTestsToRun(args) #args.testcases will be a dictionary that is passed. - #testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} - #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) - #kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'Connectivity':True} + kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True} args = ("true",1) - #testsobj.setTestsToRun(*args,**kwargs) #args.testcases will be a dictionary that is passed. testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. - #testsobj.setTestsToRun() #args.testcases will be a dictionary that is passed. - #testsobj.configureTests(mongo = args.mongoport, containers = args.lxc, rfapp = args.rfapps) testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1', RFApps = ['RFServer','RFClient','RFProxy']) + testsobj.setTestsOutputFormat() testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} testsobj.setup(os.path.dirname(os.path.realpath(__file__))) + testsobj.findTests() testsobj.runTests() diff --git a/rftest/tests/test_RFApps.py b/rftest/tests/test_RFApps.py index 4749b76e..0d491d91 100644 --- a/rftest/tests/test_RFApps.py +++ b/rftest/tests/test_RFApps.py @@ -13,6 +13,7 @@ def __init__(self, logger): super(RFApps, self).__init__(logger) def addTestsDefault(self): + self.TESTS.clear() self.tests = self.TESTS def addTest(self, cmd, method, output): @@ -22,14 +23,12 @@ def addTest(self, cmd, method, output): self.TESTS[str(cmd)] = {'method':str(method), 'output':str(output),} - #def setTestsParams(self, cmd, param): def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests ''' - #return str(cmd) + str(param) pass def run_tests(self): @@ -50,9 +49,7 @@ def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively - In this case modify cmd in self.tests ''' - #return str(cmd) + str(param) pass def run_tests(self): @@ -62,12 +59,14 @@ def run_tests(self): self.verify self.analyse ''' - print "Fuck you RFserver" self.addTestsDefault() + self.addTest("ps aux | grep rfserver","find","python ./rfserver/rfserver/py") + self.logger = logging.getLogger("Test_RFServer") self.logger.info("\n") self.logger.info("=============Test rfserver class Begin================") + self.evaluate() self.verify() self.analyse() @@ -77,17 +76,14 @@ class RFProxy(RFApps): def __init__(self, logger): super(RFProxy, self).__init__(logger) self.port = 6653 - #super(RFproxy, self).__init__(logger, port=6653) - #self.port = port #ryu-manager --use-stderr --ofp-tcp-listen-port=$CONTROLLER_PORT ryu-rfproxy/rfproxy.py" def setTestsParams(self, param): ''' Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively - In this case modify cmd in self.tests + In this case modify port number, which is passed as an argument while running testcase. ''' - #return str(cmd) + str(param) self.port = param def setTestsOutputs(self, output1, output2, param): @@ -107,16 +103,18 @@ def run_tests(self): self.analyse ''' self.addTestsDefault() + output = self.setTestsOutputs('ryu-manager --use-stderr --ofp-tcp-listen-port=',' ryu-rfproxy/rfproxy.py', self.port) self.addTest("ps aux | grep rfproxy","find",output) output = self.setTestsOutputs('127.0.0.1:', '', self.port) - cmd = "netstat -plant | grep" + str(self.port) + cmd = "netstat -plant | grep " + str(self.port) self.addTest(cmd,"find",output) self.logger = logging.getLogger("Test_RFProxy") self.logger.info("\n") self.logger.info("================Test rfproxy class Begin==================") + self.evaluate() self.verify() self.analyse() @@ -134,7 +132,6 @@ def setTestsParams(self, param): In this case modify cmd in self.tests by adding list of tests with vm names ''' - #return str(cmd) + str(param) self.port = param def run_tests(self): @@ -158,6 +155,7 @@ def run_tests(self): self.logger = logging.getLogger("Test_RFClient") self.logger.info("\n") self.logger.info("================Test rfclient class Begin==================") + self.evaluate() self.verify() self.analyse() From 743533bd4ffc3a53d48d08b3f2d301b4867cd489 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 27 Aug 2015 14:49:30 +0000 Subject: [PATCH 36/43] all the requirements satisfied --- rftest/tests/test_Connectivity.py | 68 ++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index 923711f1..7e400874 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -39,18 +39,6 @@ def parseContainerRoutes(self, name): ['0.0.0.0 172.31.1.1 0.0.0.0 UG 100 0 0 eth0\n', '172.31.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0\n'] - > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' - 172.31.1.2 - - > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 - 172.31.1.2 - - > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' - eth0 - - > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' - eth0 - lo ''' out,err = getContainerRoutes(name) if out != '': @@ -64,12 +52,54 @@ def parseContainerRoutes(self, name): def getContainerInterfaces(self, name): - cmd = "sudo lxc-attach -n" + name + " -- /sbin/ifconfig" - sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) - out,err = sp.communicate() - return out,err - #cmd = "lxc-ps -n " + name + "ifconfig" - #return subprocess.call(cmd) + ''' + import subprocess + su = subprocess.Popen("ip -o addr show | awk \'/inet / {print $2, $4}\'",stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + out,err = su.communicate() + + In [30]: for i in out.splitlines(): + if i.split()[0] == 'lo': + pass + else: + dd[i.split()[0]] = i.split()[1] + + In [31]: dd + Out[31]: {'eth0': '10.0.2.15/24', 'lxcbr0': '10.0.3.1/24'} + + > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' + 172.31.1.2 + + > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 + 172.31.1.2 + + > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' + eth0 + + > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' + eth0 + lo + ''' + listIface = [] + dictIfIp = {} + + #cmd = "sudo lxc-attach -n " + name + " -- /sbin/ifconfig -a | sed \'s/[ \t].*//;/^\(lo\|\)$/d\'" + cmd = "sudo lxc-attach -n rfvm1 -- /sbin/ifconfig -a | sed \'s/[ \t].*//;/^\(lo\|\)$/d\'" + su = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + out,err = su.communicate() + + listIface = [] + for i in out.splitlines(): + listIface.append(i) + dictIfIp.clear() + for i in listIface: + #cmd = "sudo lxc-attach -n" + name + " -- /sbin/ifconfig " + str(i) + "| grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'" + cmd = "sudo lxc-attach -n rfvm1 -- /sbin/ifconfig " + str(i) + "| grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'" + su = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + out,err = su.communicate() + dictIfIp[i] = out + + return dictIfIp + def parseContainerInterfaces(self, name): ''' @@ -96,7 +126,7 @@ def parseContainerInterfaces(self, name): collisions:0 txqueuelen:0 RX bytes:176 (176.0 B) TX bytes:176 (176.0 B) ''' - pass + self.containerInterfaces[name] = getContainerInterfaces(name) def addTest(self, cmd, method, output, **kwargs): ''' From 15fef978330edeba6f2bb74f446eb5a5cffddbe9 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Thu, 27 Aug 2015 21:36:11 +0000 Subject: [PATCH 37/43] connectivity tests issues --- rftest/tests/test_Connectivity.py | 32 +++++++++++++++----- rftest/tests/test_RF.py | 49 ++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index 7e400874..fccc46f6 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -1,12 +1,12 @@ from test_RF import RFUnitTests import subprocess -class Connectivity(RFUnitTests()): +class Connectivity(RFUnitTests): TESTS = {} def __init__(self, logger, containerNames = 'rfvm1'): - super(Conncetivity, self).__init__(logger) + super(Connectivity, self).__init__(logger) self.containerRoutes = {} self.containerInterfaces = {} self.containerNames = containerNames #string to be defined by user (e.g., in rftest2 is rfvmA, B, ... @@ -136,6 +136,13 @@ def addTest(self, cmd, method, output, **kwargs): # 'output':str(output), kwargs} pass + def setTestsParams(self, cmd, param): + ''' + Define for example self.containernames, self.mongoport, self.controllerport + for Container, Mongo, Controller classes respectively + In this case modify cmd in self.tests + ''' + pass def addTestsDefault(self): ''' @@ -176,12 +183,21 @@ def addTestsDefault(self): ''' self.TESTS.clear() - - def findFunction(self): - ''' - find packet loss percentage in ping result - and set true or false if is 100% less or not - ''' + method = 'findfp' + output = True + + for containerName in self.containerNames: + for cname,ifaceDetails in self.containerInterfaces.items(): + for iface,ipaddr in ifaceDetails.items(): + #The ContainerName and cname should not be same. + #Container1 should do ping check with ifaces on all other containers, except its own. + if cname != containerName: + cmd = "lxc-ps -n {name} ping -c3 {target}" + cmd.format(name=containerName, target = ipaddr) + addTest(cmd, method, output, name = containerName) + + #addTest(cmd, method, output, name = 'b2') + #addTest(cmd, method, output, name = 'b1') def run_tests(self): ''' diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 2d4ed61a..5ead310a 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -214,6 +214,43 @@ def __init__(self, logger): self.logger = logger self.evaluateDictionary = {} self.verifyDictionary = {} + + def findFunction(self, output): + ''' + find packet loss percentage in ping result + and set true or false if is 100% less or not + + ubuntu@b1:~$ ping -c 3 172.31.2.2 + + PING 172.31.2.2 (172.31.2.2) 56(84) bytes of data. + 64 bytes from 172.31.2.2: icmp_req=1 ttl=63 time=3.78 ms + 64 bytes from 172.31.2.2: icmp_req=2 ttl=63 time=3.60 ms + 64 bytes from 172.31.2.2: icmp_req=3 ttl=63 time=3.07 ms + + --- 172.31.2.2 ping statistics --- + 3 packets transmitted, 3 received, 0% packet loss, time 2016ms + rtt min/avg/max/mdev = 3.072/3.486/3.781/0.305 ms + + ping -c 3 8.8.8.8 + PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. + From 10.0.2.2 icmp_seq=1 Destination Net Unreachable + From 10.0.2.2 icmp_seq=2 Destination Net Unreachable + From 10.0.2.2 icmp_seq=3 Destination Net Unreachable + + --- 8.8.8.8 ping statistics --- + 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2012ms + ''' + #cmd = "sudo lxc-attach -n b1 -- /bin/ping -c 3 172.31.2.2" + #output = subprocess.Popen(cmd,stdout = subprocess.PIPE).communicate()[0] + #sp = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) + #output,error = sp.communicate() + + if ('unreachable' in output): + print False + # The packet loss when connected will usually be 0%, + # To make sure, any thing below 100% is also counted as connected. + elif('0% packet loss' in out) or ('100% packet loss' not in out): + return True def evaluate(self): ''' @@ -279,6 +316,15 @@ def verify(self): if values.find(self.tests[cmdInput]['output']) != -1: print '6' self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} + + elif self.tests[cmdInput]['method'] == 'findfp': + print '7' + #TODO : The findfp should be handled as a closure. + if self.findFunction(values) == True: + self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} + else : + self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} + #else : TODO case for custom find function. #if self.tests[inputs]['method'] == 'findfp': #This is a part of the function passed as argument from test_Connectivity file. @@ -348,7 +394,8 @@ def run_tests(self): args = parser.parse_args() testsobj = Tests() - kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True} + kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True } + #kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True, 'Connectivity':True} args = ("true",1) testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1', RFApps = ['RFServer','RFClient','RFProxy']) From 63c15b2141eabefbc3ed77194b89fbf8e80d1a9d Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 28 Aug 2015 03:11:40 +0530 Subject: [PATCH 38/43] Update README.md --- rftest/tests/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index 03b197c3..b8502f14 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -94,12 +94,12 @@ or you can run the following command to get 3.5 Issues ------ -* findTests() function in test_RF.py should be updated with parameters from configureTests dictionary * Currently Logging server logs only to RFtest.log. Should resolve the issue so that logs will be printed into Debug.log, Output.log and Error.log accordingly. -* This can possibly be a future task. But using argparse to accept huge number of parameters doesnot make sense and make the code clumsy. So I am planning to include a josn file that holds all the comman line parameters and will be used to initialise Tests class. -* Currently tests_connectivity classes's constructor is not able to take the desired number of arguments. Should fix this issue. -* As suggested, a customFind function for ping needs to be implemented and incorporated into findTests() defnition. - +* While filling the TESTS dictinary in Connectivity class, *kwargs is used to accept the additional parameters. - The implementation needs to be fixed in "addTest" defnition to accepts kwargs. +* This can possibly be a future task. But using argparse to accept huge number of parameters doesnot make sense and make the code clumsy. So I am planning to include a josn file that holds all the comman line parameters and will be used to initialise Tests class. - Still a future task. +* Currently tests_connectivity classes's constructor is not able to take the desired number of arguments. Should fix this issue. -Resolved +* As suggested, a customFind function for ping needs to be implemented and incorporated into findTests() defnition. - Implemented +* findTests() function in test_RF.py should be updated with parameters from configureTests dictionary - Resolved. License ------- From 818d224e74f2640c4d33f42e10bf28dfd37095b4 Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 28 Aug 2015 03:11:58 +0530 Subject: [PATCH 39/43] Update README.md --- rftest/tests/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index b8502f14..238b30f9 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -100,6 +100,8 @@ Issues * Currently tests_connectivity classes's constructor is not able to take the desired number of arguments. Should fix this issue. -Resolved * As suggested, a customFind function for ping needs to be implemented and incorporated into findTests() defnition. - Implemented * findTests() function in test_RF.py should be updated with parameters from configureTests dictionary - Resolved. + + License ------- From 6e5cf8b39b2fde7bf01fff1820f31d19c0fdd0a0 Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 28 Aug 2015 03:24:24 +0530 Subject: [PATCH 40/43] Update README.md --- rftest/tests/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index 238b30f9..4424bc91 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -72,6 +72,14 @@ Checking the output of testcases: Extend testcases/Implement New testcases ---------------------------------------- +* Create a file with name test_.py +* Add the to CATALOGUE dictionary. +* To run the new class, add the <:True> pair to testsToRun dictionary. +* Pass the configuration parameters through testsParams dictionary. +* In Class + + Extend class from RFUnitTests class. + + Add tests to TESTS dictionary. Use addTest() defnition to add tests to TESTS dictionary. + + Extend the runTests defnition in the new child class by calling the evaluate() verify() and analyze() functions to run the testcase. FAQ --- From 6e371f9475727bc644ab8e2040e49649cb9486ff Mon Sep 17 00:00:00 2001 From: sai krishna Date: Fri, 28 Aug 2015 03:25:39 +0530 Subject: [PATCH 41/43] Update README.md --- rftest/tests/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rftest/tests/README.md b/rftest/tests/README.md index 4424bc91..3b26e9b3 100644 --- a/rftest/tests/README.md +++ b/rftest/tests/README.md @@ -72,9 +72,9 @@ Checking the output of testcases: Extend testcases/Implement New testcases ---------------------------------------- -* Create a file with name test_.py -* Add the to CATALOGUE dictionary. -* To run the new class, add the <:True> pair to testsToRun dictionary. +* Create a file with name test_\.py +* Add the to CATALOGUE dictionary in test_RF.py. +* To run the new class, add the <\:True> pair to testsToRun dictionary. * Pass the configuration parameters through testsParams dictionary. * In Class + Extend class from RFUnitTests class. From 74b75334d28387568ff9346882e05d25aac64bd9 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 28 Aug 2015 14:04:33 +0000 Subject: [PATCH 42/43] arguments are taken as json file. Connectivity tests issue fixed. code clean up remaining --- rftest/tests/RFtest.log | 410 +++++++++++++++++++++++------- rftest/tests/arguments.json | 5 + rftest/tests/test_Connectivity.py | 13 +- rftest/tests/test_RF.py | 13 +- 4 files changed, 338 insertions(+), 103 deletions(-) create mode 100644 rftest/tests/arguments.json diff --git a/rftest/tests/RFtest.log b/rftest/tests/RFtest.log index e154aab7..ffdcc07e 100644 --- a/rftest/tests/RFtest.log +++ b/rftest/tests/RFtest.log @@ -1,22 +1,239 @@ -Aug 26 14:40:52 Test_RFProxy INFO - -Aug 26 14:40:52 Test_RFProxy INFO ================Test rfproxy class Begin================== -Aug 26 14:40:52 Test_RFProxy INFO running test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFProxy INFO running test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFProxy INFO verifying test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFProxy INFO verifying test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFProxy INFO analysing test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFProxy INFO testcase with command netstat -plant | grep6653 FAILED -Aug 26 14:40:52 Test_RFProxy INFO analysing test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFProxy INFO testcase with command ps aux | grep rfproxy FAILED -Aug 26 14:40:52 Test_Containers INFO - -Aug 26 14:40:52 Test_Containers INFO =============Test containers class Begin================== -Aug 26 14:40:52 Test_Containers INFO +++ This test case is to list down the currently 'running', 'stopped' and 'Frozen' list of containers +++ -Aug 26 14:40:52 Test_Containers INFO running test : sudo lxc-list -s -Aug 26 14:40:52 Test_Containers INFO testcase with command sudo lxc-list -s PASSED -Aug 26 14:40:52 Test_Containers INFO Status of containers -Aug 26 14:40:52 Test_Containers INFO +Aug 28 13:50:54 Test_RFProxy INFO + +Aug 28 13:50:54 Test_RFProxy INFO ================Test rfproxy class Begin================== +Aug 28 13:50:54 Test_RFProxy INFO + +Aug 28 13:50:54 Test_RFProxy INFO running test : ps aux | grep rfproxy +Aug 28 13:50:54 Test_RFProxy INFO running test : netstat -plant | grep 6653 +Aug 28 13:50:54 Test_RFProxy INFO + +Aug 28 13:50:54 Test_RFProxy INFO verifying test : ps aux | grep rfproxy +Aug 28 13:50:54 Test_RFProxy INFO verifying test : netstat -plant | grep 6653 +Aug 28 13:50:54 Test_RFProxy INFO + +Aug 28 13:50:54 Test_RFProxy INFO analysing test : ps aux | grep rfproxy +Aug 28 13:50:54 Test_RFProxy INFO testcase with command ps aux | grep rfproxy FAILED +Aug 28 13:50:54 Test_RFProxy INFO analysing test : netstat -plant | grep 6653 +Aug 28 13:50:54 Test_RFProxy INFO testcase with command netstat -plant | grep 6653 FAILED +Aug 28 13:50:54 Test_Containers INFO + +Aug 28 13:50:54 Test_Containers INFO =============Test containers class Begin================== +Aug 28 13:50:54 Test_Containers INFO +++ This test case is to list down the currently 'running', 'stopped' and 'Frozen' list of containers +++ +Aug 28 13:50:54 Test_Containers INFO running test : sudo lxc-list -s +Aug 28 13:50:54 Test_Containers INFO testcase with command sudo lxc-list -s PASSED +Aug 28 13:50:54 Test_Containers INFO Status of containers +Aug 28 13:50:54 Test_Containers INFO + + RUNNING + +FROZEN + +STOPPED + b1 + b2 + base + my-container + rfvm1 + rfvmA + rfvmB + rfvmC + rfvmD + + +Aug 28 13:50:54 Test_RFClient INFO + +Aug 28 13:50:54 Test_RFClient INFO ================Test rfclient class Begin================== +Aug 28 13:50:54 Test_RFClient INFO + +Aug 28 13:50:54 Test_RFClient INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO + +Aug 28 13:50:54 Test_RFClient INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO + +Aug 28 13:50:54 Test_RFClient INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient FAILED +Aug 28 13:50:54 Test_RFClient INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED +Aug 28 13:50:54 Test_RFClient INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 13:50:54 Test_RFClient INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED +Aug 28 13:50:54 Test_OVS INFO + +Aug 28 13:50:54 Test_OVS INFO ==========Test OVS class Begin=============== +Aug 28 13:50:54 Test_OVS INFO + +Aug 28 13:50:54 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 +Aug 28 13:50:54 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 +Aug 28 13:50:54 Test_OVS INFO running test : ps aux | grep dp0 +Aug 28 13:50:54 Test_OVS INFO + +Aug 28 13:50:54 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 +Aug 28 13:50:54 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 +Aug 28 13:50:54 Test_OVS INFO verifying test : ps aux | grep dp0 +Aug 28 13:50:54 Test_OVS INFO + +Aug 28 13:50:54 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 +Aug 28 13:50:54 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 FAILED +Aug 28 13:50:54 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 +Aug 28 13:50:54 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 FAILED +Aug 28 13:50:54 Test_OVS INFO analysing test : ps aux | grep dp0 +Aug 28 13:50:54 Test_OVS INFO testcase with command ps aux | grep dp0 FAILED +Aug 28 13:50:54 Test_RFServer INFO + +Aug 28 13:50:54 Test_RFServer INFO =============Test rfserver class Begin================ +Aug 28 13:50:54 Test_RFServer INFO + +Aug 28 13:50:54 Test_RFServer INFO running test : ps aux | grep rfserver +Aug 28 13:50:54 Test_RFServer INFO + +Aug 28 13:50:54 Test_RFServer INFO verifying test : ps aux | grep rfserver +Aug 28 13:50:54 Test_RFServer INFO + +Aug 28 13:50:54 Test_RFServer INFO analysing test : ps aux | grep rfserver +Aug 28 13:50:54 Test_RFServer INFO testcase with command ps aux | grep rfserver FAILED +Aug 28 13:50:54 Test_Mongo INFO + +Aug 28 13:50:54 Test_Mongo INFO =========Test Mongod class Begin============== +Aug 28 13:50:54 Test_Mongo INFO + +Aug 28 13:50:54 Test_Mongo INFO running test : ps aux | grep mongo +Aug 28 13:50:54 Test_Mongo INFO running test : sudo netstat -plant | grep 5056 +Aug 28 13:50:54 Test_Mongo INFO + +Aug 28 13:50:54 Test_Mongo INFO verifying test : ps aux | grep mongo +Aug 28 13:50:54 Test_Mongo INFO verifying test : sudo netstat -plant | grep 5056 +Aug 28 13:50:54 Test_Mongo INFO + +Aug 28 13:50:54 Test_Mongo INFO analysing test : ps aux | grep mongo +Aug 28 13:50:54 Test_Mongo INFO testcase with command ps aux | grep mongo FAILED +Aug 28 13:50:54 Test_Mongo INFO analysing test : sudo netstat -plant | grep 5056 +Aug 28 13:50:54 Test_Mongo INFO testcase with command sudo netstat -plant | grep 5056 FAILED +Aug 28 14:01:19 Test_RFProxy INFO + +Aug 28 14:01:19 Test_RFProxy INFO ================Test rfproxy class Begin================== +Aug 28 14:01:19 Test_RFProxy INFO + +Aug 28 14:01:19 Test_RFProxy INFO running test : ps aux | grep rfproxy +Aug 28 14:01:19 Test_RFProxy INFO running test : netstat -plant | grep 6653 +Aug 28 14:01:19 Test_RFProxy INFO + +Aug 28 14:01:19 Test_RFProxy INFO verifying test : ps aux | grep rfproxy +Aug 28 14:01:19 Test_RFProxy INFO verifying test : netstat -plant | grep 6653 +Aug 28 14:01:19 Test_RFProxy INFO + +Aug 28 14:01:19 Test_RFProxy INFO analysing test : ps aux | grep rfproxy +Aug 28 14:01:19 Test_RFProxy INFO testcase with command ps aux | grep rfproxy FAILED +Aug 28 14:01:19 Test_RFProxy INFO analysing test : netstat -plant | grep 6653 +Aug 28 14:01:19 Test_RFProxy INFO testcase with command netstat -plant | grep 6653 FAILED +Aug 28 14:01:19 Test_Containers INFO + +Aug 28 14:01:19 Test_Containers INFO =============Test containers class Begin================== +Aug 28 14:01:19 Test_Containers INFO +++ This test case is to list down the currently 'running', 'stopped' and 'Frozen' list of containers +++ +Aug 28 14:01:19 Test_Containers INFO running test : sudo lxc-list -s +Aug 28 14:01:19 Test_Containers INFO testcase with command sudo lxc-list -s PASSED +Aug 28 14:01:19 Test_Containers INFO Status of containers +Aug 28 14:01:19 Test_Containers INFO + + RUNNING + b1 + b2 + rfvm1 + +FROZEN + +STOPPED + base + my-container + rfvmA + rfvmB + rfvmC + rfvmD + + +Aug 28 14:01:19 Test_RFClient INFO + +Aug 28 14:01:19 Test_RFClient INFO ================Test rfclient class Begin================== +Aug 28 14:01:19 Test_RFClient INFO + +Aug 28 14:01:19 Test_RFClient INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO + +Aug 28 14:01:19 Test_RFClient INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO + +Aug 28 14:01:19 Test_RFClient INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient PASSED +Aug 28 14:01:19 Test_RFClient INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED +Aug 28 14:01:19 Test_RFClient INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 14:01:19 Test_RFClient INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED +Aug 28 14:01:19 Test_OVS INFO + +Aug 28 14:01:19 Test_OVS INFO ==========Test OVS class Begin=============== +Aug 28 14:01:19 Test_OVS INFO + +Aug 28 14:01:19 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 +Aug 28 14:01:19 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 +Aug 28 14:01:19 Test_OVS INFO running test : ps aux | grep dp0 +Aug 28 14:01:19 Test_OVS INFO + +Aug 28 14:01:19 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 +Aug 28 14:01:19 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 +Aug 28 14:01:19 Test_OVS INFO verifying test : ps aux | grep dp0 +Aug 28 14:01:19 Test_OVS INFO + +Aug 28 14:01:19 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 +Aug 28 14:01:19 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 PASSED +Aug 28 14:01:19 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 +Aug 28 14:01:19 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 PASSED +Aug 28 14:01:19 Test_OVS INFO analysing test : ps aux | grep dp0 +Aug 28 14:01:19 Test_OVS INFO testcase with command ps aux | grep dp0 FAILED +Aug 28 14:01:19 Test_RFServer INFO + +Aug 28 14:01:19 Test_RFServer INFO =============Test rfserver class Begin================ +Aug 28 14:01:19 Test_RFServer INFO + +Aug 28 14:01:19 Test_RFServer INFO running test : ps aux | grep rfserver +Aug 28 14:01:20 Test_RFServer INFO + +Aug 28 14:01:20 Test_RFServer INFO verifying test : ps aux | grep rfserver +Aug 28 14:01:20 Test_RFServer INFO + +Aug 28 14:01:20 Test_RFServer INFO analysing test : ps aux | grep rfserver +Aug 28 14:01:20 Test_RFServer INFO testcase with command ps aux | grep rfserver FAILED +Aug 28 14:02:05 Test_RFProxy INFO + +Aug 28 14:02:05 Test_RFProxy INFO ================Test rfproxy class Begin================== +Aug 28 14:02:05 Test_RFProxy INFO + +Aug 28 14:02:05 Test_RFProxy INFO running test : ps aux | grep rfproxy +Aug 28 14:02:05 Test_RFProxy INFO running test : netstat -plant | grep 6653 +Aug 28 14:02:05 Test_RFProxy INFO + +Aug 28 14:02:05 Test_RFProxy INFO verifying test : ps aux | grep rfproxy +Aug 28 14:02:05 Test_RFProxy INFO verifying test : netstat -plant | grep 6653 +Aug 28 14:02:05 Test_RFProxy INFO + +Aug 28 14:02:05 Test_RFProxy INFO analysing test : ps aux | grep rfproxy +Aug 28 14:02:05 Test_RFProxy INFO testcase with command ps aux | grep rfproxy FAILED +Aug 28 14:02:05 Test_RFProxy INFO analysing test : netstat -plant | grep 6653 +Aug 28 14:02:05 Test_RFProxy INFO testcase with command netstat -plant | grep 6653 FAILED +Aug 28 14:02:05 Test_Containers INFO + +Aug 28 14:02:05 Test_Containers INFO =============Test containers class Begin================== +Aug 28 14:02:05 Test_Containers INFO +++ This test case is to list down the currently 'running', 'stopped' and 'Frozen' list of containers +++ +Aug 28 14:02:05 Test_Containers INFO running test : sudo lxc-list -s +Aug 28 14:02:06 Test_Containers INFO testcase with command sudo lxc-list -s PASSED +Aug 28 14:02:06 Test_Containers INFO Status of containers +Aug 28 14:02:06 Test_Containers INFO RUNNING b1 @@ -34,79 +251,82 @@ STOPPED rfvmD -Aug 26 14:40:52 Test_RFClient INFO - -Aug 26 14:40:52 Test_RFClient INFO ================Test rfclient class Begin================== -Aug 26 14:40:52 Test_RFClient INFO running test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFClient INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO running test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFClient INFO verifying test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFClient INFO verifying test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFClient INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO analysing test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFClient INFO testcase with command netstat -plant | grep6653 FAILED -Aug 26 14:40:52 Test_RFClient INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient PASSED -Aug 26 14:40:52 Test_RFClient INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED -Aug 26 14:40:52 Test_RFClient INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFClient INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED -Aug 26 14:40:52 Test_RFClient INFO analysing test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFClient INFO testcase with command ps aux | grep rfproxy FAILED -Aug 26 14:40:52 Test_OVS INFO - -Aug 26 14:40:52 Test_OVS INFO ==========Test OVS class Begin=============== -Aug 26 14:40:52 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 -Aug 26 14:40:52 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 -Aug 26 14:40:52 Test_OVS INFO running test : ps aux | grep dp0 -Aug 26 14:40:52 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 -Aug 26 14:40:52 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 -Aug 26 14:40:52 Test_OVS INFO verifying test : ps aux | grep dp0 -Aug 26 14:40:52 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 -Aug 26 14:40:52 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 PASSED -Aug 26 14:40:52 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 -Aug 26 14:40:52 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 PASSED -Aug 26 14:40:52 Test_OVS INFO analysing test : ps aux | grep dp0 -Aug 26 14:40:52 Test_OVS INFO testcase with command ps aux | grep dp0 FAILED -Aug 26 14:40:52 Test_RFServer INFO - -Aug 26 14:40:52 Test_RFServer INFO =============Test rfserver class Begin================ -Aug 26 14:40:52 Test_RFServer INFO running test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFServer INFO running test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFServer INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO running test : ps aux | grep rfserver -Aug 26 14:40:52 Test_RFServer INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO verifying test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFServer INFO verifying test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFServer INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO verifying test : ps aux | grep rfserver -Aug 26 14:40:52 Test_RFServer INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO analysing test : ps aux | grep rfproxy -Aug 26 14:40:52 Test_RFServer INFO testcase with command ps aux | grep rfproxy FAILED -Aug 26 14:40:52 Test_RFServer INFO analysing test : netstat -plant | grep6653 -Aug 26 14:40:52 Test_RFServer INFO testcase with command netstat -plant | grep6653 FAILED -Aug 26 14:40:52 Test_RFServer INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient PASSED -Aug 26 14:40:52 Test_RFServer INFO analysing test : ps aux | grep rfserver -Aug 26 14:40:52 Test_RFServer INFO testcase with command ps aux | grep rfserver FAILED -Aug 26 14:40:52 Test_RFServer INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED -Aug 26 14:40:52 Test_RFServer INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient -Aug 26 14:40:52 Test_RFServer INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED -Aug 26 14:40:52 Test_Mongo INFO - -Aug 26 14:40:52 Test_Mongo INFO =========Test Mongod class Begin============== -Aug 26 14:40:52 Test_Mongo INFO running test : ps aux | grep mongo -Aug 26 14:40:52 Test_Mongo INFO running test : sudo netstat -plant | grep 5056 -Aug 26 14:40:52 Test_Mongo INFO verifying test : ps aux | grep mongo -Aug 26 14:40:52 Test_Mongo INFO verifying test : sudo netstat -plant | grep 5056 -Aug 26 14:40:52 Test_Mongo INFO analysing test : ps aux | grep mongo -Aug 26 14:40:52 Test_Mongo INFO testcase with command ps aux | grep mongo FAILED -Aug 26 14:40:52 Test_Mongo INFO analysing test : sudo netstat -plant | grep 5056 -Aug 26 14:40:52 Test_Mongo INFO testcase with command sudo netstat -plant | grep 5056 FAILED +Aug 28 14:02:06 Test_RFClient INFO + +Aug 28 14:02:06 Test_RFClient INFO ================Test rfclient class Begin================== +Aug 28 14:02:06 Test_RFClient INFO + +Aug 28 14:02:06 Test_RFClient INFO running test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO running test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO running test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO + +Aug 28 14:02:06 Test_RFClient INFO verifying test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO verifying test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO verifying test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO + +Aug 28 14:02:06 Test_RFClient INFO analysing test : sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO testcase with command sudo lxc-attach -n rfvm1 -- /bin/ps aux|grep rfclient PASSED +Aug 28 14:02:06 Test_RFClient INFO analysing test : sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO testcase with command sudo lxc-attach -n b1 -- /bin/ps aux|grep rfclient FAILED +Aug 28 14:02:06 Test_RFClient INFO analysing test : sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient +Aug 28 14:02:06 Test_RFClient INFO testcase with command sudo lxc-attach -n b2 -- /bin/ps aux|grep rfclient FAILED +Aug 28 14:02:06 Test_OVS INFO + +Aug 28 14:02:06 Test_OVS INFO ==========Test OVS class Begin=============== +Aug 28 14:02:06 Test_OVS INFO + +Aug 28 14:02:06 Test_OVS INFO running test : sudo ovs-vsctl show | grep dp0 +Aug 28 14:02:06 Test_OVS INFO running test : sudo ovs-dpctl show | grep dp0 +Aug 28 14:02:06 Test_OVS INFO running test : ps aux | grep dp0 +Aug 28 14:02:06 Test_OVS INFO + +Aug 28 14:02:06 Test_OVS INFO verifying test : sudo ovs-vsctl show | grep dp0 +Aug 28 14:02:06 Test_OVS INFO verifying test : sudo ovs-dpctl show | grep dp0 +Aug 28 14:02:06 Test_OVS INFO verifying test : ps aux | grep dp0 +Aug 28 14:02:06 Test_OVS INFO + +Aug 28 14:02:06 Test_OVS INFO analysing test : sudo ovs-vsctl show | grep dp0 +Aug 28 14:02:06 Test_OVS INFO testcase with command sudo ovs-vsctl show | grep dp0 PASSED +Aug 28 14:02:06 Test_OVS INFO analysing test : sudo ovs-dpctl show | grep dp0 +Aug 28 14:02:06 Test_OVS INFO testcase with command sudo ovs-dpctl show | grep dp0 PASSED +Aug 28 14:02:06 Test_OVS INFO analysing test : ps aux | grep dp0 +Aug 28 14:02:06 Test_OVS INFO testcase with command ps aux | grep dp0 FAILED +Aug 28 14:02:06 Test_RFServer INFO + +Aug 28 14:02:06 Test_RFServer INFO =============Test rfserver class Begin================ +Aug 28 14:02:06 Test_RFServer INFO + +Aug 28 14:02:06 Test_RFServer INFO running test : ps aux | grep rfserver +Aug 28 14:02:06 Test_RFServer INFO + +Aug 28 14:02:06 Test_RFServer INFO verifying test : ps aux | grep rfserver +Aug 28 14:02:06 Test_RFServer INFO + +Aug 28 14:02:06 Test_RFServer INFO analysing test : ps aux | grep rfserver +Aug 28 14:02:06 Test_RFServer INFO testcase with command ps aux | grep rfserver FAILED +Aug 28 14:02:06 Test_Connectivity INFO Test connectivity class Begin +Aug 28 14:02:06 Test_Connectivity INFO + +Aug 28 14:02:06 Test_Connectivity INFO + +Aug 28 14:02:06 Test_Connectivity INFO + +Aug 28 14:02:06 Test_Mongo INFO + +Aug 28 14:02:06 Test_Mongo INFO =========Test Mongod class Begin============== +Aug 28 14:02:06 Test_Mongo INFO + +Aug 28 14:02:06 Test_Mongo INFO running test : ps aux | grep mongo +Aug 28 14:02:06 Test_Mongo INFO running test : sudo netstat -plant | grep 5056 +Aug 28 14:02:06 Test_Mongo INFO + +Aug 28 14:02:06 Test_Mongo INFO verifying test : ps aux | grep mongo +Aug 28 14:02:06 Test_Mongo INFO verifying test : sudo netstat -plant | grep 5056 +Aug 28 14:02:06 Test_Mongo INFO + +Aug 28 14:02:06 Test_Mongo INFO analysing test : ps aux | grep mongo +Aug 28 14:02:06 Test_Mongo INFO testcase with command ps aux | grep mongo FAILED +Aug 28 14:02:06 Test_Mongo INFO analysing test : sudo netstat -plant | grep 5056 +Aug 28 14:02:06 Test_Mongo INFO testcase with command sudo netstat -plant | grep 5056 FAILED diff --git a/rftest/tests/arguments.json b/rftest/tests/arguments.json new file mode 100644 index 00000000..edf48a3b --- /dev/null +++ b/rftest/tests/arguments.json @@ -0,0 +1,5 @@ +{ + "testsToRun": {"OVS": true, "Containers": true, "Mongo": true, "RFApps": true, "Connectivity" : true}, + "configureTests" : {"Mongo" : 5056, "Containers" : "rfvmA", "Connectivity" : "rfvm1", "RFApps" : ["RFServer","RFClient","RFProxy"]}, + "testsOutputModes": {"json": false, "txt":true } +} diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index fccc46f6..57e28983 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -1,4 +1,5 @@ from test_RF import RFUnitTests +import logging import subprocess class Connectivity(RFUnitTests): @@ -128,16 +129,18 @@ def parseContainerInterfaces(self, name): ''' self.containerInterfaces[name] = getContainerInterfaces(name) - def addTest(self, cmd, method, output, **kwargs): + #def addTest(self, cmd, method, output, **kwargs): + def addTest(self, cmd, method, output): ''' add in self.tests new tests following the TEST structure ''' - #self.tests[str(cmd)] = {'method':str(method), - # 'output':str(output), kwargs} + self.tests[str(cmd)] = {'method':str(method), + 'output':str(output)} pass - def setTestsParams(self, cmd, param): + def setTestsParams(self, param): ''' + setTestsParams(Mongo, 27017) Define for example self.containernames, self.mongoport, self.controllerport for Container, Mongo, Controller classes respectively In this case modify cmd in self.tests @@ -194,7 +197,7 @@ def addTestsDefault(self): if cname != containerName: cmd = "lxc-ps -n {name} ping -c3 {target}" cmd.format(name=containerName, target = ipaddr) - addTest(cmd, method, output, name = containerName) + addTest(cmd, method, output) #addTest(cmd, method, output, name = 'b2') #addTest(cmd, method, output, name = 'b1') diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 5ead310a..6b0c26a1 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -38,7 +38,7 @@ class Tests: def __init__(self): self.testsToRun = {'OVS':True, 'Containers':False, 'RFApps':False} - self.testsParams = {'Mongo':27017, 'Containers':['rfvmA','rfvmB']}#, 'RFApps':['rfproxy','rfserver']} + self.testsParams = {'Mongo':27017, 'Containers':['rfvmA','rfvmB'], 'Connectivity': ['rfvm1','b1','b2']}#, 'RFApps':['rfproxy','rfserver']} self.outputFormat = {'txt':False, 'terminal':False} self.outputModes = {'json':False, 'raw':False} self.use_pytest = False @@ -176,6 +176,7 @@ def findTests(self): self.setUpTests[obj] = class_(self.logger) for confkey,confvalues in self.testsParams.items(): if testName == confkey: + print confvalues self.setUpTests[obj].setTestsParams(confvalues) def runTests(self): @@ -394,11 +395,17 @@ def run_tests(self): args = parser.parse_args() testsobj = Tests() + args_fp = open('arguments.json', 'r') + cmdArgs= json.load(args_fp) + print cmdArgs + kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True } #kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True, 'Connectivity':True} args = ("true",1) - testsobj.setTestsToRun(**kwargs) #args.testcases will be a dictionary that is passed. - testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1', RFApps = ['RFServer','RFClient','RFProxy']) + testsobj.setTestsToRun(**cmdArgs['testsToRun']) #args.testcases will be a dictionary that is passed. + + # testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1', RFApps = ['RFServer','RFClient','RFProxy']) + testsobj.configureTests(**cmdArgs['configureTests']) testsobj.setTestsOutputFormat() testsobj.setTestsOutputModes() #dictionary : {'json':False, 'txt':True} From 9c489aded96b41498c04cb38eedc233aa4b16fd5 Mon Sep 17 00:00:00 2001 From: anhsirksai Date: Fri, 28 Aug 2015 17:06:33 +0000 Subject: [PATCH 43/43] code cleanup --- rftest/tests/test_Connectivity.py | 38 ++++++---------- rftest/tests/test_RF.py | 75 +++++-------------------------- 2 files changed, 26 insertions(+), 87 deletions(-) diff --git a/rftest/tests/test_Connectivity.py b/rftest/tests/test_Connectivity.py index 57e28983..0971c093 100644 --- a/rftest/tests/test_Connectivity.py +++ b/rftest/tests/test_Connectivity.py @@ -36,7 +36,7 @@ def parseContainerRoutes(self, name): 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 In [36]: out1.splitlines(True)[2:] - Out[36]: + Out[36]: ['0.0.0.0 172.31.1.1 0.0.0.0 UG 100 0 0 eth0\n', '172.31.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0\n'] @@ -46,11 +46,6 @@ def parseContainerRoutes(self, name): self.containerRoutes[name] = out.splitlines(True)[2:] elif err != '': self.logger.error(name, err) - #handle storing of only routes in the self.containerRoutes dictionary. - #out = getContainerRoutes(name) - #for line in out: - # print line #handle storing of only routes in the self.containerRoutes dictionary. - def getContainerInterfaces(self, name): ''' @@ -62,29 +57,29 @@ def getContainerInterfaces(self, name): if i.split()[0] == 'lo': pass else: - dd[i.split()[0]] = i.split()[1] - + dd[i.split()[0]] = i.split()[1] + In [31]: dd Out[31]: {'eth0': '10.0.2.15/24', 'lxcbr0': '10.0.3.1/24'} - + > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' 172.31.1.2 - > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 + > sudo lxc-attach -n b1 -- /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | cut -d" " -f1 172.31.1.2 - > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' + > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' eth0 - > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' + > sudo lxc-attach -n b1 -- /sbin/ifconfig -a | sed 's/[ \t].*//;/^$/d' eth0 lo ''' listIface = [] dictIfIp = {} - #cmd = "sudo lxc-attach -n " + name + " -- /sbin/ifconfig -a | sed \'s/[ \t].*//;/^\(lo\|\)$/d\'" - cmd = "sudo lxc-attach -n rfvm1 -- /sbin/ifconfig -a | sed \'s/[ \t].*//;/^\(lo\|\)$/d\'" + cmd = "sudo lxc-attach -n " + name + " -- /sbin/ifconfig -a | sed \'s/[ \t].*//;/^\(lo\|\)$/d\'" + #cmd = "sudo lxc-attach -n rfvm1 -- /sbin/ifconfig -a | sed \'s/[ \t].*//;/^\(lo\|\)$/d\'" su = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = su.communicate() @@ -93,8 +88,8 @@ def getContainerInterfaces(self, name): listIface.append(i) dictIfIp.clear() for i in listIface: - #cmd = "sudo lxc-attach -n" + name + " -- /sbin/ifconfig " + str(i) + "| grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'" - cmd = "sudo lxc-attach -n rfvm1 -- /sbin/ifconfig " + str(i) + "| grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'" + cmd = "sudo lxc-attach -n" + name + " -- /sbin/ifconfig " + str(i) + "| grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'" + #cmd = "sudo lxc-attach -n rfvm1 -- /sbin/ifconfig " + str(i) + "| grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'" su = subprocess.Popen(cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell = True) out,err = su.communicate() dictIfIp[i] = out @@ -129,14 +124,12 @@ def parseContainerInterfaces(self, name): ''' self.containerInterfaces[name] = getContainerInterfaces(name) - #def addTest(self, cmd, method, output, **kwargs): def addTest(self, cmd, method, output): ''' add in self.tests new tests following the TEST structure ''' self.tests[str(cmd)] = {'method':str(method), 'output':str(output)} - pass def setTestsParams(self, param): ''' @@ -199,9 +192,6 @@ def addTestsDefault(self): cmd.format(name=containerName, target = ipaddr) addTest(cmd, method, output) - #addTest(cmd, method, output, name = 'b2') - #addTest(cmd, method, output, name = 'b1') - def run_tests(self): ''' basically runs methods inherited with self.tests attribute @@ -209,16 +199,16 @@ def run_tests(self): self.verify self.analyse ''' + self.containerName = ['rfvm1', 'b1', 'b2'] self.addTestsDefault() self.logger = logging.getLogger("Test_Connectivity") - self.logger.info("Test connectivity class Begin") + self.logger.info("\n") + self.logger.info("=============Test connectivity class Begin=========") self.evaluate() self.verify() self.analyse() - - class Topology(Connectivity): diff --git a/rftest/tests/test_RF.py b/rftest/tests/test_RF.py index 6b0c26a1..fd88b68f 100644 --- a/rftest/tests/test_RF.py +++ b/rftest/tests/test_RF.py @@ -7,8 +7,6 @@ import json import fnmatch import logging.handlers -#import unittest -#import pytest from subprocess import Popen, PIPE @@ -66,7 +64,6 @@ def configureTests(self, **kwargs): if kwargs: self.testsParams.clear() for key,param in kwargs.items(): - #if key in self.testsParams.keys(): self.testsParams[key] = param def setTestsOutputFormat(self, **kwargs): @@ -176,7 +173,6 @@ def findTests(self): self.setUpTests[obj] = class_(self.logger) for confkey,confvalues in self.testsParams.items(): if testName == confkey: - print confvalues self.setUpTests[obj].setTestsParams(confvalues) def runTests(self): @@ -215,7 +211,7 @@ def __init__(self, logger): self.logger = logger self.evaluateDictionary = {} self.verifyDictionary = {} - + def findFunction(self, output): ''' find packet loss percentage in ping result @@ -227,7 +223,7 @@ def findFunction(self, output): 64 bytes from 172.31.2.2: icmp_req=1 ttl=63 time=3.78 ms 64 bytes from 172.31.2.2: icmp_req=2 ttl=63 time=3.60 ms 64 bytes from 172.31.2.2: icmp_req=3 ttl=63 time=3.07 ms - + --- 172.31.2.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2016ms rtt min/avg/max/mdev = 3.072/3.486/3.781/0.305 ms @@ -237,7 +233,7 @@ def findFunction(self, output): From 10.0.2.2 icmp_seq=1 Destination Net Unreachable From 10.0.2.2 icmp_seq=2 Destination Net Unreachable From 10.0.2.2 icmp_seq=3 Destination Net Unreachable - + --- 8.8.8.8 ping statistics --- 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2012ms ''' @@ -247,8 +243,8 @@ def findFunction(self, output): #output,error = sp.communicate() if ('unreachable' in output): - print False - # The packet loss when connected will usually be 0%, + return False + # The packet loss when connected will usually be 0%, # To make sure, any thing below 100% is also counted as connected. elif('0% packet loss' in out) or ('100% packet loss' not in out): return True @@ -293,7 +289,7 @@ def verify(self): #key of self.evaluateDictionary is always equal to key of self.tests dictionary. #This is how data structure is built.Hence tests[inputs] will work. - + self.logger.info("\n") for cmdInput,outErrDict in self.evaluateDictionary.items(): self.logger.info("verifying test : %s", cmdInput) @@ -302,33 +298,21 @@ def verify(self): # If out exists, run method and validate tests['output'] with 'out' of outErrDict. # Fill in assert:True if 'output'.method = 'out' # else, assert:False. - print '2' if keys == 'err': - print '3' self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} - elif keys == 'out' : + elif keys == 'out' : if values == '': - print 3.5 self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} else: - print '4' if self.tests[cmdInput]['method'] == 'find': - print '5' if values.find(self.tests[cmdInput]['output']) != -1: - print '6' self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} - elif self.tests[cmdInput]['method'] == 'findfp': - print '7' #TODO : The findfp should be handled as a closure. if self.findFunction(values) == True: self.verifyDictionary[cmdInput] = {'assert':True, 'result':values} else : self.verifyDictionary[cmdInput] = {'assert':False, 'result':values} - - #else : TODO case for custom find function. - #if self.tests[inputs]['method'] == 'findfp': - #This is a part of the function passed as argument from test_Connectivity file. def analyse(self): ''' @@ -355,53 +339,18 @@ def run_tests(self): #pass if __name__ == '__main__': - description = 'RFTest suite, to run the tests and determine the state of system' - epilog = 'Report bugs to: https://github.com/routeflow/RouteFlow/issues' - - parser = argparse.ArgumentParser(description=description, epilog=epilog) - - #run pytest(y/n) - parser.add_argument('-p', '--pytest', default=False, type = bool, - help='Run tests with pytest(True/False)') - - #accept a list of test modules to be run. - #parser.add_argument('-tc', '--testcases', choices =['ovs','rfapps','mongo','containers'], - # help='Testcases to be run.choose form the list specified') - - parser.add_argument('-tc', '--testcases', type=json.loads, - help="Testcases to be run.enter on a dict format like :{'ovs':True, 'containers':False, 'rfapps':True}") - - #list of containers : either user gives his own container names(option:l) or chooses from the list(option:ln) - parser.add_argument('-l', '--lxc', nargs='*', - help='Lxc container name, should be given to verify, default not supported, zero or more container names accepted') - parser.add_argument('-ln', '--lxcnames', choices =['rfvm1','b1','b2','rfvmA','rfvmB'], - help='Lxc container name, should be given to verify, default not supported,to be choosen from the list') - - #list of rfapps - parser.add_argument('-rf', '--rfapps', choices = ['rfserver', 'rfproxy', 'rfclient'], - help='list of rfapps to be tested. select from the choice') - - #accept the mongodb port. default 27017 - parser.add_argument('-m', '--mongoport', default=27017, type = int, - help='port number to verify running of mongodb') - - #accept controller ports. defaults specified. - parser.add_argument('-c', '--controllerport1', default=6533, type = int, - help='rfproxy controller port1') - - parser.add_argument('-cc', '--controllerport2', default=6653, type = int, - help='rfproxy controller port2') + ''' + RFTest suite, to run the tests and determine the state of system + Report bugs to: https://github.com/routeflow/RouteFlow/issues + ''' - args = parser.parse_args() testsobj = Tests() args_fp = open('arguments.json', 'r') cmdArgs= json.load(args_fp) - print cmdArgs + #print cmdArgs - kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True } #kwargs = {'OVS':True,'Containers':True, 'Mongo':True, 'RFApps':True, 'Connectivity':True} - args = ("true",1) testsobj.setTestsToRun(**cmdArgs['testsToRun']) #args.testcases will be a dictionary that is passed. # testsobj.configureTests(Mongo = 5056, Containers = 'rfvmA', Connectivity = 'rfvm1', RFApps = ['RFServer','RFClient','RFProxy'])