[lnst] LoggingServer: Removing date subdir in slave logs
by Jiří Pírko
commit 3d83a985563069ee58989ea2f0affa11cc34a76a
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Thu Jun 21 11:24:05 2012 +0200
LoggingServer: Removing date subdir in slave logs
This commit removes $date subdirectory from slave logging file
hierarchy. It's not necessary, because there's always only one
directory anyway and it could also be misleading, because the
$date doesn't match dates from the slaves. See the following
examples:
The previous state (path to slave logs):
Logs/2012-06-19_12:05:33/libteam/10.34.1.171/2012-06-19_12:06:16/info
Current state:
Logs/2012-06-19_12:05:33/libteam/10.34.1.171/info
Logs/<controller exec time>/<recipe name>/<slave IP>/<log file>
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
Common/LoggingServer.py | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
---
diff --git a/Common/LoggingServer.py b/Common/LoggingServer.py
index 971414a..4bd4f19 100644
--- a/Common/LoggingServer.py
+++ b/Common/LoggingServer.py
@@ -75,9 +75,8 @@ class LoggingServer:
if e.errno != 17:
raise
logger = logging.getLogger(address)
- Logs(self.debug, False, logger,
- log_root=slave_root_path,
- to_display=False)
+ Logs(self.debug, False, logger, log_root=slave_root_path,
+ to_display=False, date="")
return (logger, address, sock[0])
11 years, 9 months
[lnst] Logs: Fixing unused constructor parameter
by Jiří Pírko
commit ee93d221c3082a2bee33a07e2844ae993b1b7ec8
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Thu Jun 21 11:23:56 2012 +0200
Logs: Fixing unused constructor parameter
The `date' parameter of Logs class constructor was unused. Now it's
taken into account correctly and the specified date value is used
instead of datetime.now() value.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
Common/Logs.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
---
diff --git a/Common/Logs.py b/Common/Logs.py
index 44d4f98..961e158 100644
--- a/Common/Logs.py
+++ b/Common/Logs.py
@@ -197,7 +197,10 @@ class Logs:
cls.logFolder = os.path.dirname(sys.argv[0])
cls.logger = logger
cls.debug = debug
- cls.date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
+ if date is None:
+ cls.date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
+ else:
+ cls.date = date
cls.nameExtend = nameExtend
cls.root_path = cls.prepare_logging(debug, waitForNet,
recipe_path, to_display)
11 years, 9 months
[lnst] nettestctl: Fixing logging location bug
by Jiří Pírko
commit 6f5e235a8e8c69fe11aee20dcf140e5ea382357b
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Thu Jun 21 11:23:42 2012 +0200
nettestctl: Fixing logging location bug
When executing multiple recipes in a recipe set, the line
INFO: Processing recipe file "example_recipes/rset/mcast.xml"
that indicates execution of another recipe was printed into
the log file of previous recipe in the set.
After this modification the line is put into the top-level log
for messages related to the controler.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
nettestctl.py | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
---
diff --git a/nettestctl.py b/nettestctl.py
index 614f2fc..ec99d73 100755
--- a/nettestctl.py
+++ b/nettestctl.py
@@ -143,6 +143,7 @@ def main():
summary.append(get_recipe_result(args, recipe_file,
remoteexec, cleanup,
res_serializer))
+ Logs.set_logging_root_path(clean=False)
else:
summary.append(get_recipe_result(args, recipe_path,
remoteexec, cleanup, res_serializer))
11 years, 9 months
[lnst] Tests: Adding new test - PacketAssert
by Jiří Pírko
commit 734e2cf4700b6088df6566648b9de33e49e76884
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Thu Jun 21 11:23:08 2012 +0200
Tests: Adding new test - PacketAssert
This commit introduces a new test - packet assert, that can be used to
test if a specific amount of packets with certain properties were
transfered through some interface.
Packet properties can be verified through the pcap "filter" option or
through greping tcpdump output by using "grep-for" option (grep-for can
be used multiple times.
Here's a quick examle:
<command type="test" value="PacketAssert" bg_id="1">
<options>
<option name="interface" value="br0" />
<option name="filter" value="dst port 1234" />
<option name="grep_for" value="IP" />
<option name="min" value="1" />
<option name="max" value="5" />
</options>
</command>
<command type="exec" value="sleep 3" />
<command type="intr" value="1" />
This assert evaluates true if one to five IP packets with destination
port of 1234 are transfered through interface br0 within the time period
of sleep (i.e. 3 seconds).
PacketAssert must always be run in background. It is evaluated at the
time of interruption with "intr". It will not evaluate correctly in
case the command is killed (with "kill")!
One example recipe to demonstrate it's capabilities was added as well.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
Tests/TestPacketAssert.py | 140 +++++++++++++++++++++++++++++++++++++
example_recipes/packet_assert.xml | 15 ++++
2 files changed, 155 insertions(+), 0 deletions(-)
---
diff --git a/Tests/TestPacketAssert.py b/Tests/TestPacketAssert.py
new file mode 100644
index 0000000..5f869db
--- /dev/null
+++ b/Tests/TestPacketAssert.py
@@ -0,0 +1,140 @@
+"""
+Test if packets were transfered through some interface correctly.
+This test is using tcpdump.
+
+Copyright 2012 Red Hat, Inc.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+rpazdera(a)redhat.com (Radek Pazdera)
+"""
+
+import logging
+import subprocess
+import re
+import signal
+from Common.TestsCommon import TestGeneric
+
+class TestPacketAssert(TestGeneric):
+ """ Assert for number of incomming/outgoing packets
+ Capturing backend of this class is provided by
+ tcpdump(8).
+ """
+
+ _cmd = ""
+ _tcpdump = None
+ _grep_filters = []
+
+ _min_cond = 1
+ _max_cond = None
+
+ _num_recv = 0
+
+ def _set_interrupt_handler(self):
+ signal.signal(signal.SIGINT, self._interrupt_handler)
+ signal.signal(signal.SIGTERM, self._interrupt_handler)
+
+ def _interrupt_handler(self, signum, frame):
+ """ Kill tcpdump when interrupted """
+ self._tcpdump.terminate()
+
+ def _prepare_grep_filters(self):
+ """ Parse `grep_for' test options """
+ filters = self.get_multi_opt("grep_for")
+
+ for filt in filters:
+ if filt != None:
+ self._grep_filters.append(filt)
+
+ def _prepare_conditions(self):
+ """ Parse `min' and `max' """
+ min_packets = self.get_opt("min")
+ max_packets = self.get_opt("max")
+
+ if min_packets != None:
+ self._min_cond = int(min_packets)
+
+ if max_packets != None:
+ self._max_cond = int(max_packets)
+
+ def _compose_cmd(self):
+ """ Create a command from the recipe options """
+ cmd = ""
+
+ interface = self.get_mopt("interface")
+ pcap_filter = self.get_mopt("filter")
+
+ cmd = "tcpdump -p -nn -i %s \"%s\"" % (interface, pcap_filter)
+ self._cmd = cmd
+
+ def _execute_tcpdump(self):
+ """ Start tcpdump in the background """
+ cmd = self._cmd
+ proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ self._tcpdump = proc
+
+ def _process_captured_line(self, line):
+ """ Apply filters and see if the packet passed them """
+ if len(self._grep_filters):
+ for filt in self._grep_filters:
+ if not re.search(filt, line):
+ return
+
+ self._num_recv += 1
+
+ def _evaluate_results(self):
+ """ Compare results with the conditions """
+ num = self._num_recv
+ if num >= self._min_cond:
+ if self._max_cond != None:
+ return num <= self._max_cond
+ return True
+ return False
+
+ def run(self):
+ self._set_interrupt_handler()
+
+ self._prepare_grep_filters()
+ self._prepare_conditions()
+ self._compose_cmd()
+ self._execute_tcpdump()
+
+ logging.info("Capturing started")
+
+ line = ""
+ tcpdump_output = self._tcpdump.stdout
+ while True:
+ if self._tcpdump.poll() != None:
+ if self._tcpdump.returncode > 0:
+ raise Exception("tcpdump terminated with error")
+ else:
+ break
+
+ try:
+ next_line = tcpdump_output.readline()
+ except IOError: # Interrupted system call
+ continue
+
+ if next_line == "":
+ continue
+
+ next_line = next_line.strip("\n")
+
+ if re.match("[0-9]+\:[0-9]+\:[0-9\.]+", next_line) and line != "":
+ self._process_captured_line(line)
+ line = next_line
+ else:
+ line += next_line
+
+ logging.info("Capturing finished. Received %d packets", self._num_recv)
+ res = {"received": self._num_recv,
+ "min": self._min_cond,
+ "max": self._max_cond}
+
+ if self._evaluate_results():
+ return self.set_pass(res)
+
+ return self.set_fail("PacketAssert failed!", res)
diff --git a/example_recipes/packet_assert.xml b/example_recipes/packet_assert.xml
new file mode 100644
index 0000000..11d20b7
--- /dev/null
+++ b/example_recipes/packet_assert.xml
@@ -0,0 +1,15 @@
+<nettestrecipe>
+ <command_sequence>
+ <command type="test" value="PacketAssert" bg_id="1">
+ <options>
+ <option name="interface" value="br0" />
+ <option name="filter" value="" />
+ <option name="grep_for" value="IP" />
+ <option name="min" value="1" />
+ <option name="max" value="5" />
+ </options>
+ </command>
+ <command type="exec" value="sleep 3" />
+ <command type="intr" value="1" />
+ </command_sequence>
+</nettestrecipe>
11 years, 9 months
libvirt integration XMLs discussion
by Radek Pazdera
Hi,
I'm trying to design some new options that will be needed in
LNST recipes for the libvirt integration. Basically, we need to
reflect these new things:
1. Virtual machines - How to mark a virtual machine?
The first point could be easily achievable through one
additional parameter to the <info> tag in netmachineconfig:
<info hostname="10.20.1.5" libvirt_domain="Fedora16"
rootpass="foobar" />
If libvirt_domain is set, the machine is virtual.
2. Detecting/creating controller interface
This could depend on a presence of a hostname. We could either
use existing controller interface set up manually (in case
hostname is present) or let LNST create it's own.
3. Detecting/creating test interfaces
As far as I know, there's no direct way of listing network
interfaces present on a libvirt domain through virsh. Maybe
it's possible to parse it out from the XML dump, but I don't
know if that contains also dynamically attached ones.
In my opinion it would make things easier to mark devices that
will be temporarily created. For instance:
<netdevice id="1" type="eth" phys_id="1" dynamic="true"
hwaddr="52:54:00:2E:D5:A8"/>
4. Virtual networks / connection points
The last thing necessary is to define connections between
the interfaces to form a network topology.
This is complicated by the fact, that when attaching a device
through virsh attach-interface, you need to specify the libvirt
network to which the device will be connected. And the virtual
network or a bridge must already exist at that time.
There's a couple of options here. We could define "connections"
before we start to define machines:
<connections>
<connection name="abc" type="bridge|direct" />
</connections>
And let machine connect to these connections. Direct connection
could be done by the new kernel driver, bridge would use the
libvirt's virtual networks. Machines would refer to them like
this:
<netdevice id="1" type="eth" phys_id="1" dynamic="true"
connection="abc" />
This seems kind of straight-forward to me. The other approach
doesn't work with named connections, it forms the connection
relation directly. For instance:
<connections>
<connection source_machine="1" source_interface="1"
target_machine="2" target_interface="1" />
</connections>
This would be limited to direct connections only (which isn't
that much of a problem). It would also have to be placed before
the machine configs, so controller can determine what bridges
have to exist during the device attaching, which is a little
strange.
What do you think about this? Do you see some better way? I might
have missed something ...
Radek
11 years, 9 months
[PATCH] nettestctl: New CLI option --packet_capture (-p)
by Radek Pazdera
From: Radek Pazdera <rpazdera(a)redhat.com>
This commit introduces new CLI `-p' option to nettestctl.py.
If enabled (it is off by default), network communication from all
test interfaces (excluding controller ports) are logged to hard-drive.
These logs are transfered to controller machine when the recipe
exection is over. In case of an error at the slave side, the dump
files are still transfered, but they will not be transfered when
the controller is killed during its execution.
Log files are in binary *.pcap format and can be found in the `Logs'
directory with the slave log files. For instance:
Logs/2012-06-20_18:56:42/mcast/10.34.1.236/1.pcap
The path contains:
1. date of execution - 2012-06-20_18:56:42
2. recipe name - mcast
3. slave hostname - 10.34.1.236
4. capture file name - 1.pcap
The naming convention of the capture file is <id>.pcap, where <id>
is a netdevice id from machine's netconfig.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
Common/Logs.py | 5 +++
Common/PacketCapture.py | 64 ++++++++++++++++++++++++++++++++++++++++
NetTest/NetTestController.py | 66 ++++++++++++++++++++++++++++++++++++++++--
NetTest/NetTestSlave.py | 31 +++++++++++++++++++
nettestctl.py | 37 ++++++++++++++++-------
5 files changed, 188 insertions(+), 15 deletions(-)
create mode 100644 Common/PacketCapture.py
diff --git a/Common/Logs.py b/Common/Logs.py
index 961e158..ac0d5d3 100644
--- a/Common/Logs.py
+++ b/Common/Logs.py
@@ -262,6 +262,11 @@ class Logs:
@classmethod
+ def get_logging_root_path(cls):
+ return cls.root_path
+
+
+ @classmethod
def prepare_logging(cls, debug=0, waitForNet=False,
recipe_path=None, to_display=True):
"""
diff --git a/Common/PacketCapture.py b/Common/PacketCapture.py
new file mode 100644
index 0000000..8cfad17
--- /dev/null
+++ b/Common/PacketCapture.py
@@ -0,0 +1,64 @@
+"""
+This module contains tools for capturing packets within LNST.
+
+Copyright 2012 Red Hat, Inc.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+rpazdera(a)redhat.com (Radek Pazdera)
+"""
+
+import logging
+import subprocess
+
+class PacketCapture:
+ """ Capture/handle traffic that goes through a specific
+ network interface. Capturing backend of this class
+ is provided by tcpdump(8).
+ """
+
+ _cmd = ""
+ _tcpdump = None
+
+ _devname = None
+ _file = None
+ _filter = None
+
+ def set_interface(self, devname):
+ self._devname = devname
+
+ def set_output_file(self, file_path):
+ self._file = file_path
+
+ def set_filter(self, filt):
+ self._filter = filt
+
+ def start(self):
+ self._run()
+
+ def stop(self):
+ """ Send SIGTERM to the background instance of
+ tcpdump.
+ """
+ self._tcpdump.terminate()
+
+ def _compose_cmd(self):
+ """ Create a command from the options """
+ interface = self._devname
+ output_file = self._file
+ pcap_filter = self._filter
+
+ self._cmd = "tcpdump -p -i %s -w %s \"%s\"" % (interface, output_file,
+ pcap_filter)
+
+ def _execute_tcpdump(self):
+ """ Start tcpdump in the background """
+ cmd = self._cmd
+ self._tcpdump = subprocess.Popen(cmd, shell=True, stdout=None,
+ stderr=None)
+
+ def _run(self):
+ self._compose_cmd()
+ self._execute_tcpdump()
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py
index c787204..a1b4dcf 100644
--- a/NetTest/NetTestController.py
+++ b/NetTest/NetTestController.py
@@ -13,6 +13,9 @@ jpirko(a)redhat.com (Jiri Pirko)
import logging
import socket
+import os
+from Common.Logs import Logs
+from Common.SshUtils import scp_from_remote
from pprint import pprint, pformat
from Common.XmlRpc import ServerProxy
from NetTestParse import NetTestParse
@@ -32,6 +35,7 @@ class NetTestController:
self._remoteexec = remoteexec
self._docleanup = cleanup
self._res_serializer = res_serializer
+ self._remote_capture_files = {}
def _get_machineinfo(self, machine_id):
return self._recipe["machines"][machine_id]["info"]
@@ -211,8 +215,31 @@ class NetTestController:
self._cleanup()
return True
- def run_recipe(self):
+ def run_recipe(self, packet_capture=False):
self._prepare()
+
+ if packet_capture:
+ self._start_packet_capture()
+
+ err = None
+ try:
+ res = self._run_recipe()
+ except e:
+ err = e
+
+ if packet_capture:
+ self._stop_packet_capture()
+ self._gather_capture_files()
+
+ if self._docleanup:
+ self._cleanup()
+
+ if not err:
+ return res
+ else:
+ raise err
+
+ def _run_recipe(self):
for sequence in self._recipe["sequences"]:
res = self._run_command_sequence(sequence)
@@ -223,10 +250,43 @@ class NetTestController:
if not res:
break
- if self._docleanup:
- self._cleanup()
return res
+ def _start_packet_capture(self):
+ logging.info("Starting packet capture")
+ for machine_id in self._recipe["machines"]:
+ rpc = self._get_machinerpc(machine_id)
+ capture_files = rpc.start_packet_capture("")
+ self._remote_capture_files[machine_id] = capture_files
+
+ def _stop_packet_capture(self):
+ logging.info("Stopping packet capture")
+ for machine_id in self._recipe["machines"]:
+ rpc = self._get_machinerpc(machine_id)
+ rpc.stop_packet_capture()
+
+ def _gather_capture_files(self):
+ logging_root = Logs.get_logging_root_path()
+ logging_root = os.path.abspath(logging_root)
+ logging.info("Retrieving capture files from slaves")
+ for machine_id in self._recipe["machines"]:
+ hostname = self._recipe["machines"][machine_id]['info']['hostname']
+ rootpass = self._recipe["machines"][machine_id]['info']['rootpass']
+
+ slave_logging_dir = os.path.join(logging_root, hostname)
+ try:
+ os.mkdir(slave_logging_dir)
+ except OSError, e:
+ if e.errno != 17:
+ raise
+
+ capture_files = self._remote_capture_files[machine_id]
+ for remote_path in capture_files:
+ filename = os.path.basename(remote_path)
+ local_path = os.path.join(slave_logging_dir, filename)
+ scp_from_remote(hostname, "22", "root", rootpass,
+ remote_path, local_path)
+
def eval_expression_recipe(self, expr):
self._prepare()
value = eval("self._recipe%s" % expr)
diff --git a/NetTest/NetTestSlave.py b/NetTest/NetTestSlave.py
index 06654be..955c28e 100644
--- a/NetTest/NetTestSlave.py
+++ b/NetTest/NetTestSlave.py
@@ -14,6 +14,8 @@ jpirko(a)redhat.com (Jiri Pirko)
from Common.Logs import Logs
import signal
import select, logging
+import os
+from Common.PacketCapture import PacketCapture
from Common.XmlRpc import Server
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
from NetConfig.NetConfig import NetConfig
@@ -29,6 +31,7 @@ class NetTestSlaveXMLRPC:
'''
def __init__(self):
self._netconfig = None
+ self._packet_captures = {}
def hello(self):
return "hello"
@@ -55,6 +58,34 @@ class NetTestSlaveXMLRPC:
self.__init__()
return True
+ def start_packet_capture(self, filt):
+ logging_dir = Logs.get_logging_root_path()
+ logging_dir = os.path.abspath(logging_dir)
+ netconfig = self._netconfig.dump_config()
+
+ files = []
+ for dev_id, dev_spec in netconfig.iteritems():
+ dump_file = os.path.join(logging_dir, "%s.pcap" % dev_id)
+ files.append(dump_file)
+
+ pcap = PacketCapture()
+ pcap.set_interface(dev_spec["name"])
+ pcap.set_output_file(dump_file)
+ pcap.set_filter(filt)
+ pcap.start()
+
+ self._packet_captures[dev_id] = pcap
+
+ return files
+
+ def stop_packet_capture(self):
+ netconfig = self._netconfig.dump_config()
+ for dev_id in netconfig.keys():
+ pcap = self._packet_captures[dev_id]
+ pcap.stop()
+
+ return True
+
def run_command(self, command):
try:
return NetTestCommand(command).run()
diff --git a/nettestctl.py b/nettestctl.py
index ec99d73..f1511df 100755
--- a/nettestctl.py
+++ b/nettestctl.py
@@ -32,6 +32,9 @@ def usage():
print "ACTION = [run | dump | all_dump | config_only | eval EXPR]"
print ""
print " -d, --debug emit debugging messages"
+ print " -p, --packet_capture capture and log all ongoing\n" \
+ " network communication during\n" \
+ " the test"
print " -h, --help print this message"
print " -r, --recipe=FILE use this net test recipe"
print " -e, --remoteexec transfer and execute\n" \
@@ -41,13 +44,14 @@ def usage():
print " -x, --result=FILE file to write xml_result"
sys.exit()
-def process_recipe(args, file_path, remoteexec, cleanup, res_serializer):
+def process_recipe(args, file_path, remoteexec, cleanup,
+ res_serializer, packet_capture):
nettestctl = NetTestController(os.path.realpath(file_path),
remoteexec=remoteexec, cleanup=cleanup,
res_serializer=res_serializer)
action = args[0]
if action == "run":
- return nettestctl.run_recipe()
+ return nettestctl.run_recipe(packet_capture)
elif action == "dump":
return nettestctl.dump_recipe()
elif action == "all_dump":
@@ -73,12 +77,15 @@ def print_summary(summary):
logging.info("*%s* %s" % (res, recipe_file))
logging.info("=====================================================")
-def get_recipe_result(args, file_path, remoteexec, cleanup, res_serializer):
+def get_recipe_result(args, file_path, remoteexec, cleanup,
+ res_serializer, packet_capture):
res_serializer.add_recipe(file_path)
Logs.set_logging_root_path(file_path)
- loggingServer = LoggingServer(LoggingServer.DEFAULT_PORT, Logs.root_path, Logs.debug)
+ loggingServer = LoggingServer(LoggingServer.DEFAULT_PORT,
+ Logs.root_path, Logs.debug)
loggingServer.start()
- res = process_recipe(args, file_path, remoteexec, cleanup, res_serializer)
+ res = process_recipe(args, file_path, remoteexec, cleanup,
+ res_serializer, packet_capture)
loggingServer.stop()
return ((file_path, res))
@@ -89,8 +96,9 @@ def main():
try:
opts, args = getopt.getopt(
sys.argv[1:],
- "dhr:ecx:",
- ["debug", "help", "recipe=", "remoteexec", "cleanup", "result"]
+ "dhr:ecx:p",
+ ["debug", "help", "recipe=", "remoteexec", "cleanup", "result=",
+ "packet_capture"]
)
except getopt.GetoptError, err:
print str(err)
@@ -102,6 +110,7 @@ def main():
remoteexec = False
cleanup = False
result_path = None
+ packet_capture = False
for opt, arg in opts:
if opt in ("-d", "--debug"):
debug += 1
@@ -115,6 +124,9 @@ def main():
cleanup = True
elif opt in ("-x", "--result"):
result_path = arg
+ elif opt in ("-p", "--packet_capture"):
+ packet_capture = True
+
Logs(debug)
@@ -140,13 +152,14 @@ def main():
recipe_file = os.path.join(recipe_path, f)
if re.match(r'^.*\.xml$', recipe_file):
logging.info("Processing recipe file \"%s\"" % recipe_file)
- summary.append(get_recipe_result(args, recipe_file,
- remoteexec, cleanup,
- res_serializer))
+ summary.append(get_recipe_result(args, recipe_file, remoteexec,
+ cleanup, res_serializer,
+ packet_capture))
Logs.set_logging_root_path(clean=False)
else:
- summary.append(get_recipe_result(args, recipe_path,
- remoteexec, cleanup, res_serializer))
+ summary.append(get_recipe_result(args, recipe_path, remoteexec,
+ cleanup, res_serializer,
+ packet_capture))
Logs.set_logging_root_path(clean=False)
print_summary(summary)
--
1.7.7.6
11 years, 9 months
[PATCH] LoggingServer: Removing date subdir in slave logs
by Radek Pazdera
From: Radek Pazdera <rpazdera(a)redhat.com>
This commit removes $date subdirectory from slave logging file
hierarchy. It's not necessary, because there's always only one
directory anyway and it could also be misleading, because the
$date doesn't match dates from the slaves. See the following
examples:
The previous state (path to slave logs):
Logs/2012-06-19_12:05:33/libteam/10.34.1.171/2012-06-19_12:06:16/info
Current state:
Logs/2012-06-19_12:05:33/libteam/10.34.1.171/info
Logs/<controller exec time>/<recipe name>/<slave IP>/<log file>
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
Common/LoggingServer.py | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/Common/LoggingServer.py b/Common/LoggingServer.py
index 971414a..4bd4f19 100644
--- a/Common/LoggingServer.py
+++ b/Common/LoggingServer.py
@@ -75,9 +75,8 @@ class LoggingServer:
if e.errno != 17:
raise
logger = logging.getLogger(address)
- Logs(self.debug, False, logger,
- log_root=slave_root_path,
- to_display=False)
+ Logs(self.debug, False, logger, log_root=slave_root_path,
+ to_display=False, date="")
return (logger, address, sock[0])
--
1.7.7.6
11 years, 9 months
[PATCH] Logs: Fixing unused constructor parameter
by Radek Pazdera
From: Radek Pazdera <rpazdera(a)redhat.com>
The `date' parameter of Logs class constructor was unused. Now it's
taken into account correctly and the specified date value is used
instead of datetime.now() value.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
Common/Logs.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/Common/Logs.py b/Common/Logs.py
index 44d4f98..961e158 100644
--- a/Common/Logs.py
+++ b/Common/Logs.py
@@ -197,7 +197,10 @@ class Logs:
cls.logFolder = os.path.dirname(sys.argv[0])
cls.logger = logger
cls.debug = debug
- cls.date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
+ if date is None:
+ cls.date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
+ else:
+ cls.date = date
cls.nameExtend = nameExtend
cls.root_path = cls.prepare_logging(debug, waitForNet,
recipe_path, to_display)
--
1.7.7.6
11 years, 9 months
[PATCH] nettestctl: Fixing logging location bug
by Radek Pazdera
From: Radek Pazdera <rpazdera(a)redhat.com>
When executing multiple recipes in a recipe set, the line
INFO: Processing recipe file "example_recipes/rset/mcast.xml"
that indicates execution of another recipe was printed into
the log file of previous recipe in the set.
After this modification the line is put into the top-level log
for messages related to the controler.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
nettestctl.py | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/nettestctl.py b/nettestctl.py
index 614f2fc..ec99d73 100755
--- a/nettestctl.py
+++ b/nettestctl.py
@@ -143,6 +143,7 @@ def main():
summary.append(get_recipe_result(args, recipe_file,
remoteexec, cleanup,
res_serializer))
+ Logs.set_logging_root_path(clean=False)
else:
summary.append(get_recipe_result(args, recipe_path,
remoteexec, cleanup, res_serializer))
--
1.7.7.6
11 years, 9 months
[PATCH] Tests: Adding new test - PacketAssert
by Radek Pazdera
From: Radek Pazdera <rpazdera(a)redhat.com>
This commit introduces a new test - packet assert, that can be used to
test if a specific amount of packets with certain properties were
transfered through some interface.
Packet properties can be verified through the pcap "filter" option or
through greping tcpdump output by using "grep-for" option (grep-for can
be used multiple times.
Here's a quick examle:
<command type="test" value="PacketAssert" bg_id="1">
<options>
<option name="interface" value="br0" />
<option name="filter" value="dst port 1234" />
<option name="grep_for" value="IP" />
<option name="min" value="1" />
<option name="max" value="5" />
</options>
</command>
<command type="exec" value="sleep 3" />
<command type="intr" value="1" />
This assert evaluates true if one to five IP packets with destination
port of 1234 are transfered through interface br0 within the time period
of sleep (i.e. 3 seconds).
PacketAssert must always be run in background. It is evaluated at the
time of interruption with "intr". It will not evaluate correctly in
case the command is killed (with "kill")!
One example recipe to demonstrate it's capabilities was added as well.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
Tests/TestPacketAssert.py | 140 +++++++++++++++++++++++++++++++++++++
example_recipes/packet_assert.xml | 15 ++++
2 files changed, 155 insertions(+), 0 deletions(-)
create mode 100644 Tests/TestPacketAssert.py
create mode 100644 example_recipes/packet_assert.xml
diff --git a/Tests/TestPacketAssert.py b/Tests/TestPacketAssert.py
new file mode 100644
index 0000000..5f869db
--- /dev/null
+++ b/Tests/TestPacketAssert.py
@@ -0,0 +1,140 @@
+"""
+Test if packets were transfered through some interface correctly.
+This test is using tcpdump.
+
+Copyright 2012 Red Hat, Inc.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+rpazdera(a)redhat.com (Radek Pazdera)
+"""
+
+import logging
+import subprocess
+import re
+import signal
+from Common.TestsCommon import TestGeneric
+
+class TestPacketAssert(TestGeneric):
+ """ Assert for number of incomming/outgoing packets
+ Capturing backend of this class is provided by
+ tcpdump(8).
+ """
+
+ _cmd = ""
+ _tcpdump = None
+ _grep_filters = []
+
+ _min_cond = 1
+ _max_cond = None
+
+ _num_recv = 0
+
+ def _set_interrupt_handler(self):
+ signal.signal(signal.SIGINT, self._interrupt_handler)
+ signal.signal(signal.SIGTERM, self._interrupt_handler)
+
+ def _interrupt_handler(self, signum, frame):
+ """ Kill tcpdump when interrupted """
+ self._tcpdump.terminate()
+
+ def _prepare_grep_filters(self):
+ """ Parse `grep_for' test options """
+ filters = self.get_multi_opt("grep_for")
+
+ for filt in filters:
+ if filt != None:
+ self._grep_filters.append(filt)
+
+ def _prepare_conditions(self):
+ """ Parse `min' and `max' """
+ min_packets = self.get_opt("min")
+ max_packets = self.get_opt("max")
+
+ if min_packets != None:
+ self._min_cond = int(min_packets)
+
+ if max_packets != None:
+ self._max_cond = int(max_packets)
+
+ def _compose_cmd(self):
+ """ Create a command from the recipe options """
+ cmd = ""
+
+ interface = self.get_mopt("interface")
+ pcap_filter = self.get_mopt("filter")
+
+ cmd = "tcpdump -p -nn -i %s \"%s\"" % (interface, pcap_filter)
+ self._cmd = cmd
+
+ def _execute_tcpdump(self):
+ """ Start tcpdump in the background """
+ cmd = self._cmd
+ proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ self._tcpdump = proc
+
+ def _process_captured_line(self, line):
+ """ Apply filters and see if the packet passed them """
+ if len(self._grep_filters):
+ for filt in self._grep_filters:
+ if not re.search(filt, line):
+ return
+
+ self._num_recv += 1
+
+ def _evaluate_results(self):
+ """ Compare results with the conditions """
+ num = self._num_recv
+ if num >= self._min_cond:
+ if self._max_cond != None:
+ return num <= self._max_cond
+ return True
+ return False
+
+ def run(self):
+ self._set_interrupt_handler()
+
+ self._prepare_grep_filters()
+ self._prepare_conditions()
+ self._compose_cmd()
+ self._execute_tcpdump()
+
+ logging.info("Capturing started")
+
+ line = ""
+ tcpdump_output = self._tcpdump.stdout
+ while True:
+ if self._tcpdump.poll() != None:
+ if self._tcpdump.returncode > 0:
+ raise Exception("tcpdump terminated with error")
+ else:
+ break
+
+ try:
+ next_line = tcpdump_output.readline()
+ except IOError: # Interrupted system call
+ continue
+
+ if next_line == "":
+ continue
+
+ next_line = next_line.strip("\n")
+
+ if re.match("[0-9]+\:[0-9]+\:[0-9\.]+", next_line) and line != "":
+ self._process_captured_line(line)
+ line = next_line
+ else:
+ line += next_line
+
+ logging.info("Capturing finished. Received %d packets", self._num_recv)
+ res = {"received": self._num_recv,
+ "min": self._min_cond,
+ "max": self._max_cond}
+
+ if self._evaluate_results():
+ return self.set_pass(res)
+
+ return self.set_fail("PacketAssert failed!", res)
diff --git a/example_recipes/packet_assert.xml b/example_recipes/packet_assert.xml
new file mode 100644
index 0000000..11d20b7
--- /dev/null
+++ b/example_recipes/packet_assert.xml
@@ -0,0 +1,15 @@
+<nettestrecipe>
+ <command_sequence>
+ <command type="test" value="PacketAssert" bg_id="1">
+ <options>
+ <option name="interface" value="br0" />
+ <option name="filter" value="" />
+ <option name="grep_for" value="IP" />
+ <option name="min" value="1" />
+ <option name="max" value="5" />
+ </options>
+ </command>
+ <command type="exec" value="sleep 3" />
+ <command type="intr" value="1" />
+ </command_sequence>
+</nettestrecipe>
--
1.7.7.6
11 years, 9 months