[lnst] NetTestSlave: change log message handling
by Jiří Pírko
commit 0682f8bd29ff93292ff9c4a7fc0b5efcff37757f
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 12 12:20:04 2013 +0200
NetTestSlave: change log message handling
The server process now handles the logs from the commands by directing
them to its root logger which will result in the log message being
written on the tty, the log file and sent to the controller.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Slave/NetTestSlave.py | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
---
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index be85da4..5e71ba1 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -396,9 +396,9 @@ class NetTestSlave:
self._cmd_context.cleanup()
self._log_ctl.cancel_connection()
elif msg["type"] == "log":
- if not self._server_handler.send_data_to_ctl(msg):
- self._cmd_context.cleanup()
- self._log_ctl.cancel_connection()
+ logger = logging.getLogger()
+ record = logging.makeLogRecord(msg["record"])
+ logger.handle(record)
elif msg["type"] == "exception":
if msg["cmd_id"] != None:
logging.debug("Recieved an exception from command with id: %s"
10 years, 11 months
[lnst] NetTestCommand: use the new disable_logging method
by Jiří Pírko
commit 3863a6596b229f724a52985b8a7ab3de6e041f7b
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 12 12:20:03 2013 +0200
NetTestCommand: use the new disable_logging method
The NetTestCommand object only disabled the recipe handler and the
connection handler. This was incorrect as it ment that the logs from
commands wouldn't be saved on the slave machine. Also when there were
multiple commands running they would be accessing the tty without
synchronization which is probably not correct.
This commit changes the behaviour by calling the disable_logging method
which clears all the handlers to files, tty or sockets. And after
creates a new connection handler that forwards the log messages to the
main server process.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Common/NetTestCommand.py | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
---
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index b5569a5..57a9350 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -107,9 +107,8 @@ class NetTestCommand:
self._connection_pipe = self._write_pipe
- self._log_ctl.unset_recipe()
- self._log_ctl.cancel_connection()
- self._log_ctl.set_connection(self._write_pipe)
+ self._log_ctl.disable_logging()
+ self._log_ctl.set_connection(self._connection_pipe)
result = {}
try:
10 years, 11 months
[lnst] Logs: add method disable_logging
by Jiří Pírko
commit da7ff110d28ab581b162b5a7a29acdce0f459a6c
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 12 12:20:02 2013 +0200
Logs: add method disable_logging
This commit adds a new method that can be used to remove all the
handlers in the logging system. This effectively disables the logging
functionality and any log messages will be discarded.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Common/Logs.py | 26 +++++++++++++++++++-------
1 files changed, 19 insertions(+), 7 deletions(-)
---
diff --git a/lnst/Common/Logs.py b/lnst/Common/Logs.py
index f00082d..032a4af 100644
--- a/lnst/Common/Logs.py
+++ b/lnst/Common/Logs.py
@@ -78,6 +78,7 @@ class MultilineFormater(Formatter):
class LoggingCtl:
log_folder = ""
formatter = None
+ display_handler = None
recipe_handlers = (None,None)
recipe_log_path = ""
slaves = {}
@@ -101,19 +102,19 @@ class LoggingCtl:
#the display_handler will display logs in the terminal
- display_handler = logging.StreamHandler(sys.stdout)
- display_handler.setFormatter(self.formatter)
+ self.display_handler = logging.StreamHandler(sys.stdout)
+ self.display_handler.setFormatter(self.formatter)
if not debug:
- display_handler.setLevel(logging.INFO)
+ self.display_handler.setLevel(logging.INFO)
else:
if debug == 1:
- display_handler.setLevel(logging.DEBUG)
+ self.display_handler.setLevel(logging.DEBUG)
else:
- display_handler.setLevel(logging.NOTSET)
+ self.display_handler.setLevel(logging.NOTSET)
logger = logging.getLogger()
logger.setLevel(logging.NOTSET)
- logger.addHandler(display_handler)
+ logger.addHandler(self.display_handler)
def set_recipe(self, recipe_path, clean=True, expand=""):
recipe_name = os.path.splitext(os.path.split(recipe_path)[1])[0]
@@ -162,7 +163,7 @@ class LoggingCtl:
logger.removeHandler(self.slaves[name][0])
logger.removeHandler(self.slaves[name][1])
- del slaves[name]
+ del self.slaves[name]
def set_connection(self, target):
if self.transmit_handler != None:
@@ -181,6 +182,17 @@ class LoggingCtl:
logger.removeHandler(self.transmit_handler)
del self.transmit_handler
+ def disable_logging(self):
+ self.cancel_connection()
+
+ for s in self.slaves.keys():
+ self.remove_slave(s)
+
+ self.unset_recipe()
+ logger = logging.getLogger()
+ logger.removeHandler(self.display_handler)
+ self.display_handler = None
+
def _clean_folder(self, path):
try:
shutil.rmtree(path)
10 years, 11 months
[lnst] ConnectionHandler: adjust send and recv functions
by Jiří Pírko
commit 74dad390c9b525bbf4373132b7f6ae34bd99261f
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 12 12:20:01 2013 +0200
ConnectionHandler: adjust send and recv functions
When using the Connection object for communication it is unnecessary to
pickle our data as this is automatically handled by the Connection
object.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Common/ConnectionHandler.py | 12 +++++-------
1 files changed, 5 insertions(+), 7 deletions(-)
---
diff --git a/lnst/Common/ConnectionHandler.py b/lnst/Common/ConnectionHandler.py
index eb80055..8e9e548 100644
--- a/lnst/Common/ConnectionHandler.py
+++ b/lnst/Common/ConnectionHandler.py
@@ -17,16 +17,15 @@ import socket
from _multiprocessing import Connection
def send_data(s, data):
- pickled_data = cPickle.dumps(data)
- length = len(pickled_data)
-
- data_to_send = str(length) + " " + pickled_data
-
try:
if isinstance(s, socket.SocketType):
+ pickled_data = cPickle.dumps(data)
+ length = len(pickled_data)
+
+ data_to_send = str(length) + " " + pickled_data
s.sendall(data_to_send)
elif isinstance(s, Connection):
- s.send(pickled_data)
+ s.send(data)
else:
return False
except socket.error:
@@ -57,7 +56,6 @@ def recv_data(s):
data = cPickle.loads(data)
elif isinstance(s, Connection):
data = s.recv()
- data = cPickle.loads(data)
else:
return None
return data
10 years, 11 months
[lnst] NetTestCommand: fix deadlock bug
by Jiří Pírko
commit e2dcc4d126b8dee82df9a6f21e66b75905f224c4
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 12 12:20:00 2013 +0200
NetTestCommand: fix deadlock bug
This commit fixes a deadlock situation that appears in case we interrupt
a command which results in the command ending a large amount of data at
once through the communication pipe. The pipe is buffered so the write
operation starts blocking until somebody starts reading from the pipe.
However the main process that would be reading this message is waiting
in a join() call creating a deadlock.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Common/NetTestCommand.py | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
---
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index 2ce9b67..b5569a5 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -144,7 +144,6 @@ class NetTestCommand:
if os.path.exists("/proc/%d" % self._pid):
logging.debug("Interrupting background command with id \"%s\", pid \"%d\"" % (self._id, self._pid))
os.killpg(os.getpgid(self._pid), signal.SIGINT)
- self._process.join()
def kill(self):
if os.path.exists("/proc/%d" % self._pid):
10 years, 11 months
[lnst] SlavePool: test connection to slave machines
by Jiří Pírko
commit 0deed16ab3a1edea3c8664e311cbacaf8d4f3055
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 5 16:19:55 2013 +0200
SlavePool: test connection to slave machines
This commit adds a new functionality to the SlavePool class. Until now
we have used all configuration files located in the pool directory and
used them in our matching algorithm. This however won't work correctly
if some machines are turned off. In this case the matching algorithm
uses the first configuration that matches even if it contains a machine
that is turned off which will result in a failure state. Even though
there exists a match that is fully available.
This commit adds code to the SlavePool initialization where we test
connectability of each machine being added to the pool and skip machines
that are turned off.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Controller/NetTestController.py | 2 +-
lnst/Controller/SlavePool.py | 16 +++++++++++++++-
2 files changed, 16 insertions(+), 2 deletions(-)
---
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index 645ed78..ffa2bf8 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -51,7 +51,7 @@ class NetTestController:
self._msg_dispatcher = MessageDispatcher()
sp = SlavePool(config.get_option('environment', 'pool_dirs'),
- check_process_running("libvirtd"))
+ check_process_running("libvirtd"), config)
self._slave_pool = sp
self._recipe = recipe = {}
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index 8470190..c98bb24 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -19,6 +19,7 @@ import re
import copy
from xml.dom import minidom
from lnst.Common.XmlProcessing import XmlDomTreeInit
+from lnst.Common.NetUtils import test_tcp_connection
from lnst.Controller.NetTestParse import SlaveMachineParse
class SlavePool:
@@ -35,7 +36,8 @@ class SlavePool:
_allow_virtual = False
- def __init__(self, pool_dirs, allow_virtual=False):
+ def __init__(self, pool_dirs, allow_virtual=False, config=None):
+ self._config = config
self._allow_virtual = allow_virtual
for pool_dir in pool_dirs:
self.add_dir(pool_dir)
@@ -64,8 +66,20 @@ class SlavePool:
slavemachine = dom.getElementsByTagName("slavemachine")[0]
parser.parse(slavemachine)
+
+ hostname = machine["params"]["hostname"]
+ if "rpcport" in machine:
+ port = machine["params"]["rpcport"]
+ else:
+ port = self._config.get_option('environment', 'rpcport')
+ logging.info("Querying slave machine %s." % machine_id)
+ if not test_tcp_connection(hostname, port):
+ return
+
if 'libvirt_domain' not in machine['params'] \
or self._allow_virtual:
+ logging.info("Adding slave machine %s to slave pool."
+ % machine_id)
self._pool[machine_id] = machine
else:
logging.warning("libvirtd not found- Machine Pool skipping "\
10 years, 11 months
[lnst] conf-team.xml: fix slave ids, remove hwaddr
by Jiří Pírko
commit 031616fedf4c9fe18a92c94e14e6e41d4f4dcdb3
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 5 16:19:54 2013 +0200
conf-team.xml: fix slave ids, remove hwaddr
Team configuration was referencing wrong slave ids.
The hwaddr option was causing problems for networks where multiple teams
were used. All of these interfaces used the same config file meaning
there were multiple interfaces with the same hwaddr.
This commit fixes both issues.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
recipes/smoke/lib/conf-team.xml | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
---
diff --git a/recipes/smoke/lib/conf-team.xml b/recipes/smoke/lib/conf-team.xml
index 05c050a..08bc7d5 100644
--- a/recipes/smoke/lib/conf-team.xml
+++ b/recipes/smoke/lib/conf-team.xml
@@ -5,14 +5,13 @@
<options>
<option name="teamd_config">
{
- "hwaddr": "00:11:22:33:44:55",
"runner": {"name": "roundrobin"}
}
</option>
</options>
<slaves>
- <slave id="1"/>
- <slave id="2"/>
+ <slave id="if-1"/>
+ <slave id="if-2"/>
</slaves>
<addresses>
<address value="{$testip}"/>
10 years, 11 months
[lnst] conf-vlan.xml: fix id and xml error
by Jiří Pírko
commit 0352730c48ed5c09a37ef18a82529a3e2c09a964
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 5 16:19:53 2013 +0200
conf-vlan.xml: fix id and xml error
The conf-vlan.xml file contained a mismatched interface tag, and the
referenced slave id was wrong.
This commit fixes that.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
recipes/smoke/lib/conf-vlan.xml | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
---
diff --git a/recipes/smoke/lib/conf-vlan.xml b/recipes/smoke/lib/conf-vlan.xml
index 7b1b657..872c4b4 100644
--- a/recipes/smoke/lib/conf-vlan.xml
+++ b/recipes/smoke/lib/conf-vlan.xml
@@ -1,11 +1,11 @@
<netconfig>
<interface id="if-1" phys_id="dev-1" type="eth"/>
- <interface id="testiface" type="vlan"/>
+ <interface id="testiface" type="vlan">
<options>
<option name="vlan_tci" value="10"/>
</options>
<slaves>
- <slave id="1"/>
+ <slave id="if-1"/>
</slaves>
<addresses>
<address value="{$testip}"/>
10 years, 11 months
[lnst] NetConfigDevice: fix team configuration
by Jiří Pírko
commit a617f44b565e250f55bdbd602d233c94ad0d75d9
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Apr 5 16:19:52 2013 +0200
NetConfigDevice: fix team configuration
The interface for teamdctl application has changed and the team
configuration executed by lnst slaves wasn't working.
This commit fixes that.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
lnst/Slave/NetConfigDevice.py | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
---
diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py
index cd8a69e..c781e83 100644
--- a/lnst/Slave/NetConfigDevice.py
+++ b/lnst/Slave/NetConfigDevice.py
@@ -271,16 +271,18 @@ class NetConfigDeviceTeam(NetConfigDeviceGeneric):
port_netdev = self._config[slaveid]
port_name = port_netdev["name"]
teamd_port_config = get_option(port_netdev, "teamd_port_config")
+ dbus_option = "-D" if self._should_enable_dbus() else ""
if teamd_port_config:
teamd_port_config = prepare_json_str(teamd_port_config)
- exec_cmd("teamdctl %s PortConfigUpdate %s \"%s\"" % (dev_name, port_name, teamd_port_config))
+ exec_cmd("teamdctl %s %s port config update %s \"%s\"" % (dbus_option, dev_name, port_name, teamd_port_config))
NetConfigDevice(port_netdev, self._config).down()
- exec_cmd("teamdctl %s PortAdd %s" % (dev_name, port_name))
+ exec_cmd("teamdctl %s %s port add %s" % (dbus_option, dev_name, port_name))
def slave_del(self, slaveid):
dev_name = self._netdev["name"]
port_name = self._config[slaveid]["name"]
- exec_cmd("teamdctl %s PortRemove %s" % (dev_name, port_name))
+ dbus_option = "-D" if self._should_enable_dbus() else ""
+ exec_cmd("teamdctl %s %s port remove %s" % (dbus_option, dev_name, port_name))
type_class_mapping = {
"eth": NetConfigDeviceEth,
10 years, 11 months
[PATCH] Reimplementing TCPConnect and TCPListen test as a test tool in C
by Jan Tluka
The TCPConnect and TCPListen written in Python had issues that were hard
to debug. The main problem was with the test hangs during tests. Another
issue is that these tests are using threads. Since the mixing of fork and
threading is strongly discouraged in Python I rewrote these tests as test
tools tcp_connect and tcp_listen under tcp_conn directory in C language
using direct system calls for networking routines and using processes for
handling connections.
Example usage:
<command_sequence>
<define>
<alias name="my_range" value="10000-10050" />
</define>
<command machine_id="testmachine2" type="exec" from="tcp_conn"
value="./tcp_listen -p {$my_range} -a {ip(testmachine2,testifc1)} -c"
bg_id="1"/>
<command type="ctl_wait" value="3" />
<command machine_id="testmachine1" type="exec" from="tcp_conn"
value="./tcp_connect -p {$my_range} -a {ip(testmachine2,testifc1)} -c"
bg_id="2"/>
<command machine_id="testmachine1" type="exec" from="tcp_conn"
value="./tcp_listen -p {$my_range} -a {ip(testmachine1,testifc1)} -c"
bg_id="3"/>
<command type="ctl_wait" value="3" />
<command machine_id="testmachine2" type="exec" from="tcp_conn"
value="./tcp_connect -p {$my_range} -a {ip(testmachine1,testifc1)} -c"
bg_id="4"/>
<command type="ctl_wait" value="100" />
<command machine_id="testmachine1" type="intr" value="2" />
<command machine_id="testmachine2" type="intr" value="1" />
<command machine_id="testmachine2" type="intr" value="4" />
<command machine_id="testmachine1" type="intr" value="3" />
</command_sequence>
Options are common both for server and client part:
-p port_range ... specifies the range of ports to use under test
-c ... use continuous mode otherwise just do a single
connection and end
-d ... turn on debug output, use with caution since this
produces a lot of data
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
test_modules/TestTCPConnect.py | 205 ----------------------------------
test_modules/TestTCPListen.py | 198 --------------------------------
test_tools/tcp_conn/Makefile | 12 ++
test_tools/tcp_conn/lnst-setup.sh | 3 +
test_tools/tcp_conn/tcp_connect.c | 202 +++++++++++++++++++++++++++++++++
test_tools/tcp_conn/tcp_listen.c | 229 ++++++++++++++++++++++++++++++++++++++
6 files changed, 446 insertions(+), 403 deletions(-)
delete mode 100644 test_modules/TestTCPConnect.py
delete mode 100644 test_modules/TestTCPListen.py
create mode 100644 test_tools/tcp_conn/Makefile
create mode 100755 test_tools/tcp_conn/lnst-setup.sh
create mode 100644 test_tools/tcp_conn/tcp_connect.c
create mode 100644 test_tools/tcp_conn/tcp_listen.c
diff --git a/test_modules/TestTCPConnect.py b/test_modules/TestTCPConnect.py
deleted file mode 100644
index 4da2600..0000000
--- a/test_modules/TestTCPConnect.py
+++ /dev/null
@@ -1,205 +0,0 @@
-"""
-This module defines TCPConnect module
-"""
-
-__author__ = """
-jtluka(a)redhat.com (Jan Tluka)
-"""
-
-import sys
-import socket
-import errno
-from multiprocessing import Process, Value
-from signal import signal, SIGINT
-from time import sleep
-from random import randrange, sample
-import logging
-import re
-import ctypes
-from lnst.Common.TestsCommon import TestGeneric
-
-"""
-Test description:
- Test spawns client(s) connecting to TCP port(s) defined by port or
- port_range option. When connected, the client sends random bursts of
- random data to server. If cont option is set the connections are initiated
- again and data is sent to server until interrupted by the controller.
-
-Parameters:
- addr ... mandatory, address to connect to
- port ... mandatory, port to send data
- sleep ... optional, sleep time between bursts, if undefined, the bursts
- are immediate
- cont ... optional, sets continuous mode of connecting, if set connections
- are infinitely re-spawned when closed
-"""
-
-class ConnectionWorker():
- def __init__(self, host, port, sleep_time = None, continuous = None):
- self._host = host
- self._port = port
- self._sleep_time = sleep_time
- self._cont = continuous
- self._ascii = [chr(i) for i in range(0,255)]
-
- def run(self, closecon):
- loop = True
-
- while loop and not closecon.value:
- loop = (self._cont is not None)
- logging.debug("Starting connection to (%s) port %s " % (self._host,
- self._port))
-
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- except socket.error as msg:
- logging.debug(msg)
- return
-
- s.settimeout(5)
-
- try:
- s.connect((self._host, self._port))
- except socket.timeout as msg:
- logging.debug("Could not connect to %s port %s" %
- (self._host, self._port))
- s.close()
- continue
- except socket.error as msg:
- logging.debug(msg)
- s.close()
- break
-
- s.settimeout(1)
-
- for txs in range(10, randrange(20,100)):
- if closecon.value:
- logging.debug("Terminating connection on port %s" %
- self._port)
- loop = False
- s.settimeout(0)
- try:
- s.shutdown(socket.SHUT_WR)
- except:
- pass
- s.close()
- return
-
- rnd_str = "".join(sample(self._ascii, len(self._ascii)))
- while True and not closecon.value:
- try:
- data = s.sendall(rnd_str)
- except socket.timeout:
- continue
- except socket.error as msg:
- logging.debug(msg)
- s.settimeout(0)
- try:
- s.shutdown(socket.SHUT_WR)
- except:
- pass
- s.close()
- return
-
- break
-
- if (self._sleep_time):
- sleep(self._sleep_time)
-
- s.settimeout(0)
- try:
- s.shutdown(socket.SHUT_WR)
- except:
- pass
- s.close()
-
-
-class TestTCPConnect(TestGeneric):
- def _parse_options(self):
- addr = self.get_mopt("addr")
- if addr:
- self._host = addr
-
- # either port or port_range should be set
- port = self.get_opt("port")
- if port:
- self._port = int(port)
- else:
- port_range = self.get_opt("port_range")
- if port_range:
- self._port_range = port_range
- else:
- e = TestOptionMissing()
- raise e
-
- sleep_time = self.get_opt("sleep")
- if sleep_time:
- self._sleep_time = float(sleep_time)
- else:
- self._sleep_time = 0
-
- cont = self.get_opt("cont")
- if cont:
- self._cont = cont
-
- def parse_port_range(self):
- if self._port_range == None:
- return []
-
- for c in [',','-']:
- s = self._port_range.split(c)
- if len(s) == 2:
- break
-
- if len(s) != 2:
- logging.error("Port range malformed! ", self._port_range)
-
- low = int(s[0])
- high = int(s[1]) + 1
-
- return range(low, high)
-
- def _close_connections(self, signum, frame):
- logging.debug("Termination signal delivered ...")
- self._closecon.value = True
-
- def _set_interrupt_handler(self):
- signal(SIGINT, self._close_connections)
-
- def run(self):
- self._host = None
- self._port = None
- self._cont = None
- self._cw_instances = []
- self._closecon = Value(ctypes.c_bool, False, lock=True)
-
- self._set_interrupt_handler()
-
- self._parse_options()
-
- ports = []
- if self._port:
- ports.append(self._port)
- else:
- r = self.parse_port_range()
- ports.extend(r)
-
- workers = []
- for p in ports:
- cw = ConnectionWorker(self._host, p, self._sleep_time, self._cont)
- self._cw_instances.append(cw)
-
- w = Process(target=cw.run, args=(self._closecon,))
- w.start()
- workers.append(w)
-
- logging.debug("Waiting for workers ...")
- while len(workers) > 0:
- for w in workers:
- try:
- w.join()
- except OSError as e:
- continue
- workers.remove(w)
-
- logging.info("Connect: Finished ...")
diff --git a/test_modules/TestTCPListen.py b/test_modules/TestTCPListen.py
deleted file mode 100644
index eb1697b..0000000
--- a/test_modules/TestTCPListen.py
+++ /dev/null
@@ -1,198 +0,0 @@
-"""
-This module defines TCPListen module
-"""
-
-__author__ = """
-jtluka(a)redhat.com (Jan Tluka)
-"""
-
-import sys
-import socket
-import errno
-import logging
-import re
-from multiprocessing import Process, Value
-from signal import signal, SIGINT
-import ctypes
-from lnst.Common.TestsCommon import TestGeneric
-
-"""
-Test description:
- Test spawns server(s) listening for TCP connection(s) on port(s) defined by
- port or port_range options. When client connects to the port, server reads
- the data sent and close the connection when no more data is available.
- If cont option is set the connection is reopened and server reads data
- again.
-
-Parameters:
- addr ... optional, address to bind to, if undefined listen on all ifaces
- port ... mandatory, port to listen on
- cont ... optional, if set the listening port is reopened when the
- connection is closed
-"""
-
-class TestTCPListen(TestGeneric):
- def __init__(self, command):
- self._addr = None
- self._port = None
- self._cont = None
- self._closecon = Value(ctypes.c_bool, False, lock=True)
-
- TestGeneric.__init__(self, command)
-
- def _parse_options(self):
- addr = self.get_opt("addr")
- if addr:
- self._addr = addr
-
- # either port or port_range should be set
- port = self.get_opt("port")
- if port:
- self._port = int(port)
- else:
- port_range = self.get_opt("port_range")
- if port_range:
- self._port_range = port_range
- else:
- e = TestOptionMissing()
- raise e
-
- cont = self.get_opt("cont")
- if cont:
- self._cont = cont
-
- def _worker(self, host, port, connections, closecon):
- logging.debug("Starting listener (%s) on port %s " % (host, port))
-
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.settimeout(1)
- except socket.error as msg:
- logging.debug(msg)
- return
-
- try:
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind((host, port))
- s.listen(1)
- except socket.error as msg:
- logging.debug(msg)
- s.close()
- return
-
- loop = 1
-
- while (loop or self._cont) and not closecon.value:
- conn = None
- while conn == None and not closecon.value:
- try:
- conn, addr = s.accept()
- except socket.timeout as e:
- continue
- except socket.error as e:
- logging.debug(e)
- s.close()
- return
- except:
- logging.warning("Unknown exception.")
- s.close()
- return
-
- if conn:
- connections.value += 1
- conn.settimeout(1)
- else:
- continue
-
- logging.debug('Connected from ' + addr[0] + ' port:' +
- str(addr[1]))
-
- while not closecon.value:
- try:
- data = conn.recv(1024)
- except socket.timeout as e:
- continue
- except socket.error as e:
- logging.debug(e)
- if conn:
- conn.shutdown(socket.SHUT_RDWR)
- conn.close()
- s.close()
- return
-
- if not data:
- logging.debug('Client disconnected: ' + addr[0] +
- ' port:' + str(addr[1]))
- break
-
- if conn:
- try:
- conn.shutdown(socket.SHUT_RDWR)
- conn.close()
- except socket.error as msg:
- logging.debug(msg)
- s.close()
- return
-
- loop = 0
-
- s.close()
-
- def _parse_port_range(self):
- if self._port_range == None:
- return []
-
- for c in [',','-']:
- s = self._port_range.split(c)
- if len(s) == 2:
- break
-
- if len(s) != 2:
- logging.error("Port range malformed! ", self._port_range)
-
- low = int(s[0])
- high = int(s[1]) + 1
-
- return range(low, high)
-
- def _terminate(self, signum, frame):
- self._closecon.value = True
- logging.debug("Listen: terminate called")
-
- def _set_interrupt_handler(self):
- signal(SIGINT, self._terminate)
-
- def run(self):
- self._set_interrupt_handler()
-
- self._parse_options()
-
- ports = []
- if self._port:
- ports.append(self._port)
- else:
- r = self._parse_port_range()
- ports.extend(r)
-
- self.workers = []
-
- connections = Value('L', 0, lock=True)
-
- for p in ports:
- w = Process(target=self._worker, args=(self._addr, p, connections, self._closecon))
- w.start()
- self.workers.append(w)
-
-
- logging.debug("Waiting for workers ...")
- while len(self.workers) > 0:
- for w in self.workers:
- try:
- w.join()
- except:
- continue
- self.workers.remove(w)
-
- logging.info("Handled %s TCP connections." % connections.value)
-
- return self.set_pass("Handled %s TCP connections." % connections.value)
diff --git a/test_tools/tcp_conn/Makefile b/test_tools/tcp_conn/Makefile
new file mode 100644
index 0000000..2e8f5e6
--- /dev/null
+++ b/test_tools/tcp_conn/Makefile
@@ -0,0 +1,12 @@
+CC=gcc
+
+all: tcp_listen tcp_connect
+
+tcp_listen: tcp_listen.c
+ $(CC) -o tcp_listen tcp_listen.c
+
+tcp_connect: tcp_connect.c
+ $(CC) -o tcp_connect tcp_connect.c
+
+clean:
+ rm -f tcp_connect tcp_listen
diff --git a/test_tools/tcp_conn/lnst-setup.sh b/test_tools/tcp_conn/lnst-setup.sh
new file mode 100755
index 0000000..2aad486
--- /dev/null
+++ b/test_tools/tcp_conn/lnst-setup.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+make
diff --git a/test_tools/tcp_conn/tcp_connect.c b/test_tools/tcp_conn/tcp_connect.c
new file mode 100644
index 0000000..598ac80
--- /dev/null
+++ b/test_tools/tcp_conn/tcp_connect.c
@@ -0,0 +1,202 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <signal.h>
+
+
+int connection_handlers[1024];
+int handlers_count;
+
+int debug_on = 0;
+int cont = 0;
+
+int usage()
+{
+ printf("./tcp_connect -p [port_range] -a [ipaddr]\n");
+ return 0;
+}
+
+#define MSG_MAX 256
+char msg[MSG_MAX];
+
+int debug(char* msg)
+{
+ if (debug_on)
+ {
+ printf("Debug: %s\n", msg);
+ }
+}
+
+void terminate_connections(int p)
+{
+ int cc;
+
+ debug("signalled");
+ for (cc=0; cc < handlers_count; cc++)
+ {
+ snprintf(msg, MSG_MAX, "killing process %i", connection_handlers[cc]);
+ debug(msg);
+ kill(connection_handlers[cc], SIGKILL);
+ }
+}
+
+int handle_connections(char* host, int port)
+{
+ int conn_sock;
+ int remote_sock;
+ struct sockaddr_in my_addr;
+ struct sockaddr remote;
+ int end_loop = 0;
+ char data[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ char buf[21*10*strlen(data)+1];
+
+ snprintf(msg, MSG_MAX, "Starting connection on %s port %i", host, port);
+ debug(msg);
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port);
+ if (inet_aton(host, &(my_addr.sin_addr)) == 0)
+ {
+ printf("failed on inet_aton()\n");
+ return 1;
+ }
+
+ do
+ {
+ conn_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn_sock == -1)
+ {
+ perror("fail on socket()");
+ return 1;
+ }
+
+ if (connect(conn_sock, (struct sockaddr*) &my_addr, sizeof(struct sockaddr_in)) == -1)
+ {
+ perror("fail on connect");
+ return 1;
+ }
+
+ int bursts = 5*(random() % 10) + 1;
+ int b;
+ int sum = 0;
+
+ for (b=0; b < bursts; b++)
+ {
+ int parts = 20*(random() % 10) + 1;
+ int j;
+
+ sum += parts*strlen(data);
+ for (j = 0; j < parts; j++)
+ {
+ strncpy(buf + (j*strlen(data)), data, strlen(data));
+ }
+
+ int wr_rc = write(conn_sock, buf, parts * strlen(data));
+ usleep(100*(random()%100));
+ }
+
+ snprintf(msg, MSG_MAX, "sent %i bytes (bursts: %i)", sum, bursts);
+ debug(msg);
+ snprintf(msg, MSG_MAX, "closing connection on port %i", port);
+ debug(msg);
+
+ close(conn_sock);
+ } while (cont);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int p;
+ char port_str[256];
+ char str_port_start[128];
+ char str_port_end[128];
+ char host_str[16] = "\0";
+ int start_port;
+ int end_port;
+ int opt;
+ char *delimiter;
+ struct sigaction sa;
+
+ sa.sa_handler = &terminate_connections;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ handlers_count = 0;
+
+ /* collect program args */
+ while ((opt = getopt(argc, argv, "p:a:dc")) != -1) {
+ switch (opt) {
+ case 'p':
+ strncpy(port_str, optarg, 256);
+ port_str[256-1]='\0';
+ delimiter = strchr(port_str, '-');
+ if (delimiter == NULL)
+ {
+ usage();
+ }
+ strncpy(str_port_start, port_str, delimiter - port_str);
+ str_port_start[delimiter - port_str] = '\0';
+ strncpy(str_port_end, delimiter+1, strlen(port_str) - (delimiter+1-port_str));
+ start_port = atoi(str_port_start);
+ end_port = atoi(str_port_end);
+ break;
+ case 'a':
+ strncpy(host_str, optarg, 64);
+ host_str[16-1] = '\0';
+ break;
+ case 'd':
+ debug_on = 1;
+ break;
+ case 'c':
+ cont = 1;
+ break;
+ }
+ }
+
+ if (strlen(host_str) == 0)
+ {
+ usage();
+ return 1;
+ }
+
+ /* spawn process to handle every port specified */
+ for (p = start_port; p < end_port; p++){
+ if ((rc = fork()) > 0)
+ {
+ /* parent, add pid to the list */
+ connection_handlers[handlers_count++] = rc;
+ }
+ else if (rc == 0)
+ {
+ /* child */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ handle_connections(host_str, p);
+ return 0;
+ }
+ }
+
+ /* gather children */
+ int child_status;
+ int i;
+ for (i=0; i < handlers_count; i++)
+ {
+ wait(&child_status);
+ debug("worker finished");
+ }
+
+ debug("tcp_connect finished");
+
+ return 0;
+}
diff --git a/test_tools/tcp_conn/tcp_listen.c b/test_tools/tcp_conn/tcp_listen.c
new file mode 100644
index 0000000..df07f69
--- /dev/null
+++ b/test_tools/tcp_conn/tcp_listen.c
@@ -0,0 +1,229 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <signal.h>
+
+int listener_count;
+int listeners[1024];
+
+int connection_count;
+int debug_on = 0;
+int cont = 0;
+
+int usage()
+{
+ printf("./tcp_listen -p [port_range] -a [ipaddr]\n");
+ return 0;
+}
+
+int debug(char* msg)
+{
+ if (debug_on)
+ {
+ printf("Debug: %s\n", msg);
+ }
+}
+
+#define MSG_MAX 256
+char msg[MSG_MAX];
+
+void terminate_connections(int p)
+{
+ int l;
+
+ debug("signal received, killing servers");
+ for (l=0; l < listener_count; l++)
+ {
+ snprintf(msg, MSG_MAX, "killing process %i", listeners[l]);
+ debug(msg);
+ kill(listeners[l], SIGKILL);
+ }
+}
+
+void connection_counter(int p)
+{
+ debug("increase counter");
+ connection_count += 1;
+}
+
+int handle_connections(char* host, int port)
+{
+ int listen_sock;
+ struct sockaddr_in my_addr;
+ socklen_t my_addr_size = sizeof(my_addr);
+ int remote_sock;
+ struct sockaddr_in remote;
+ socklen_t remote_size = sizeof(remote);
+ char data[256];
+
+ snprintf(msg, MSG_MAX, "Starting listener on %s port %i", host, port);
+ debug(msg);
+
+ bzero(&my_addr, my_addr_size);
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port);
+ if (inet_aton(host, &(my_addr.sin_addr)) == 0)
+ {
+ printf("failed on inet_aton()\n");
+ return 1;
+ }
+
+ if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ perror("fail on socket creation");
+ return 1;
+ }
+
+ if (bind(listen_sock, (struct sockaddr*) &my_addr, sizeof(struct sockaddr_in)))
+ {
+ perror("fail on bind");
+ return 1;
+ }
+
+ if (listen(listen_sock, 0))
+ {
+ perror("fail on listen");
+ return 1;
+ }
+
+ do
+ {
+ struct in_addr ra = remote.sin_addr;
+ char host_address_str[256];
+ ssize_t read_rc;
+
+ /* handle single connection */
+ bzero(&remote, remote_size);
+ remote_sock = accept(listen_sock, (struct sockaddr*) &remote, &remote_size);
+ if (remote_sock == -1)
+ {
+ perror("failure on accept");
+ close(listen_sock);
+ return 1;
+ }
+
+ inet_ntop(AF_INET, &ra, host_address_str, 255);
+ snprintf(msg, MSG_MAX, "accepted connection from host %s port %i", host_address_str, port);
+ debug(msg);
+
+ int sum = 0;
+ while (read_rc = read(remote_sock, &data, 256))
+ {
+ sum += read_rc;
+ }
+
+ snprintf(msg, MSG_MAX, "connection closed, read %d bytes (%i)", sum, port);
+ debug(msg);
+ close(remote_sock);
+ kill(getppid(), SIGUSR1);
+ } while (cont);
+
+ close(listen_sock);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int p;
+ char port_str[256];
+ char str_port_start[128];
+ char str_port_end[128];
+ char host_str[16] = "\0";
+ int start_port;
+ int end_port;
+ int opt;
+ char *delimiter;
+ struct sigaction sa;
+ struct sigaction sa2;
+
+ sa.sa_handler = &terminate_connections;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa2.sa_handler = &connection_counter;
+ sigaction(SIGUSR1, &sa2, NULL);
+
+ /* collect program args */
+ while ((opt = getopt(argc, argv, "p:a:dc")) != -1) {
+ switch (opt) {
+ case 'p':
+ strncpy(port_str, optarg, 256);
+ port_str[256-1]='\0';
+ delimiter = strchr(port_str, '-');
+ if (delimiter == NULL)
+ {
+ usage();
+ }
+ strncpy(str_port_start, port_str, delimiter - port_str);
+ str_port_start[delimiter - port_str] = '\0';
+ strncpy(str_port_end, delimiter+1, strlen(port_str) - (delimiter+1-port_str));
+ start_port = atoi(str_port_start);
+ end_port = atoi(str_port_end);
+ break;
+ case 'a':
+ strncpy(host_str, optarg, 64);
+ host_str[16-1] = '\0';
+ break;
+ case 'd':
+ debug_on = 1;
+ break;
+ case 'c':
+ cont = 1;
+ }
+ }
+
+ if (strlen(host_str) == 0)
+ {
+ usage();
+ return 1;
+ }
+
+ listener_count = 0;
+ /* spawn process to handle every port specified */
+ for (p = start_port; p < end_port; p++){
+ if ((rc = fork()) > 0)
+ {
+ /* parent, add pid to the list */
+ listeners[listener_count++] = rc;
+ }
+ else if (rc == 0)
+ {
+ /* child */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ /* run the main processing loop */
+ handle_connections(host_str, p);
+ return 0;
+ }
+ }
+
+ /* gather children */
+ int child_status;
+ int i;
+ for (i=0; i < listener_count; i++)
+ {
+ int rc = -1;
+ while (wait(&child_status) == -1 && errno == EINTR)
+ {
+ ;
+ }
+ debug("worker finished");
+ }
+
+ debug("tcp_listener finished");
+
+ printf("handled %i connections\n", connection_count);
+
+ return 0;
+}
--
1.7.11.7
10 years, 11 months