[PATCH] test_modules: use uppercase for kbits in Iperf test
by Jan Tluka
Iperf reports performance as Kbits/sec. The regexp used for matching the
threshold value contained only lowercase 'k'. This patch updates the regexp
to match both upper and lowercase and converts the matched string to
uppercase.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
test_modules/Iperf.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/test_modules/Iperf.py b/test_modules/Iperf.py
index 1d8735b..09cc6b4 100644
--- a/test_modules/Iperf.py
+++ b/test_modules/Iperf.py
@@ -35,15 +35,15 @@ class Iperf(TestGeneric):
def _rate_over_threshold(self, rate):
# convert rate to the same unit as threshold unit
- pattern = "(\d*(\.\d*){0,1})\s*([ kMGT])bits\/sec"
+ pattern = "(\d*(\.\d*){0,1})\s*([ kKMGT])bits\/sec"
# parse threshold value
r1 = re.match(pattern, self.threshold)
- thr_units = r1.group(3)
+ thr_units = r1.group(3).upper()
# parse measured rate value
r2 = re.match(pattern, rate)
- rate_units = r2.group(3)
+ rate_units = r2.group(3).upper()
thr_val = float(r1.group(1))
@@ -52,7 +52,7 @@ class Iperf(TestGeneric):
# do the conversion of rate units
if thr_units != rate_units:
# remove any k,M,G,T from measured rate
- if rate_units == 'k':
+ if rate_units == 'K':
rate_val *= 1000
elif rate_units == 'M':
rate_val *= 1000*1000
@@ -60,7 +60,7 @@ class Iperf(TestGeneric):
rate_val *= 1000*1000*1000
# divide by k or M or G if present
- if thr_units == 'k':
+ if thr_units == 'K':
rate_val /= 1000
elif thr_units == 'M':
rate_val /= 1000*1000
--
2.1.0
7 years, 10 months
[PATCH 1/7] change shebangs to python2
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
According to PEP 0394:
in preparation for an eventual change in the default version of Python,
Python 2 only scripts should either be updated to be source compatible
with Python 3 or else to use python2 in the shebang line.
It's not a complete solution but it solves problems of running LNST on
Arch Linux until more work is done on issue #105.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst-ctl | 2 +-
lnst-pool-wizard | 2 +-
lnst-slave | 2 +-
misc/recipe_conv.py | 2 +-
recipes/smoke/generate-recipes.py | 2 +-
setup.py | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl
index 9a321b6..947f3ad 100755
--- a/lnst-ctl
+++ b/lnst-ctl
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python2
"""
Net test controller
diff --git a/lnst-pool-wizard b/lnst-pool-wizard
index c788688..2b49744 100755
--- a/lnst-pool-wizard
+++ b/lnst-pool-wizard
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python2
"""
Machine pool wizard
diff --git a/lnst-slave b/lnst-slave
index d4345c3..6238769 100755
--- a/lnst-slave
+++ b/lnst-slave
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python2
"""
Net test slave
diff --git a/misc/recipe_conv.py b/misc/recipe_conv.py
index a5c19c4..a18a0d2 100755
--- a/misc/recipe_conv.py
+++ b/misc/recipe_conv.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
"""
Recipe converter
diff --git a/recipes/smoke/generate-recipes.py b/recipes/smoke/generate-recipes.py
index bed434b..24806ad 100755
--- a/recipes/smoke/generate-recipes.py
+++ b/recipes/smoke/generate-recipes.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python2
# LNST Smoke Tests
# Author: Ondrej Lichtner <olichtne(a)redhat.com>
diff --git a/setup.py b/setup.py
index d281fe9..4033d4a 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
"""
Install script for lnst
--
2.4.6
7 years, 10 months
[PATCH] Refactorization of LNST Pool Wizard
by Jiri Prochazka
Removed MessageDispatcherLite.py
lnst-pool-wizard
+ Make pep8 compliant
+ Print error messages on stderr
+ Changes correspoding with Wizard.py refactorization
Wizard.py
+ Make pep8 compliant
+ Most of the methods reworked to be more clean and readable
+ Setup for implementation of support for virtual machines
Closes #129
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
---
lnst-pool-wizard | 55 +++--
lnst/Controller/MessageDispatcherLite.py | 66 ------
lnst/Controller/Wizard.py | 349 ++++++++++++++++++++-----------
3 files changed, 259 insertions(+), 211 deletions(-)
delete mode 100644 lnst/Controller/MessageDispatcherLite.py
diff --git a/lnst-pool-wizard b/lnst-pool-wizard
index c788688..d17d23a 100755
--- a/lnst-pool-wizard
+++ b/lnst-pool-wizard
@@ -17,48 +17,67 @@ import getopt
RETVAL_PASS = 0
RETVAL_ERR = 1
-def help(retval = 0):
+
+def help(retval=0):
print "Usage:\n"\
" lnst-pool-wizard [mode] [hostname[:port]]\n"\
"\n"\
"Modes:\n"\
- " -h, --help display this help text and exit\n"\
- " -i, --interactive start wizard in interactive mode (this "\
- "is default mode)\n"\
- " -n, --noninteractive start wizard in noninteractive mode\n"\
+ " -h, --help display this help text and exit\n"\
+ " -p, --pool_dir <directory> set the pool dir (works both in) "\
+ "interactive and noninteractive mode\n"\
+ " -i, --interactive start wizard in interactive mode (this "\
+ "is default mode)\n"\
+ " -n, --noninteractive start wizard in noninteractive mode\n"\
"Examples:\n"\
" lnst-pool-wizard --interactive\n"\
" lnst-pool-wizard --noninteractive 192.168.122.2\n"\
- " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.3:9999 192.168.122.4\n"
+ " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.4\n"\
+ " lnst-pool-wizard -p \".pool/\" -n 192.168.1.1:8877 192.168.122.4\n"
sys.exit(retval)
def main():
try:
opts, args = getopt.getopt(
- sys.argv[1:],
- "hin",
- ["help", "interactive", "noninteractive"]
+ sys.argv[1:],
+ "hinp:",
+ ["help", "interactive", "noninteractive", "pool_dir="]
)
except getopt.GetoptError as err:
- print str(err)
+ sys.stderr.write(str(err))
help(RETVAL_ERR)
- wizard = Wizard()
- for opt, arg in opts:
+
+ pool_dir= None
+ mode = "interactive"
+
+ for opt, arg in opts:
if opt in ("-h", "--help"):
help()
elif opt in ("-i", "--interactive"):
- wizard.interactive()
- sys.exit(RETVAL_PASS)
+ mode = "interactive"
elif opt in ("-n", "--noninteractive"):
if not args:
- print "No hostname entered!"
+ sys.stderr.write("No hostnames entered\n")
return RETVAL_ERR
- wizard.noninteractive(args)
- sys.exit(RETVAL_PASS)
+ else:
+ hostlist = args
+ mode = "noninteractive"
+ elif opt in ("-p", "--pool_dir"):
+ if not arg:
+ sys.stderr.write("No pool directory specified\n")
+ else:
+ pool_dir = arg
else:
help(RETVAL_ERR)
- wizard.interactive()
+
+ w = Wizard()
+
+ if mode == "noninteractive":
+ w.noninteractive(hostlist, pool_dir)
+ else:
+ w.interactive(pool_dir)
+
sys.exit(RETVAL_PASS)
diff --git a/lnst/Controller/MessageDispatcherLite.py b/lnst/Controller/MessageDispatcherLite.py
deleted file mode 100644
index 2973763..0000000
--- a/lnst/Controller/MessageDispatcherLite.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""
-This module defines MessageDispatcherLite class which is derived from
-lnst.NetTestController.MessageDispatcher.
-
-Copyright 2011 Red Hat, Inc.
-Licensed under the GNU General Public License, version 2 as
-published by the Free Software Foundation; see COPYING for details.
-"""
-
-__author__ = """
-jpirko(a)redhat.com (Jiri Pirko)
-"""
-
-from lnst.Common.ConnectionHandler import send_data
-from lnst.Common.ConnectionHandler import ConnectionHandler
-from lnst.Controller.NetTestController import NetTestError
-
-class MessageDispatcherLite(ConnectionHandler):
- def __init__(self):
- super(MessageDispatcherLite, self).__init__()
-
- def add_slave(self, machine, connection):
- self.add_connection(1, connection)
-
- def send_message(self, machine_id, data):
- soc = self.get_connection(1)
-
- if send_data(soc, data) == False:
- msg = "Connection error from slave %s" % str(1)
- raise NetTestError(msg)
-
- def wait_for_result(self, machine_id):
- wait = True
- while wait:
- connected_slaves = self._connection_mapping.keys()
-
- messages = self.check_connections()
-
- remaining_slaves = self._connection_mapping.keys()
-
- for msg in messages:
- if msg[1]["type"] == "result" and msg[0] == 1:
- wait = False
- result = msg[1]["result"]
- else:
- self._process_message(msg)
-
- if connected_slaves != remaining_slaves:
- disconnected_slaves = set(connected_slaves) -\
- set(remaining_slaves)
- msg = "Slaves " + str(list(disconnected_slaves)) + \
- " disconnected from the controller."
- raise NetTestError(msg)
-
- return result
-
- def _process_message(self, message):
- if message[1]["type"] == "log":
- pass
- else:
- msg = "Unknown message type: %s" % message[1]["type"]
- raise NetTestError(msg)
-
- def disconnect_slave(self, machine_id):
- soc = self.get_connection(machine_id)
- self.remove_connection(soc)
diff --git a/lnst/Controller/Wizard.py b/lnst/Controller/Wizard.py
index c38aae5..7db8459 100644
--- a/lnst/Controller/Wizard.py
+++ b/lnst/Controller/Wizard.py
@@ -1,7 +1,7 @@
"""
-Wizard class for creating machine pool .xml files
+Wizard class for creating slave machine config files
-Copyright 2014 Red Hat Inc.
+Copyright 2015 Red Hat Inc.
Licensed under the GNU General Public Licence, version 2 as
published by the Free Software Foundation; see COPYING for details.
"""
@@ -9,150 +9,149 @@ published by the Free Software Foundation; see COPYING for details.
__author__ = """
jprochaz(a)redhat.com (Jiri Prochazka)
"""
+
import socket
import sys
+import time
import os
-from lnst.Controller.Machine import Machine
-from lnst.Controller.MessageDispatcherLite import MessageDispatcherLite
-from lnst.Common.NetUtils import test_tcp_connection
from lnst.Common.Utils import mkdir_p
from lnst.Common.Config import DefaultRPCPort
+from lnst.Common.ConnectionHandler import send_data, recv_data
from xml.dom.minidom import getDOMImplementation
+DefaultPoolDir = os.path.expanduser("~/.lnst/pool/")
+
+
class Wizard:
- def __init__(self):
- self._msg_dispatcher = MessageDispatcherLite()
- self._pool_dir = os.path.expanduser("~/.lnst/pool")
- def interactive(self):
+ def interactive(self, pool_dir=None):
+ """ Starts Wizard in an interactive mode
+ @param pool_dir Path to pool directory (optional)
"""
- Starts Wizard in interactive mode. Wizard requests hostname and port
- from user, tests connectivity to entered host, then he tries to connect
- and configure slave on host machine, and finally he requests list of
- ethernet interfaces in state DOWN. Then he writes them into .xml file
- named by user. User can choose which interfaces should be added to the
- .xml file.
- """
-
- pool_dir = raw_input("Enter path to pool directory "\
- "(default: ~/.lnst/pool): ")
- if pool_dir != "":
- self._pool_dir = os.path.expanduser(pool_dir)
- print "Pool directory set to %s" % self._pool_dir
+ if pool_dir is None:
+ pool_dir = self._query_pool_dir()
+ # Invalid pool dir entered, query for correct one
+ elif not self._check_pool_dir(pool_dir):
+ pool_dir = self._query_pool_dir()
while True:
- while True:
- hostname = raw_input("Enter hostname: ")
- try:
- # Tests if hostname is translatable into IP address
- socket.gethostbyname(hostname)
- break
- except:
- sys.stderr.write("Hostname is not translatable into valid"\
- " IP address.\n")
+ hostname = self._query_hostname()
+ port = self._query_port()
+
+ sock = self._get_connection(hostname, port)
+ if sock is None:
+ if self._query_continuation():
continue
- while True:
- port = raw_input("Enter port(default: 9999): ")
- if port == "":
- port = DefaultRPCPort
- try:
- port = int(port)
+ else:
break
- except:
- sys.stderr.write("Invalid port.")
- continue
- msg = self._get_suitable_interfaces(socket.gethostbyname(hostname),\
- port)
- if msg:
- self._write_to_xml(msg, hostname, port, "interactive")
- next_machine = raw_input("Do you want to add another machine? "\
- "[Y/n]: ")
- if next_machine.lower() == 'y' or next_machine == "":
+
+ machine_interfaces = self._get_machine_interfaces(sock)
+ sock.close()
+
+ if machine_interfaces == {}:
+ sys.stderr.write("No suitable interfaces found on the host "
+ "'%s:%s'\n" % (hostname, port))
+ elif machine_interfaces is not None:
+ filename = self._query_filename(hostname)
+ self._create_xml(machine_interfaces, hostname,
+ port, pool_dir, filename, "interactive")
+ if self._query_continuation():
continue
else:
break
- return
- def noninteractive(self, hostlist):
- """
- Starts Wizard in noninteractive mode. Wizard gets list of hosts and
- ports as arguments. He tries to connect and get info about suitable
- interfaces for each of the hosts. Noninteractive mode does not prompt
- user about anything, it automatically adds all suitable interfaces into
- .xml file named the same as the hostname of the selected machine.
+ def noninteractive(self, hostlist, pool_dir):
+ """ Starts Wizard in noninteractive mode
+ @param hostlist List of hosts (mandatory)
+ @param pool_dir Path to pool_directory (optional)
"""
- self._mode = "noninteractive"
+ if pool_dir is None:
+ pool_dir = DefaultPoolDir
+ # Invalid pool_dir entered, abort wizard
+ if not self._check_pool_dir(pool_dir):
+ sys.stderr.write("Pool wizard aborted\n")
+ return
for host in hostlist:
- # Checks if port was entered along with hostname
+ print("Processing host '%s'" % host)
+ # Check if port was entered along with hostname
if host.find(":") != -1:
- hostname = host.split(':')[0]
- try:
- port = int(host.split(':')[1])
- except:
- port = DefaultRPCPort
+ hostname = host.split(":")[0]
+ if hostname == "":
+ msg = "'%s' does not contain valid hostname\n" % host
+ sys.stderr.write(msg)
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
+ try:
+ port = int(host.split(":")[1])
+ except:
+ port = DefaultRPCPort
+ msg = "Invalid port entered, "\
+ "using '%s' instead\n" % port
+ sys.stderr.write(msg)
else:
hostname = host
port = DefaultRPCPort
- msg = self._get_suitable_interfaces(hostname, port)
- if not msg:
+
+ if not self._check_hostname(hostname):
+ sys.stderr.write("Skipping host '%s'\n" % host)
continue
- self._write_to_xml(msg, hostname, port, "noninteractive")
- def _get_suitable_interfaces(self, hostname, port):
- """
- Calls all functions, which are used by both interactive and
- noninteractive mode to get list of suitable interfaces. The list is
- saved to variable msg.
- """
- if not test_tcp_connection(hostname, port):
- sys.stderr.write("Host destination '%s:%s' unreachable.\n"
- % (hostname, port))
- return False
- if not self._connect_and_configure_machine(hostname, port):
- return False
- msg = self._get_device_ifcs(hostname, port)
- self._msg_dispatcher.disconnect_slave(1)
- return msg
+ sock = self._get_connection(hostname, port)
+ if sock is None:
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
- def _get_device_ifcs(self, hostname, port):
- """
- Sends RPC call request to Slave to call function get_devices, which
- returns list of ethernet devices which are in state DOWN.
- """
- msg = self._machine._rpc_call("get_devices")
- if msg == {}:
- sys.stderr.write("No suitable interfaces found on the slave "\
- "'%s:%s'.\n" % (hostname, port))
- return False
- return msg
+ machine_interfaces = self._get_machine_interfaces(sock)
+ sock.close()
- def _connect_and_configure_machine(self, hostname, port):
- """
- Connects to Slave and configures it
+ if machine_interfaces is {}:
+ sys.stderr.write("No suitable interfaces found on the host "
+ "'%s:%s'\n" % (hostname, port))
+ continue
+ elif machine_interfaces is None:
+ continue
+ else:
+ filename = hostname + ".xml"
+ self._create_xml(machine_interfaces, hostname,
+ port, pool_dir, filename, "noninteractive")
+
+ def _check_hostname(self, hostname):
+ """ Checks hostnames translatibility
+ @param hostname Hostname which is checked whether it's valid
+ @return True if valid hostname was entered, False otherwise
"""
try:
- self._machine = Machine(1, hostname, None, port)
- self._machine.set_rpc(self._msg_dispatcher)
- self._machine.configure("MachinePoolWizard")
+ # Test if hostname is translatable into IP address
+ socket.gethostbyname(hostname)
return True
except:
- sys.stderr.write("Remote machine '%s:%s' configuration failed!\n"
- % (hostname, port))
- self._msg_dispatcher.disconnect_slave(1)
+ sys.stderr.write("Hostname '%s' is not translatable into a valid "
+ "IP address\n" % hostname)
return False
- def _write_to_xml(self, msg, hostname, port, mode):
+ def _check_pool_dir(self, pool_dir):
+ """ Checks users access to selected directory
+ @param pool_dir Path to checked directory
+ @return True if user can write in entered directory, False otherwise
"""
- Used for writing desired output into .xml file. In interactive mode,
- user is prompted for every interface, in noninteractive mode all
- interfaces are automatically added to the .xml file.
+ if os.access(pool_dir, os.W_OK):
+ return True
+ else:
+ sys.stderr.write("Directory '%s' is not writable\n"
+ % pool_dir)
+ return False
+
+ def _create_xml(self, machine_interfaces, hostname,
+ port, pool_dir, filename, mode):
+ """ Creates slave machine XML file
+ @param machine_interfaces Dictionary with machine's interfaces
+ @param hostname Hostname of the machine
+ @param port Port on which LNST listens on the machine
+ @param pool_dir Path to directory where XML file will be created
+ @param filename Name of the XML file
+ @param mode Mode in which wizard was started
"""
- if mode == "interactive":
- output_file = raw_input("Enter the name of the output .xml file "\
- "(without .xml, default is hostname.xml): ")
- if mode == "noninteractive" or output_file == "":
- output_file = hostname
impl = getDOMImplementation()
doc = impl.createDocument(None, "slavemachine", None)
@@ -166,42 +165,138 @@ class Wizard:
interfaces_el = doc.createElement("interfaces")
top_el.appendChild(interfaces_el)
- devices_added = 0
- for interface in msg.itervalues():
- if mode == 'interactive':
+ interfaces_added = 0
+ for iface in machine_interfaces.itervalues():
+ if mode == "interactive":
answer = raw_input("Do you want to add interface '%s' (%s) "
- "to the recipe? [Y/n]" % (interface['name'],
- interface['hwaddr']))
- if mode == "noninteractive" or answer.lower() == 'y'\
+ "to the recipe? [Y/n]: " % (iface["name"],
+ iface["hwaddr"]))
+ if mode == "noninteractive" or answer.lower() == "y"\
or answer == "":
- devices_added += 1
+ interfaces_added += 1
eth_el = doc.createElement("eth")
- eth_el.setAttribute("id", interface['name'])
+ eth_el.setAttribute("id", iface["name"])
eth_el.setAttribute("label", "default_network")
interfaces_el.appendChild(eth_el)
params_el = doc.createElement("params")
eth_el.appendChild(params_el)
param_el = doc.createElement("param")
param_el.setAttribute("name", "hwaddr")
- param_el.setAttribute("value", interface['hwaddr'])
+ param_el.setAttribute("value", iface["hwaddr"])
params_el.appendChild(param_el)
- if devices_added == 0:
- sys.stderr.write("You didn't add any interface, no file '%s.xml' "\
- "will be created!\n" % output_file)
+ if interfaces_added == 0:
+ sys.stderr.write("You didn't add any interface, no file "
+ "'%s' will be created\n" % filename)
return
- mkdir_p(self._pool_dir)
+ mkdir_p(pool_dir)
try:
- f = open(self._pool_dir + "/" + output_file + ".xml", 'w')
+ f = open(pool_dir + "/" + filename, "w")
f.write(doc.toprettyxml())
f.close()
except:
- sys.stderr.write("File '%s.xml' could not be opened "\
- "or data written." % output_file+"\n")
- raise WizardException()
+ msg = "File '%s/%s' could not be opened or data written\n"\
+ % (pool_dir, filename)
+ raise WizardException(msg)
+
+ print("File '%s' successfuly created" % filename)
+
+ def _get_connection(self, hostname, port):
+ """ Connects to machine
+ @param hostname Hostname of the machine
+ @param port Port of the machine
+ @return Connected socket if connection was successful, None otherwise
+ """
+ try:
+ sock = socket.create_connection((hostname, port))
+ return sock
+ except socket.error:
+ sys.stderr.write("Connection to remote host '%s:%s' failed\n"
+ % (hostname, port))
+ return None
+
+ def _get_machine_interfaces(self, sock):
+ """ Gets machine interfaces via RPC call
+ @param sock Socket used for connecting to machine
+ @return Dictionary with machine interfaces or None if RPC call fails
+ """
+ msg = {"type": "command", "method_name": "get_devices", "args": {}}
+ if not send_data(sock, msg):
+ sys.stderr.write("Could not send request to slave machine\n")
+ return None
+
+ while True:
+ data = recv_data(sock)
+ if data["type"] == "result":
+ return data["result"]
+
+ def _query_continuation(self):
+ """ Queries user for adding next machine
+ @return True if user wants to add another machine, False otherwise
+ """
+ answer = raw_input("Do you want to add another machine? [Y/n]: ")
+ if answer.lower() == "y" or answer == "":
+ return True
+ else:
+ return False
+
+ def _query_filename(self, hostname):
+ """ Queries user for name of the file
+ @hostname Hostname of the machine which is used as default filename
+ @return Name of the file with .xml extension
+ """
+ output_file = raw_input("Enter the name of the output .xml file "
+ "(without .xml, default is '%s.xml'): "
+ % hostname)
+ if output_file == "":
+ return hostname + ".xml"
+ else:
+ return output_file + ".xml"
+
+ def _query_hostname(self):
+ """ Queries user for hostname
+ @return Valid (is translatable to an IP address) hostname
+ """
+ while True:
+ hostname = raw_input("Enter hostname: ")
+ if hostname == "":
+ sys.stderr.write("No hostname entered\n")
+ continue
+ if self._check_hostname(hostname):
+ return hostname
+
+ def _query_pool_dir(self):
+ """ Queries user for pool directory
+ @return Valid (is writable by user) path to directory
+ """
+ while True:
+ pool_dir = raw_input("Enter path to a pool directory "
+ "(default: '%s'): " % DefaultPoolDir)
+ if pool_dir == "":
+ return DefaultPoolDir
+
+ pool_dir = os.path.expanduser(pool_dir)
+ # check if dir is writable
+ if self._check_pool_dir(pool_dir):
+ print("Pool directory set to '%s'" % pool_dir)
+ return pool_dir
+
+ def _query_port(self):
+ """ Queries user for port
+ @return Integer representing port
+ """
+ while True:
+ port = raw_input("Enter port (default: %d): " % DefaultRPCPort)
+ if port == "":
+ return DefaultRPCPort
+ else:
+ try:
+ port = int(port)
+ return port
+ except:
+ sys.stderr.write("Invalid port entered\n")
- print "File '%s.xml' successfuly created." % output_file
class WizardException(Exception):
pass
--
2.4.3
7 years, 10 months
[PATCH] Refactorization of LNST Pool Wizard
by Jiri Prochazka
MessageDispatcherLite.py
+ Remove unused import
+ Make pep8 compliant
+ Rewrite one error message
lnst-pool-wizard
+ Make pep8 compliant
+ Print error messages on stderr
+ Changes correspoding with Wizard.py refactorization
Wizard.py
+ Make pep8 compliant
+ Convert public methods to private
+ Most of the methods reworked to be more clean and readable
+ Setup for implementation of support for virtual machines
Closes #129
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
---
lnst-pool-wizard | 55 +++--
lnst/Controller/MessageDispatcherLite.py | 9 +-
lnst/Controller/Wizard.py | 349 ++++++++++++++++++++-----------
3 files changed, 264 insertions(+), 149 deletions(-)
diff --git a/lnst-pool-wizard b/lnst-pool-wizard
index c788688..d17d23a 100755
--- a/lnst-pool-wizard
+++ b/lnst-pool-wizard
@@ -17,48 +17,67 @@ import getopt
RETVAL_PASS = 0
RETVAL_ERR = 1
-def help(retval = 0):
+
+def help(retval=0):
print "Usage:\n"\
" lnst-pool-wizard [mode] [hostname[:port]]\n"\
"\n"\
"Modes:\n"\
- " -h, --help display this help text and exit\n"\
- " -i, --interactive start wizard in interactive mode (this "\
- "is default mode)\n"\
- " -n, --noninteractive start wizard in noninteractive mode\n"\
+ " -h, --help display this help text and exit\n"\
+ " -p, --pool_dir <directory> set the pool dir (works both in) "\
+ "interactive and noninteractive mode\n"\
+ " -i, --interactive start wizard in interactive mode (this "\
+ "is default mode)\n"\
+ " -n, --noninteractive start wizard in noninteractive mode\n"\
"Examples:\n"\
" lnst-pool-wizard --interactive\n"\
" lnst-pool-wizard --noninteractive 192.168.122.2\n"\
- " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.3:9999 192.168.122.4\n"
+ " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.4\n"\
+ " lnst-pool-wizard -p \".pool/\" -n 192.168.1.1:8877 192.168.122.4\n"
sys.exit(retval)
def main():
try:
opts, args = getopt.getopt(
- sys.argv[1:],
- "hin",
- ["help", "interactive", "noninteractive"]
+ sys.argv[1:],
+ "hinp:",
+ ["help", "interactive", "noninteractive", "pool_dir="]
)
except getopt.GetoptError as err:
- print str(err)
+ sys.stderr.write(str(err))
help(RETVAL_ERR)
- wizard = Wizard()
- for opt, arg in opts:
+
+ pool_dir= None
+ mode = "interactive"
+
+ for opt, arg in opts:
if opt in ("-h", "--help"):
help()
elif opt in ("-i", "--interactive"):
- wizard.interactive()
- sys.exit(RETVAL_PASS)
+ mode = "interactive"
elif opt in ("-n", "--noninteractive"):
if not args:
- print "No hostname entered!"
+ sys.stderr.write("No hostnames entered\n")
return RETVAL_ERR
- wizard.noninteractive(args)
- sys.exit(RETVAL_PASS)
+ else:
+ hostlist = args
+ mode = "noninteractive"
+ elif opt in ("-p", "--pool_dir"):
+ if not arg:
+ sys.stderr.write("No pool directory specified\n")
+ else:
+ pool_dir = arg
else:
help(RETVAL_ERR)
- wizard.interactive()
+
+ w = Wizard()
+
+ if mode == "noninteractive":
+ w.noninteractive(hostlist, pool_dir)
+ else:
+ w.interactive(pool_dir)
+
sys.exit(RETVAL_PASS)
diff --git a/lnst/Controller/MessageDispatcherLite.py b/lnst/Controller/MessageDispatcherLite.py
index 2973763..623c232 100644
--- a/lnst/Controller/MessageDispatcherLite.py
+++ b/lnst/Controller/MessageDispatcherLite.py
@@ -15,7 +15,9 @@ from lnst.Common.ConnectionHandler import send_data
from lnst.Common.ConnectionHandler import ConnectionHandler
from lnst.Controller.NetTestController import NetTestError
+
class MessageDispatcherLite(ConnectionHandler):
+
def __init__(self):
super(MessageDispatcherLite, self).__init__()
@@ -25,9 +27,8 @@ class MessageDispatcherLite(ConnectionHandler):
def send_message(self, machine_id, data):
soc = self.get_connection(1)
- if send_data(soc, data) == False:
- msg = "Connection error from slave %s" % str(1)
- raise NetTestError(msg)
+ if send_data(soc, data) is False:
+ raise NetTestError("Connection error from slave")
def wait_for_result(self, machine_id):
wait = True
@@ -47,7 +48,7 @@ class MessageDispatcherLite(ConnectionHandler):
if connected_slaves != remaining_slaves:
disconnected_slaves = set(connected_slaves) -\
- set(remaining_slaves)
+ set(remaining_slaves)
msg = "Slaves " + str(list(disconnected_slaves)) + \
" disconnected from the controller."
raise NetTestError(msg)
diff --git a/lnst/Controller/Wizard.py b/lnst/Controller/Wizard.py
index c38aae5..7db8459 100644
--- a/lnst/Controller/Wizard.py
+++ b/lnst/Controller/Wizard.py
@@ -1,7 +1,7 @@
"""
-Wizard class for creating machine pool .xml files
+Wizard class for creating slave machine config files
-Copyright 2014 Red Hat Inc.
+Copyright 2015 Red Hat Inc.
Licensed under the GNU General Public Licence, version 2 as
published by the Free Software Foundation; see COPYING for details.
"""
@@ -9,150 +9,149 @@ published by the Free Software Foundation; see COPYING for details.
__author__ = """
jprochaz(a)redhat.com (Jiri Prochazka)
"""
+
import socket
import sys
+import time
import os
-from lnst.Controller.Machine import Machine
-from lnst.Controller.MessageDispatcherLite import MessageDispatcherLite
-from lnst.Common.NetUtils import test_tcp_connection
from lnst.Common.Utils import mkdir_p
from lnst.Common.Config import DefaultRPCPort
+from lnst.Common.ConnectionHandler import send_data, recv_data
from xml.dom.minidom import getDOMImplementation
+DefaultPoolDir = os.path.expanduser("~/.lnst/pool/")
+
+
class Wizard:
- def __init__(self):
- self._msg_dispatcher = MessageDispatcherLite()
- self._pool_dir = os.path.expanduser("~/.lnst/pool")
- def interactive(self):
+ def interactive(self, pool_dir=None):
+ """ Starts Wizard in an interactive mode
+ @param pool_dir Path to pool directory (optional)
"""
- Starts Wizard in interactive mode. Wizard requests hostname and port
- from user, tests connectivity to entered host, then he tries to connect
- and configure slave on host machine, and finally he requests list of
- ethernet interfaces in state DOWN. Then he writes them into .xml file
- named by user. User can choose which interfaces should be added to the
- .xml file.
- """
-
- pool_dir = raw_input("Enter path to pool directory "\
- "(default: ~/.lnst/pool): ")
- if pool_dir != "":
- self._pool_dir = os.path.expanduser(pool_dir)
- print "Pool directory set to %s" % self._pool_dir
+ if pool_dir is None:
+ pool_dir = self._query_pool_dir()
+ # Invalid pool dir entered, query for correct one
+ elif not self._check_pool_dir(pool_dir):
+ pool_dir = self._query_pool_dir()
while True:
- while True:
- hostname = raw_input("Enter hostname: ")
- try:
- # Tests if hostname is translatable into IP address
- socket.gethostbyname(hostname)
- break
- except:
- sys.stderr.write("Hostname is not translatable into valid"\
- " IP address.\n")
+ hostname = self._query_hostname()
+ port = self._query_port()
+
+ sock = self._get_connection(hostname, port)
+ if sock is None:
+ if self._query_continuation():
continue
- while True:
- port = raw_input("Enter port(default: 9999): ")
- if port == "":
- port = DefaultRPCPort
- try:
- port = int(port)
+ else:
break
- except:
- sys.stderr.write("Invalid port.")
- continue
- msg = self._get_suitable_interfaces(socket.gethostbyname(hostname),\
- port)
- if msg:
- self._write_to_xml(msg, hostname, port, "interactive")
- next_machine = raw_input("Do you want to add another machine? "\
- "[Y/n]: ")
- if next_machine.lower() == 'y' or next_machine == "":
+
+ machine_interfaces = self._get_machine_interfaces(sock)
+ sock.close()
+
+ if machine_interfaces == {}:
+ sys.stderr.write("No suitable interfaces found on the host "
+ "'%s:%s'\n" % (hostname, port))
+ elif machine_interfaces is not None:
+ filename = self._query_filename(hostname)
+ self._create_xml(machine_interfaces, hostname,
+ port, pool_dir, filename, "interactive")
+ if self._query_continuation():
continue
else:
break
- return
- def noninteractive(self, hostlist):
- """
- Starts Wizard in noninteractive mode. Wizard gets list of hosts and
- ports as arguments. He tries to connect and get info about suitable
- interfaces for each of the hosts. Noninteractive mode does not prompt
- user about anything, it automatically adds all suitable interfaces into
- .xml file named the same as the hostname of the selected machine.
+ def noninteractive(self, hostlist, pool_dir):
+ """ Starts Wizard in noninteractive mode
+ @param hostlist List of hosts (mandatory)
+ @param pool_dir Path to pool_directory (optional)
"""
- self._mode = "noninteractive"
+ if pool_dir is None:
+ pool_dir = DefaultPoolDir
+ # Invalid pool_dir entered, abort wizard
+ if not self._check_pool_dir(pool_dir):
+ sys.stderr.write("Pool wizard aborted\n")
+ return
for host in hostlist:
- # Checks if port was entered along with hostname
+ print("Processing host '%s'" % host)
+ # Check if port was entered along with hostname
if host.find(":") != -1:
- hostname = host.split(':')[0]
- try:
- port = int(host.split(':')[1])
- except:
- port = DefaultRPCPort
+ hostname = host.split(":")[0]
+ if hostname == "":
+ msg = "'%s' does not contain valid hostname\n" % host
+ sys.stderr.write(msg)
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
+ try:
+ port = int(host.split(":")[1])
+ except:
+ port = DefaultRPCPort
+ msg = "Invalid port entered, "\
+ "using '%s' instead\n" % port
+ sys.stderr.write(msg)
else:
hostname = host
port = DefaultRPCPort
- msg = self._get_suitable_interfaces(hostname, port)
- if not msg:
+
+ if not self._check_hostname(hostname):
+ sys.stderr.write("Skipping host '%s'\n" % host)
continue
- self._write_to_xml(msg, hostname, port, "noninteractive")
- def _get_suitable_interfaces(self, hostname, port):
- """
- Calls all functions, which are used by both interactive and
- noninteractive mode to get list of suitable interfaces. The list is
- saved to variable msg.
- """
- if not test_tcp_connection(hostname, port):
- sys.stderr.write("Host destination '%s:%s' unreachable.\n"
- % (hostname, port))
- return False
- if not self._connect_and_configure_machine(hostname, port):
- return False
- msg = self._get_device_ifcs(hostname, port)
- self._msg_dispatcher.disconnect_slave(1)
- return msg
+ sock = self._get_connection(hostname, port)
+ if sock is None:
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
- def _get_device_ifcs(self, hostname, port):
- """
- Sends RPC call request to Slave to call function get_devices, which
- returns list of ethernet devices which are in state DOWN.
- """
- msg = self._machine._rpc_call("get_devices")
- if msg == {}:
- sys.stderr.write("No suitable interfaces found on the slave "\
- "'%s:%s'.\n" % (hostname, port))
- return False
- return msg
+ machine_interfaces = self._get_machine_interfaces(sock)
+ sock.close()
- def _connect_and_configure_machine(self, hostname, port):
- """
- Connects to Slave and configures it
+ if machine_interfaces is {}:
+ sys.stderr.write("No suitable interfaces found on the host "
+ "'%s:%s'\n" % (hostname, port))
+ continue
+ elif machine_interfaces is None:
+ continue
+ else:
+ filename = hostname + ".xml"
+ self._create_xml(machine_interfaces, hostname,
+ port, pool_dir, filename, "noninteractive")
+
+ def _check_hostname(self, hostname):
+ """ Checks hostnames translatibility
+ @param hostname Hostname which is checked whether it's valid
+ @return True if valid hostname was entered, False otherwise
"""
try:
- self._machine = Machine(1, hostname, None, port)
- self._machine.set_rpc(self._msg_dispatcher)
- self._machine.configure("MachinePoolWizard")
+ # Test if hostname is translatable into IP address
+ socket.gethostbyname(hostname)
return True
except:
- sys.stderr.write("Remote machine '%s:%s' configuration failed!\n"
- % (hostname, port))
- self._msg_dispatcher.disconnect_slave(1)
+ sys.stderr.write("Hostname '%s' is not translatable into a valid "
+ "IP address\n" % hostname)
return False
- def _write_to_xml(self, msg, hostname, port, mode):
+ def _check_pool_dir(self, pool_dir):
+ """ Checks users access to selected directory
+ @param pool_dir Path to checked directory
+ @return True if user can write in entered directory, False otherwise
"""
- Used for writing desired output into .xml file. In interactive mode,
- user is prompted for every interface, in noninteractive mode all
- interfaces are automatically added to the .xml file.
+ if os.access(pool_dir, os.W_OK):
+ return True
+ else:
+ sys.stderr.write("Directory '%s' is not writable\n"
+ % pool_dir)
+ return False
+
+ def _create_xml(self, machine_interfaces, hostname,
+ port, pool_dir, filename, mode):
+ """ Creates slave machine XML file
+ @param machine_interfaces Dictionary with machine's interfaces
+ @param hostname Hostname of the machine
+ @param port Port on which LNST listens on the machine
+ @param pool_dir Path to directory where XML file will be created
+ @param filename Name of the XML file
+ @param mode Mode in which wizard was started
"""
- if mode == "interactive":
- output_file = raw_input("Enter the name of the output .xml file "\
- "(without .xml, default is hostname.xml): ")
- if mode == "noninteractive" or output_file == "":
- output_file = hostname
impl = getDOMImplementation()
doc = impl.createDocument(None, "slavemachine", None)
@@ -166,42 +165,138 @@ class Wizard:
interfaces_el = doc.createElement("interfaces")
top_el.appendChild(interfaces_el)
- devices_added = 0
- for interface in msg.itervalues():
- if mode == 'interactive':
+ interfaces_added = 0
+ for iface in machine_interfaces.itervalues():
+ if mode == "interactive":
answer = raw_input("Do you want to add interface '%s' (%s) "
- "to the recipe? [Y/n]" % (interface['name'],
- interface['hwaddr']))
- if mode == "noninteractive" or answer.lower() == 'y'\
+ "to the recipe? [Y/n]: " % (iface["name"],
+ iface["hwaddr"]))
+ if mode == "noninteractive" or answer.lower() == "y"\
or answer == "":
- devices_added += 1
+ interfaces_added += 1
eth_el = doc.createElement("eth")
- eth_el.setAttribute("id", interface['name'])
+ eth_el.setAttribute("id", iface["name"])
eth_el.setAttribute("label", "default_network")
interfaces_el.appendChild(eth_el)
params_el = doc.createElement("params")
eth_el.appendChild(params_el)
param_el = doc.createElement("param")
param_el.setAttribute("name", "hwaddr")
- param_el.setAttribute("value", interface['hwaddr'])
+ param_el.setAttribute("value", iface["hwaddr"])
params_el.appendChild(param_el)
- if devices_added == 0:
- sys.stderr.write("You didn't add any interface, no file '%s.xml' "\
- "will be created!\n" % output_file)
+ if interfaces_added == 0:
+ sys.stderr.write("You didn't add any interface, no file "
+ "'%s' will be created\n" % filename)
return
- mkdir_p(self._pool_dir)
+ mkdir_p(pool_dir)
try:
- f = open(self._pool_dir + "/" + output_file + ".xml", 'w')
+ f = open(pool_dir + "/" + filename, "w")
f.write(doc.toprettyxml())
f.close()
except:
- sys.stderr.write("File '%s.xml' could not be opened "\
- "or data written." % output_file+"\n")
- raise WizardException()
+ msg = "File '%s/%s' could not be opened or data written\n"\
+ % (pool_dir, filename)
+ raise WizardException(msg)
+
+ print("File '%s' successfuly created" % filename)
+
+ def _get_connection(self, hostname, port):
+ """ Connects to machine
+ @param hostname Hostname of the machine
+ @param port Port of the machine
+ @return Connected socket if connection was successful, None otherwise
+ """
+ try:
+ sock = socket.create_connection((hostname, port))
+ return sock
+ except socket.error:
+ sys.stderr.write("Connection to remote host '%s:%s' failed\n"
+ % (hostname, port))
+ return None
+
+ def _get_machine_interfaces(self, sock):
+ """ Gets machine interfaces via RPC call
+ @param sock Socket used for connecting to machine
+ @return Dictionary with machine interfaces or None if RPC call fails
+ """
+ msg = {"type": "command", "method_name": "get_devices", "args": {}}
+ if not send_data(sock, msg):
+ sys.stderr.write("Could not send request to slave machine\n")
+ return None
+
+ while True:
+ data = recv_data(sock)
+ if data["type"] == "result":
+ return data["result"]
+
+ def _query_continuation(self):
+ """ Queries user for adding next machine
+ @return True if user wants to add another machine, False otherwise
+ """
+ answer = raw_input("Do you want to add another machine? [Y/n]: ")
+ if answer.lower() == "y" or answer == "":
+ return True
+ else:
+ return False
+
+ def _query_filename(self, hostname):
+ """ Queries user for name of the file
+ @hostname Hostname of the machine which is used as default filename
+ @return Name of the file with .xml extension
+ """
+ output_file = raw_input("Enter the name of the output .xml file "
+ "(without .xml, default is '%s.xml'): "
+ % hostname)
+ if output_file == "":
+ return hostname + ".xml"
+ else:
+ return output_file + ".xml"
+
+ def _query_hostname(self):
+ """ Queries user for hostname
+ @return Valid (is translatable to an IP address) hostname
+ """
+ while True:
+ hostname = raw_input("Enter hostname: ")
+ if hostname == "":
+ sys.stderr.write("No hostname entered\n")
+ continue
+ if self._check_hostname(hostname):
+ return hostname
+
+ def _query_pool_dir(self):
+ """ Queries user for pool directory
+ @return Valid (is writable by user) path to directory
+ """
+ while True:
+ pool_dir = raw_input("Enter path to a pool directory "
+ "(default: '%s'): " % DefaultPoolDir)
+ if pool_dir == "":
+ return DefaultPoolDir
+
+ pool_dir = os.path.expanduser(pool_dir)
+ # check if dir is writable
+ if self._check_pool_dir(pool_dir):
+ print("Pool directory set to '%s'" % pool_dir)
+ return pool_dir
+
+ def _query_port(self):
+ """ Queries user for port
+ @return Integer representing port
+ """
+ while True:
+ port = raw_input("Enter port (default: %d): " % DefaultRPCPort)
+ if port == "":
+ return DefaultRPCPort
+ else:
+ try:
+ port = int(port)
+ return port
+ except:
+ sys.stderr.write("Invalid port entered\n")
- print "File '%s.xml' successfuly created." % output_file
class WizardException(Exception):
pass
--
2.4.3
7 years, 10 months
[PATCH] NetTestCommand: include netns information in str_command
by Jan Tluka
Info log does not contain any information about network namespace used for
command. This patch appends netns information.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
lnst/Common/NetTestCommand.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index 1e404bf..0ce8998 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -59,6 +59,9 @@ def str_command(command):
else:
raise RuntimeError("Unknown command type '%s'" % command["type"])
+ if "netns" in command:
+ attrs.append("netns(%s)" % command["netns"])
+
return ", ".join(attrs)
class CommandException(Exception):
--
2.1.0
7 years, 10 months
[PATCH] recipes: add mtu alias to phase1 virt tests
by Jan Tluka
This patch adds mtu alias to all phase1 virt tests so that user can
override the default mtu of size 1500. Using higher mtu in these setups
gives much better throughput on the receiving side.
Test with mtu=1500:
tx: 13.7592 Gbits/s
rx: 0.167 Gbits/s
Test with mtu=8000:
tx: 25.052 Gbits/s
rx: 7.4019 Gbits/s
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
...tual_bridge_2_vlans_over_active_backup_bond.xml | 1 +
.../phase1/virtual_bridge_2_vlans_over_bond.py | 23 ++++++++++++++++++++++
.../phase1/virtual_bridge_vlan_in_guest.py | 11 +++++++++++
.../phase1/virtual_bridge_vlan_in_guest.xml | 1 +
.../phase1/virtual_bridge_vlan_in_host.py | 12 +++++++++++
.../phase1/virtual_bridge_vlan_in_host.xml | 1 +
6 files changed, 49 insertions(+)
diff --git a/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_active_backup_bond.xml b/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_active_backup_bond.xml
index 2c9ed11..d4ff566 100644
--- a/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_active_backup_bond.xml
+++ b/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_active_backup_bond.xml
@@ -2,6 +2,7 @@
<define>
<alias name="ipv" value="both" />
<alias name="netperf_duration" value="60" />
+ <alias name="mtu" value="1500" />
</define>
<network>
<host id="host1">
diff --git a/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_bond.py b/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_bond.py
index 8347eaf..2ecd7f2 100644
--- a/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_bond.py
+++ b/recipes/regression_tests/phase1/virtual_bridge_2_vlans_over_bond.py
@@ -26,6 +26,7 @@ offloads = ["gso", "gro", "tso"]
ipv = ctl.get_alias("ipv")
netperf_duration = ctl.get_alias("netperf_duration")
+mtu = ctl.get_alias("mtu")
ping_mod = ctl.get_module("IcmpPing",
options={
@@ -145,6 +146,28 @@ ping_mod6_bad2 = ctl.get_module("Icmp6Ping",
"interval" : 0.1
})
+# configure mtu on all devices
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("bond"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("tap1"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("tap2"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("vlan10"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("vlan20"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("br1"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("br2"), mtu))
+
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("bond"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("tap1"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("tap2"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("vlan10"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("vlan20"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("br1"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("br2"), mtu))
+
+g1.run("ip link set dev %s mtu %s" % (g1.get_devname("guestnic"), mtu))
+g2.run("ip link set dev %s mtu %s" % (g2.get_devname("guestnic"), mtu))
+g3.run("ip link set dev %s mtu %s" % (g3.get_devname("guestnic"), mtu))
+g4.run("ip link set dev %s mtu %s" % (g4.get_devname("guestnic"), mtu))
+
ctl.wait(15)
for offload in offloads:
diff --git a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.py b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.py
index bc90f5d..78585a1 100644
--- a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.py
+++ b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.py
@@ -20,6 +20,7 @@ offloads = ["gso", "gro", "tso"]
ipv = ctl.get_alias("ipv")
netperf_duration = ctl.get_alias("netperf_duration")
+mtu = ctl.get_alias("mtu")
ping_mod = ctl.get_module("IcmpPing",
options={
@@ -92,6 +93,16 @@ netperf_cli_udp6 = ctl.get_module("Netperf",
"-L %s -6" % h2.get_ip("vlan10", 1)
})
+# configure mtu
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("nic"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("tap"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("br"), mtu))
+
+g1.run("ip link set dev %s mtu %s" % (g1.get_devname("guestnic"), mtu))
+g1.run("ip link set dev %s mtu %s" % (g1.get_devname("vlan10"), mtu))
+
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("nic"), mtu))
+
ctl.wait(15)
for offload in offloads:
diff --git a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.xml b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.xml
index ea49082..f552876 100644
--- a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.xml
+++ b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_guest.xml
@@ -2,6 +2,7 @@
<define>
<alias name="ipv" value="both" />
<alias name="netperf_duration" value="60" />
+ <alias name="mtu" value="1500" />
</define>
<network>
<host id="host1">
diff --git a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.py b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.py
index 0344641..c8d22c3 100644
--- a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.py
+++ b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.py
@@ -20,6 +20,7 @@ offloads = ["gso", "gro", "tso"]
ipv = ctl.get_alias("ipv")
netperf_duration = ctl.get_alias("netperf_duration")
+mtu = ctl.get_alias("mtu")
ping_mod = ctl.get_module("IcmpPing",
options={
@@ -92,6 +93,17 @@ netperf_cli_udp6 = ctl.get_module("Netperf",
"-L %s -6" % h2.get_ip("vlan10", 1)
})
+# configure mtu
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("nic"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("tap"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("vlan10"), mtu))
+h1.run("ip link set dev %s mtu %s" % (h1.get_devname("br"), mtu))
+
+g1.run("ip link set dev %s mtu %s" % (g1.get_devname("guestnic"), mtu))
+
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("nic"), mtu))
+h2.run("ip link set dev %s mtu %s" % (h2.get_devname("vlan10"), mtu))
+
ctl.wait(15)
for offload in offloads:
diff --git a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.xml b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.xml
index 3649ca9..82a7642 100644
--- a/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.xml
+++ b/recipes/regression_tests/phase1/virtual_bridge_vlan_in_host.xml
@@ -2,6 +2,7 @@
<define>
<alias name="ipv" value="both" />
<alias name="netperf_duration" value="60" />
+ <alias name="mtu" value="1500" />
</define>
<network>
<host id="host1">
--
2.1.0
7 years, 10 months
[PATCH] Return None from Device._ethtool_get_driver() on failed query
by Artem Savkov
When ethtool fails to find a driver for some reason lnst-slave fails with a
"AttributeError: 'NoneType' object has no attribute 'group'" exception. This
should be handled gracefully.
This might happen for example on 2.6.32-431.*.el6 kernels probably due to a kernel
bug: the kernel sends a RTM_DELLINK message and right after that a RTM_NEWLINK
for the same device which triggers device.init_netlink() call and a driver
query for a missing device.
Signed-off-by: Artem Savkov <asavkov(a)redhat.com>
---
lnst/Slave/InterfaceManager.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py
index 01b23e5..b014c6b 100644
--- a/lnst/Slave/InterfaceManager.py
+++ b/lnst/Slave/InterfaceManager.py
@@ -498,7 +498,7 @@ class Device(object):
return 'loopback'
out, _ = exec_cmd("ethtool -i %s" % self._name, False, False, False)
match = re.search("^driver: (.*)$", out, re.MULTILINE)
- return match.group(1)
+ return match.group(1) if match else None
def get_if_data(self):
if_data = {"devname": self._name,
--
2.1.0
7 years, 10 months
[PATCH] Refactorization of LNST Pool Wizard
by Jiri Prochazka
MessageDispatcherLite.py
+ Remove unused import
+ Make pep8 compliant
+ Rewrite one error message
lnst-pool-wizard
+ Make pep8 compliant
+ Print error messages on stderr
+ Changes correspoding with Wizard.py refactorization
Wizard.py
+ Make pep8 compliant
+ Convert public methods to private
+ Use private attributes for storing hostname, port and mode
+ Most of the methods reworked to be more clean and readable
+ Setup for implementation of support for virtual machines
Closes #129
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
---
lnst-pool-wizard | 24 +--
lnst/Controller/MessageDispatcherLite.py | 9 +-
lnst/Controller/Wizard.py | 247 ++++++++++++++++++-------------
3 files changed, 159 insertions(+), 121 deletions(-)
diff --git a/lnst-pool-wizard b/lnst-pool-wizard
index c788688..af3ca6c 100755
--- a/lnst-pool-wizard
+++ b/lnst-pool-wizard
@@ -17,48 +17,48 @@ import getopt
RETVAL_PASS = 0
RETVAL_ERR = 1
-def help(retval = 0):
+
+def help(retval=0):
print "Usage:\n"\
" lnst-pool-wizard [mode] [hostname[:port]]\n"\
"\n"\
"Modes:\n"\
" -h, --help display this help text and exit\n"\
" -i, --interactive start wizard in interactive mode (this "\
- "is default mode)\n"\
+ "is default mode)\n"\
" -n, --noninteractive start wizard in noninteractive mode\n"\
"Examples:\n"\
" lnst-pool-wizard --interactive\n"\
" lnst-pool-wizard --noninteractive 192.168.122.2\n"\
- " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.3:9999 192.168.122.4\n"
+ " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.4\n"
sys.exit(retval)
def main():
try:
opts, args = getopt.getopt(
- sys.argv[1:],
- "hin",
- ["help", "interactive", "noninteractive"]
+ sys.argv[1:],
+ "hinv",
+ ["help", "interactive", "noninteractive", "virtual"]
)
except getopt.GetoptError as err:
- print str(err)
+ sys.stderr.write(str(err))
help(RETVAL_ERR)
- wizard = Wizard()
for opt, arg in opts:
if opt in ("-h", "--help"):
help()
elif opt in ("-i", "--interactive"):
- wizard.interactive()
+ Wizard("interactive")
sys.exit(RETVAL_PASS)
elif opt in ("-n", "--noninteractive"):
if not args:
- print "No hostname entered!"
+ sys.stderr.write("No hostname entered!")
return RETVAL_ERR
- wizard.noninteractive(args)
+ Wizard("noninteractive", args)
sys.exit(RETVAL_PASS)
else:
help(RETVAL_ERR)
- wizard.interactive()
+ Wizard("interactive")
sys.exit(RETVAL_PASS)
diff --git a/lnst/Controller/MessageDispatcherLite.py b/lnst/Controller/MessageDispatcherLite.py
index 2973763..623c232 100644
--- a/lnst/Controller/MessageDispatcherLite.py
+++ b/lnst/Controller/MessageDispatcherLite.py
@@ -15,7 +15,9 @@ from lnst.Common.ConnectionHandler import send_data
from lnst.Common.ConnectionHandler import ConnectionHandler
from lnst.Controller.NetTestController import NetTestError
+
class MessageDispatcherLite(ConnectionHandler):
+
def __init__(self):
super(MessageDispatcherLite, self).__init__()
@@ -25,9 +27,8 @@ class MessageDispatcherLite(ConnectionHandler):
def send_message(self, machine_id, data):
soc = self.get_connection(1)
- if send_data(soc, data) == False:
- msg = "Connection error from slave %s" % str(1)
- raise NetTestError(msg)
+ if send_data(soc, data) is False:
+ raise NetTestError("Connection error from slave")
def wait_for_result(self, machine_id):
wait = True
@@ -47,7 +48,7 @@ class MessageDispatcherLite(ConnectionHandler):
if connected_slaves != remaining_slaves:
disconnected_slaves = set(connected_slaves) -\
- set(remaining_slaves)
+ set(remaining_slaves)
msg = "Slaves " + str(list(disconnected_slaves)) + \
" disconnected from the controller."
raise NetTestError(msg)
diff --git a/lnst/Controller/Wizard.py b/lnst/Controller/Wizard.py
index c38aae5..9b21aa9 100644
--- a/lnst/Controller/Wizard.py
+++ b/lnst/Controller/Wizard.py
@@ -9,6 +9,7 @@ published by the Free Software Foundation; see COPYING for details.
__author__ = """
jprochaz(a)redhat.com (Jiri Prochazka)
"""
+
import socket
import sys
import os
@@ -19,61 +20,50 @@ from lnst.Common.Utils import mkdir_p
from lnst.Common.Config import DefaultRPCPort
from xml.dom.minidom import getDOMImplementation
+
class Wizard:
- def __init__(self):
+ def __init__(self, mode, hostlist=None):
self._msg_dispatcher = MessageDispatcherLite()
+ self._hostname = ""
+ self._port = DefaultRPCPort
self._pool_dir = os.path.expanduser("~/.lnst/pool")
-
- def interactive(self):
+ self._mode = mode
+ if self._mode == "interactive":
+ self._interactive()
+ elif self._mode == "noninteractive":
+ self._noninteractive(hostlist)
+ else:
+ raise WizardException("Unknown mode selected\n")
+
+ def _interactive(self):
"""
- Starts Wizard in interactive mode. Wizard requests hostname and port
+ Starts Wizard in an interactive mode. Wizard requests hostname and port
from user, tests connectivity to entered host, then he tries to connect
- and configure slave on host machine, and finally he requests list of
- ethernet interfaces in state DOWN. Then he writes them into .xml file
+ and configure Slave on host machine and finally he requests list of
+ ethernet interfaces in down state and creates slave machine .xml file
named by user. User can choose which interfaces should be added to the
.xml file.
"""
+ while True:
+ self._set_pool_dir()
+ self._set_hostname()
+ self._set_port()
- pool_dir = raw_input("Enter path to pool directory "\
- "(default: ~/.lnst/pool): ")
- if pool_dir != "":
- self._pool_dir = os.path.expanduser(pool_dir)
- print "Pool directory set to %s" % self._pool_dir
+ if not self._connect_to_and_configure_machine():
+ continue
- while True:
- while True:
- hostname = raw_input("Enter hostname: ")
- try:
- # Tests if hostname is translatable into IP address
- socket.gethostbyname(hostname)
- break
- except:
- sys.stderr.write("Hostname is not translatable into valid"\
- " IP address.\n")
- continue
- while True:
- port = raw_input("Enter port(default: 9999): ")
- if port == "":
- port = DefaultRPCPort
- try:
- port = int(port)
- break
- except:
- sys.stderr.write("Invalid port.")
- continue
- msg = self._get_suitable_interfaces(socket.gethostbyname(hostname),\
- port)
- if msg:
- self._write_to_xml(msg, hostname, port, "interactive")
- next_machine = raw_input("Do you want to add another machine? "\
- "[Y/n]: ")
- if next_machine.lower() == 'y' or next_machine == "":
+ machine_interfaces = self._get_machine_interfaces()
+ if machine_interfaces:
+ self._write_to_xml(machine_interfaces)
+
+ msg = "Do you want to add another machine? [Y/n]: "
+ next_machine = raw_input(msg)
+ if next_machine.lower() == "y" or next_machine == "":
continue
else:
- break
- return
+ return
- def noninteractive(self, hostlist):
+ def _noninteractive(self, hostlist):
"""
Starts Wizard in noninteractive mode. Wizard gets list of hosts and
ports as arguments. He tries to connect and get info about suitable
@@ -81,78 +71,124 @@ class Wizard:
user about anything, it automatically adds all suitable interfaces into
.xml file named the same as the hostname of the selected machine.
"""
- self._mode = "noninteractive"
-
for host in hostlist:
- # Checks if port was entered along with hostname
+ # Check if port was entered along with hostname
if host.find(":") != -1:
- hostname = host.split(':')[0]
- try:
- port = int(host.split(':')[1])
- except:
- port = DefaultRPCPort
+ self._hostname = host.split(":")[0]
+ if self._hostname == "":
+ msg = "'%s' does not contain valid hostname\n" % host
+ sys.stderr.write(msg)
+ continue
+ try:
+ self._port = int(host.split(":")[1])
+ except:
+ msg = "Invalid port entered, "\
+ "using '%s' instead\n" % self._port
+ sys.stderr.write(msg)
else:
- hostname = host
- port = DefaultRPCPort
- msg = self._get_suitable_interfaces(hostname, port)
- if not msg:
- continue
- self._write_to_xml(msg, hostname, port, "noninteractive")
+ self._hostname = host
- def _get_suitable_interfaces(self, hostname, port):
- """
- Calls all functions, which are used by both interactive and
- noninteractive mode to get list of suitable interfaces. The list is
- saved to variable msg.
- """
- if not test_tcp_connection(hostname, port):
- sys.stderr.write("Host destination '%s:%s' unreachable.\n"
- % (hostname, port))
- return False
- if not self._connect_and_configure_machine(hostname, port):
- return False
- msg = self._get_device_ifcs(hostname, port)
- self._msg_dispatcher.disconnect_slave(1)
- return msg
+ if not self._check_hostname():
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
+ if not self._connect_to_and_configure_machine():
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
+ machine_interfaces = self._get_machine_interfaces()
+ if machine_interfaces:
+ self._write_to_xml(machine_interfaces)
- def _get_device_ifcs(self, hostname, port):
+ def _get_machine_interfaces(self):
"""
Sends RPC call request to Slave to call function get_devices, which
- returns list of ethernet devices which are in state DOWN.
+ returns list of ethernet devices which are in down state.
"""
- msg = self._machine._rpc_call("get_devices")
- if msg == {}:
- sys.stderr.write("No suitable interfaces found on the slave "\
- "'%s:%s'.\n" % (hostname, port))
+ machine_interfaces = self._machine._rpc_call("get_devices")
+ self._msg_dispatcher.disconnect_slave(1)
+ if machine_interfaces == {}:
+ sys.stderr.write("No suitable interfaces found on the host "
+ "'%s:%s'\n" % (self._hostname, self._port))
return False
- return msg
+ return machine_interfaces
- def _connect_and_configure_machine(self, hostname, port):
+ def _connect_to_and_configure_machine(self):
"""
Connects to Slave and configures it
"""
+ if not test_tcp_connection(self._hostname, self._port):
+ sys.stderr.write("Host destination '%s:%s' unreachable\n"
+ % (self._hostname, self._port))
+ return False
try:
- self._machine = Machine(1, hostname, None, port)
+ self._machine = Machine(1, self._hostname, None, self._port)
self._machine.set_rpc(self._msg_dispatcher)
self._machine.configure("MachinePoolWizard")
return True
except:
- sys.stderr.write("Remote machine '%s:%s' configuration failed!\n"
- % (hostname, port))
+ sys.stderr.write("Remote host '%s:%s' configuration failed\n"
+ % (self._hostname, self._port))
self._msg_dispatcher.disconnect_slave(1)
return False
- def _write_to_xml(self, msg, hostname, port, mode):
+ def _set_hostname(self):
+ while True:
+ self._hostname = raw_input("Enter hostname: ")
+ if self._hostname == "":
+ sys.stderr.write("No hostname entered\n")
+ continue
+ if self._check_hostname():
+ return
+
+ def _check_hostname(self):
+ try:
+ # Test if hostname is translatable into IP address
+ socket.gethostbyname(self._hostname)
+ return True
+ except:
+ sys.stderr.write("Hostname '%s' is not translatable into a valid "
+ "IP address\n" % self._hostname)
+ return False
+
+ def _set_port(self):
+ while True:
+ port = raw_input("Enter port (default: 9999): ")
+ if port == "":
+ return
+ else:
+ try:
+ self._port = int(port)
+ return
+ except:
+ sys.stderr.write("Invalid port\n")
+
+ def _set_pool_dir(self):
+ while True:
+ pool_dir = raw_input("Enter path to a pool directory "
+ "(default: ~/.lnst/pool/): ")
+ if pool_dir == "":
+ return
+
+ self._pool_dir = os.path.expanduser(pool_dir)
+ # check if dir is writable
+ if os.access(os.path.dirname(self._pool_dir), os.W_OK):
+ print "Pool directory set to '%s'" % self._pool_dir
+ return
+ # dir is not writable
+ else:
+ sys.stderr.write("Directory '%s' is not writable\n"
+ % self._pool_dir)
+
+ def _write_to_xml(self, machine_interfaces):
"""
Used for writing desired output into .xml file. In interactive mode,
user is prompted for every interface, in noninteractive mode all
interfaces are automatically added to the .xml file.
"""
- if mode == "interactive":
- output_file = raw_input("Enter the name of the output .xml file "\
+ if self._mode == "interactive":
+ output_file = raw_input("Enter the name of the output .xml file "
"(without .xml, default is hostname.xml): ")
- if mode == "noninteractive" or output_file == "":
- output_file = hostname
+ if self._mode == "noninteractive" or output_file == "":
+ output_file = self._hostname
impl = getDOMImplementation()
doc = impl.createDocument(None, "slavemachine", None)
@@ -161,47 +197,48 @@ class Wizard:
top_el.appendChild(params_el)
param_el = doc.createElement("param")
param_el.setAttribute("name", "hostname")
- param_el.setAttribute("value", hostname)
+ param_el.setAttribute("value", self._hostname)
params_el.appendChild(param_el)
interfaces_el = doc.createElement("interfaces")
top_el.appendChild(interfaces_el)
- devices_added = 0
- for interface in msg.itervalues():
- if mode == 'interactive':
+ interfaces_added = 0
+ for iface in machine_interfaces.itervalues():
+ if self._mode == "interactive":
answer = raw_input("Do you want to add interface '%s' (%s) "
- "to the recipe? [Y/n]" % (interface['name'],
- interface['hwaddr']))
- if mode == "noninteractive" or answer.lower() == 'y'\
+ "to the recipe? [Y/n]: " % (iface["name"],
+ iface["hwaddr"]))
+ if self._mode == "noninteractive" or answer.lower() == "y"\
or answer == "":
- devices_added += 1
+ interfaces_added += 1
eth_el = doc.createElement("eth")
- eth_el.setAttribute("id", interface['name'])
+ eth_el.setAttribute("id", iface["name"])
eth_el.setAttribute("label", "default_network")
interfaces_el.appendChild(eth_el)
params_el = doc.createElement("params")
eth_el.appendChild(params_el)
param_el = doc.createElement("param")
param_el.setAttribute("name", "hwaddr")
- param_el.setAttribute("value", interface['hwaddr'])
+ param_el.setAttribute("value", iface["hwaddr"])
params_el.appendChild(param_el)
- if devices_added == 0:
- sys.stderr.write("You didn't add any interface, no file '%s.xml' "\
- "will be created!\n" % output_file)
+ if interfaces_added == 0:
+ sys.stderr.write("You didn't add any interface, no file "
+ "%s.xml will be created\n" % output_file)
return
mkdir_p(self._pool_dir)
try:
- f = open(self._pool_dir + "/" + output_file + ".xml", 'w')
+ f = open(self._pool_dir + "/" + output_file + ".xml", "w")
f.write(doc.toprettyxml())
f.close()
except:
- sys.stderr.write("File '%s.xml' could not be opened "\
- "or data written." % output_file+"\n")
- raise WizardException()
+ msg = "File '%s/%s.xml' could not be opened or data written\n"\
+ % (self._pool_dir, output_file)
+ raise WizardException(msg)
+
+ print "File '%s.xml' successfuly created" % output_file
- print "File '%s.xml' successfuly created." % output_file
class WizardException(Exception):
pass
--
2.4.3
7 years, 11 months
[PATCH] Refactorization of LNST Pool Wizard
by Jiri Prochazka
MessageDispatcherLite.py
+ Remove unused import
+ Make pep8 compliant
+ Rewrite one error message
lnst-pool-wizard
+ Make pep8 compliant
+ Print error messages on stderr
+ Changes correspoding with Wizard.py refactorization
Wizard.py
+ Make pep8 compliant
+ Convert public methods to private
+ Use private attributes for storing hostname, port and mode
+ Most of the methods reworked to be more clean and readable
+ Setup for implementation of support for virtual machines
Closes #129
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
---
lnst-pool-wizard | 24 +--
lnst/Controller/MessageDispatcherLite.py | 11 +-
lnst/Controller/Wizard.py | 247 ++++++++++++++++++-------------
3 files changed, 160 insertions(+), 122 deletions(-)
diff --git a/lnst-pool-wizard b/lnst-pool-wizard
index c788688..af3ca6c 100755
--- a/lnst-pool-wizard
+++ b/lnst-pool-wizard
@@ -17,48 +17,48 @@ import getopt
RETVAL_PASS = 0
RETVAL_ERR = 1
-def help(retval = 0):
+
+def help(retval=0):
print "Usage:\n"\
" lnst-pool-wizard [mode] [hostname[:port]]\n"\
"\n"\
"Modes:\n"\
" -h, --help display this help text and exit\n"\
" -i, --interactive start wizard in interactive mode (this "\
- "is default mode)\n"\
+ "is default mode)\n"\
" -n, --noninteractive start wizard in noninteractive mode\n"\
"Examples:\n"\
" lnst-pool-wizard --interactive\n"\
" lnst-pool-wizard --noninteractive 192.168.122.2\n"\
- " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.3:9999 192.168.122.4\n"
+ " lnst-pool-wizard -n 192.168.122.2:8888 192.168.122.4\n"
sys.exit(retval)
def main():
try:
opts, args = getopt.getopt(
- sys.argv[1:],
- "hin",
- ["help", "interactive", "noninteractive"]
+ sys.argv[1:],
+ "hinv",
+ ["help", "interactive", "noninteractive", "virtual"]
)
except getopt.GetoptError as err:
- print str(err)
+ sys.stderr.write(str(err))
help(RETVAL_ERR)
- wizard = Wizard()
for opt, arg in opts:
if opt in ("-h", "--help"):
help()
elif opt in ("-i", "--interactive"):
- wizard.interactive()
+ Wizard("interactive")
sys.exit(RETVAL_PASS)
elif opt in ("-n", "--noninteractive"):
if not args:
- print "No hostname entered!"
+ sys.stderr.write("No hostname entered!")
return RETVAL_ERR
- wizard.noninteractive(args)
+ Wizard("noninteractive", args)
sys.exit(RETVAL_PASS)
else:
help(RETVAL_ERR)
- wizard.interactive()
+ Wizard("interactive")
sys.exit(RETVAL_PASS)
diff --git a/lnst/Controller/MessageDispatcherLite.py b/lnst/Controller/MessageDispatcherLite.py
index e86c077..623c232 100644
--- a/lnst/Controller/MessageDispatcherLite.py
+++ b/lnst/Controller/MessageDispatcherLite.py
@@ -11,11 +11,13 @@ __author__ = """
jpirko(a)redhat.com (Jiri Pirko)
"""
-from lnst.Common.ConnectionHandler import send_data, recv_data
+from lnst.Common.ConnectionHandler import send_data
from lnst.Common.ConnectionHandler import ConnectionHandler
from lnst.Controller.NetTestController import NetTestError
+
class MessageDispatcherLite(ConnectionHandler):
+
def __init__(self):
super(MessageDispatcherLite, self).__init__()
@@ -25,9 +27,8 @@ class MessageDispatcherLite(ConnectionHandler):
def send_message(self, machine_id, data):
soc = self.get_connection(1)
- if send_data(soc, data) == False:
- msg = "Connection error from slave %s" % str(1)
- raise NetTestError(msg)
+ if send_data(soc, data) is False:
+ raise NetTestError("Connection error from slave")
def wait_for_result(self, machine_id):
wait = True
@@ -47,7 +48,7 @@ class MessageDispatcherLite(ConnectionHandler):
if connected_slaves != remaining_slaves:
disconnected_slaves = set(connected_slaves) -\
- set(remaining_slaves)
+ set(remaining_slaves)
msg = "Slaves " + str(list(disconnected_slaves)) + \
" disconnected from the controller."
raise NetTestError(msg)
diff --git a/lnst/Controller/Wizard.py b/lnst/Controller/Wizard.py
index c38aae5..9b21aa9 100644
--- a/lnst/Controller/Wizard.py
+++ b/lnst/Controller/Wizard.py
@@ -9,6 +9,7 @@ published by the Free Software Foundation; see COPYING for details.
__author__ = """
jprochaz(a)redhat.com (Jiri Prochazka)
"""
+
import socket
import sys
import os
@@ -19,61 +20,50 @@ from lnst.Common.Utils import mkdir_p
from lnst.Common.Config import DefaultRPCPort
from xml.dom.minidom import getDOMImplementation
+
class Wizard:
- def __init__(self):
+ def __init__(self, mode, hostlist=None):
self._msg_dispatcher = MessageDispatcherLite()
+ self._hostname = ""
+ self._port = DefaultRPCPort
self._pool_dir = os.path.expanduser("~/.lnst/pool")
-
- def interactive(self):
+ self._mode = mode
+ if self._mode == "interactive":
+ self._interactive()
+ elif self._mode == "noninteractive":
+ self._noninteractive(hostlist)
+ else:
+ raise WizardException("Unknown mode selected\n")
+
+ def _interactive(self):
"""
- Starts Wizard in interactive mode. Wizard requests hostname and port
+ Starts Wizard in an interactive mode. Wizard requests hostname and port
from user, tests connectivity to entered host, then he tries to connect
- and configure slave on host machine, and finally he requests list of
- ethernet interfaces in state DOWN. Then he writes them into .xml file
+ and configure Slave on host machine and finally he requests list of
+ ethernet interfaces in down state and creates slave machine .xml file
named by user. User can choose which interfaces should be added to the
.xml file.
"""
+ while True:
+ self._set_pool_dir()
+ self._set_hostname()
+ self._set_port()
- pool_dir = raw_input("Enter path to pool directory "\
- "(default: ~/.lnst/pool): ")
- if pool_dir != "":
- self._pool_dir = os.path.expanduser(pool_dir)
- print "Pool directory set to %s" % self._pool_dir
+ if not self._connect_to_and_configure_machine():
+ continue
- while True:
- while True:
- hostname = raw_input("Enter hostname: ")
- try:
- # Tests if hostname is translatable into IP address
- socket.gethostbyname(hostname)
- break
- except:
- sys.stderr.write("Hostname is not translatable into valid"\
- " IP address.\n")
- continue
- while True:
- port = raw_input("Enter port(default: 9999): ")
- if port == "":
- port = DefaultRPCPort
- try:
- port = int(port)
- break
- except:
- sys.stderr.write("Invalid port.")
- continue
- msg = self._get_suitable_interfaces(socket.gethostbyname(hostname),\
- port)
- if msg:
- self._write_to_xml(msg, hostname, port, "interactive")
- next_machine = raw_input("Do you want to add another machine? "\
- "[Y/n]: ")
- if next_machine.lower() == 'y' or next_machine == "":
+ machine_interfaces = self._get_machine_interfaces()
+ if machine_interfaces:
+ self._write_to_xml(machine_interfaces)
+
+ msg = "Do you want to add another machine? [Y/n]: "
+ next_machine = raw_input(msg)
+ if next_machine.lower() == "y" or next_machine == "":
continue
else:
- break
- return
+ return
- def noninteractive(self, hostlist):
+ def _noninteractive(self, hostlist):
"""
Starts Wizard in noninteractive mode. Wizard gets list of hosts and
ports as arguments. He tries to connect and get info about suitable
@@ -81,78 +71,124 @@ class Wizard:
user about anything, it automatically adds all suitable interfaces into
.xml file named the same as the hostname of the selected machine.
"""
- self._mode = "noninteractive"
-
for host in hostlist:
- # Checks if port was entered along with hostname
+ # Check if port was entered along with hostname
if host.find(":") != -1:
- hostname = host.split(':')[0]
- try:
- port = int(host.split(':')[1])
- except:
- port = DefaultRPCPort
+ self._hostname = host.split(":")[0]
+ if self._hostname == "":
+ msg = "'%s' does not contain valid hostname\n" % host
+ sys.stderr.write(msg)
+ continue
+ try:
+ self._port = int(host.split(":")[1])
+ except:
+ msg = "Invalid port entered, "\
+ "using '%s' instead\n" % self._port
+ sys.stderr.write(msg)
else:
- hostname = host
- port = DefaultRPCPort
- msg = self._get_suitable_interfaces(hostname, port)
- if not msg:
- continue
- self._write_to_xml(msg, hostname, port, "noninteractive")
+ self._hostname = host
- def _get_suitable_interfaces(self, hostname, port):
- """
- Calls all functions, which are used by both interactive and
- noninteractive mode to get list of suitable interfaces. The list is
- saved to variable msg.
- """
- if not test_tcp_connection(hostname, port):
- sys.stderr.write("Host destination '%s:%s' unreachable.\n"
- % (hostname, port))
- return False
- if not self._connect_and_configure_machine(hostname, port):
- return False
- msg = self._get_device_ifcs(hostname, port)
- self._msg_dispatcher.disconnect_slave(1)
- return msg
+ if not self._check_hostname():
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
+ if not self._connect_to_and_configure_machine():
+ sys.stderr.write("Skipping host '%s'\n" % host)
+ continue
+ machine_interfaces = self._get_machine_interfaces()
+ if machine_interfaces:
+ self._write_to_xml(machine_interfaces)
- def _get_device_ifcs(self, hostname, port):
+ def _get_machine_interfaces(self):
"""
Sends RPC call request to Slave to call function get_devices, which
- returns list of ethernet devices which are in state DOWN.
+ returns list of ethernet devices which are in down state.
"""
- msg = self._machine._rpc_call("get_devices")
- if msg == {}:
- sys.stderr.write("No suitable interfaces found on the slave "\
- "'%s:%s'.\n" % (hostname, port))
+ machine_interfaces = self._machine._rpc_call("get_devices")
+ self._msg_dispatcher.disconnect_slave(1)
+ if machine_interfaces == {}:
+ sys.stderr.write("No suitable interfaces found on the host "
+ "'%s:%s'\n" % (self._hostname, self._port))
return False
- return msg
+ return machine_interfaces
- def _connect_and_configure_machine(self, hostname, port):
+ def _connect_to_and_configure_machine(self):
"""
Connects to Slave and configures it
"""
+ if not test_tcp_connection(self._hostname, self._port):
+ sys.stderr.write("Host destination '%s:%s' unreachable\n"
+ % (self._hostname, self._port))
+ return False
try:
- self._machine = Machine(1, hostname, None, port)
+ self._machine = Machine(1, self._hostname, None, self._port)
self._machine.set_rpc(self._msg_dispatcher)
self._machine.configure("MachinePoolWizard")
return True
except:
- sys.stderr.write("Remote machine '%s:%s' configuration failed!\n"
- % (hostname, port))
+ sys.stderr.write("Remote host '%s:%s' configuration failed\n"
+ % (self._hostname, self._port))
self._msg_dispatcher.disconnect_slave(1)
return False
- def _write_to_xml(self, msg, hostname, port, mode):
+ def _set_hostname(self):
+ while True:
+ self._hostname = raw_input("Enter hostname: ")
+ if self._hostname == "":
+ sys.stderr.write("No hostname entered\n")
+ continue
+ if self._check_hostname():
+ return
+
+ def _check_hostname(self):
+ try:
+ # Test if hostname is translatable into IP address
+ socket.gethostbyname(self._hostname)
+ return True
+ except:
+ sys.stderr.write("Hostname '%s' is not translatable into a valid "
+ "IP address\n" % self._hostname)
+ return False
+
+ def _set_port(self):
+ while True:
+ port = raw_input("Enter port (default: 9999): ")
+ if port == "":
+ return
+ else:
+ try:
+ self._port = int(port)
+ return
+ except:
+ sys.stderr.write("Invalid port\n")
+
+ def _set_pool_dir(self):
+ while True:
+ pool_dir = raw_input("Enter path to a pool directory "
+ "(default: ~/.lnst/pool/): ")
+ if pool_dir == "":
+ return
+
+ self._pool_dir = os.path.expanduser(pool_dir)
+ # check if dir is writable
+ if os.access(os.path.dirname(self._pool_dir), os.W_OK):
+ print "Pool directory set to '%s'" % self._pool_dir
+ return
+ # dir is not writable
+ else:
+ sys.stderr.write("Directory '%s' is not writable\n"
+ % self._pool_dir)
+
+ def _write_to_xml(self, machine_interfaces):
"""
Used for writing desired output into .xml file. In interactive mode,
user is prompted for every interface, in noninteractive mode all
interfaces are automatically added to the .xml file.
"""
- if mode == "interactive":
- output_file = raw_input("Enter the name of the output .xml file "\
+ if self._mode == "interactive":
+ output_file = raw_input("Enter the name of the output .xml file "
"(without .xml, default is hostname.xml): ")
- if mode == "noninteractive" or output_file == "":
- output_file = hostname
+ if self._mode == "noninteractive" or output_file == "":
+ output_file = self._hostname
impl = getDOMImplementation()
doc = impl.createDocument(None, "slavemachine", None)
@@ -161,47 +197,48 @@ class Wizard:
top_el.appendChild(params_el)
param_el = doc.createElement("param")
param_el.setAttribute("name", "hostname")
- param_el.setAttribute("value", hostname)
+ param_el.setAttribute("value", self._hostname)
params_el.appendChild(param_el)
interfaces_el = doc.createElement("interfaces")
top_el.appendChild(interfaces_el)
- devices_added = 0
- for interface in msg.itervalues():
- if mode == 'interactive':
+ interfaces_added = 0
+ for iface in machine_interfaces.itervalues():
+ if self._mode == "interactive":
answer = raw_input("Do you want to add interface '%s' (%s) "
- "to the recipe? [Y/n]" % (interface['name'],
- interface['hwaddr']))
- if mode == "noninteractive" or answer.lower() == 'y'\
+ "to the recipe? [Y/n]: " % (iface["name"],
+ iface["hwaddr"]))
+ if self._mode == "noninteractive" or answer.lower() == "y"\
or answer == "":
- devices_added += 1
+ interfaces_added += 1
eth_el = doc.createElement("eth")
- eth_el.setAttribute("id", interface['name'])
+ eth_el.setAttribute("id", iface["name"])
eth_el.setAttribute("label", "default_network")
interfaces_el.appendChild(eth_el)
params_el = doc.createElement("params")
eth_el.appendChild(params_el)
param_el = doc.createElement("param")
param_el.setAttribute("name", "hwaddr")
- param_el.setAttribute("value", interface['hwaddr'])
+ param_el.setAttribute("value", iface["hwaddr"])
params_el.appendChild(param_el)
- if devices_added == 0:
- sys.stderr.write("You didn't add any interface, no file '%s.xml' "\
- "will be created!\n" % output_file)
+ if interfaces_added == 0:
+ sys.stderr.write("You didn't add any interface, no file "
+ "%s.xml will be created\n" % output_file)
return
mkdir_p(self._pool_dir)
try:
- f = open(self._pool_dir + "/" + output_file + ".xml", 'w')
+ f = open(self._pool_dir + "/" + output_file + ".xml", "w")
f.write(doc.toprettyxml())
f.close()
except:
- sys.stderr.write("File '%s.xml' could not be opened "\
- "or data written." % output_file+"\n")
- raise WizardException()
+ msg = "File '%s/%s.xml' could not be opened or data written\n"\
+ % (self._pool_dir, output_file)
+ raise WizardException(msg)
+
+ print "File '%s.xml' successfuly created" % output_file
- print "File '%s.xml' successfuly created." % output_file
class WizardException(Exception):
pass
--
2.4.3
7 years, 11 months
[PATCH] recipes: use two ethernet devices for phase2 team recipes
by Jan Tluka
The phase1 tests use two ethernet devices for bond setups. Most of the
machines in our lab have 2 devices available for testing. This patch
modifies phase2 team recipes so that team device is grouping two devices
instead of original three.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
recipes/regression_tests/phase2/3_vlans_over_active_backup_team.xml | 2 --
recipes/regression_tests/phase2/3_vlans_over_round_robin_team.xml | 2 --
recipes/regression_tests/phase2/3_vlans_over_team.py | 2 --
recipes/regression_tests/phase2/active_backup_team.xml | 2 --
.../phase2/active_backup_team_vs_active_backup_bond.xml | 4 ----
.../phase2/active_backup_team_vs_round_robin_bond.xml | 4 ----
recipes/regression_tests/phase2/round_robin_double_team.xml | 4 ----
recipes/regression_tests/phase2/round_robin_team.xml | 2 --
.../phase2/round_robin_team_vs_active_backup_bond.xml | 4 ----
9 files changed, 26 deletions(-)
diff --git a/recipes/regression_tests/phase2/3_vlans_over_active_backup_team.xml b/recipes/regression_tests/phase2/3_vlans_over_active_backup_team.xml
index 87a056a..de4738f 100644
--- a/recipes/regression_tests/phase2/3_vlans_over_active_backup_team.xml
+++ b/recipes/regression_tests/phase2/3_vlans_over_active_backup_team.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="1.2.3.4/24" />
diff --git a/recipes/regression_tests/phase2/3_vlans_over_round_robin_team.xml b/recipes/regression_tests/phase2/3_vlans_over_round_robin_team.xml
index 612fe01..bf1bcf9 100644
--- a/recipes/regression_tests/phase2/3_vlans_over_round_robin_team.xml
+++ b/recipes/regression_tests/phase2/3_vlans_over_round_robin_team.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="1.2.3.4/24" />
diff --git a/recipes/regression_tests/phase2/3_vlans_over_team.py b/recipes/regression_tests/phase2/3_vlans_over_team.py
index fc04b29..0913d27 100644
--- a/recipes/regression_tests/phase2/3_vlans_over_team.py
+++ b/recipes/regression_tests/phase2/3_vlans_over_team.py
@@ -119,8 +119,6 @@ for vlan1 in vlans:
offload, state))
m1.run("ethtool -K %s %s %s" % (m1.get_devname("eth2"),
offload, state))
- m1.run("ethtool -K %s %s %s" % (m1.get_devname("eth3"),
- offload, state))
m2.run("ethtool -K %s %s %s" % (m2.get_devname("eth1"),
offload, state))
diff --git a/recipes/regression_tests/phase2/active_backup_team.xml b/recipes/regression_tests/phase2/active_backup_team.xml
index 1897d4f..fa91239 100644
--- a/recipes/regression_tests/phase2/active_backup_team.xml
+++ b/recipes/regression_tests/phase2/active_backup_team.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.1/24" />
diff --git a/recipes/regression_tests/phase2/active_backup_team_vs_active_backup_bond.xml b/recipes/regression_tests/phase2/active_backup_team_vs_active_backup_bond.xml
index 21b5463..7ce28d2 100644
--- a/recipes/regression_tests/phase2/active_backup_team_vs_active_backup_bond.xml
+++ b/recipes/regression_tests/phase2/active_backup_team_vs_active_backup_bond.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.1/24" />
@@ -35,7 +33,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<bond id="test_if">
<options>
<option name="mode" value="active-backup" />
@@ -44,7 +41,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.2/24" />
diff --git a/recipes/regression_tests/phase2/active_backup_team_vs_round_robin_bond.xml b/recipes/regression_tests/phase2/active_backup_team_vs_round_robin_bond.xml
index a949c07..30f95ca 100644
--- a/recipes/regression_tests/phase2/active_backup_team_vs_round_robin_bond.xml
+++ b/recipes/regression_tests/phase2/active_backup_team_vs_round_robin_bond.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.1/24" />
@@ -35,7 +33,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<bond id="test_if">
<options>
<option name="mode" value="balance-rr" />
@@ -44,7 +41,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.2/24" />
diff --git a/recipes/regression_tests/phase2/round_robin_double_team.xml b/recipes/regression_tests/phase2/round_robin_double_team.xml
index 450bad8..e553b43 100644
--- a/recipes/regression_tests/phase2/round_robin_double_team.xml
+++ b/recipes/regression_tests/phase2/round_robin_double_team.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.1/24" />
@@ -35,7 +33,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -48,7 +45,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.2/24" />
diff --git a/recipes/regression_tests/phase2/round_robin_team.xml b/recipes/regression_tests/phase2/round_robin_team.xml
index ba1b8fc..df91816 100644
--- a/recipes/regression_tests/phase2/round_robin_team.xml
+++ b/recipes/regression_tests/phase2/round_robin_team.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.1/24" />
diff --git a/recipes/regression_tests/phase2/round_robin_team_vs_active_backup_bond.xml b/recipes/regression_tests/phase2/round_robin_team_vs_active_backup_bond.xml
index e389126..19fad3e 100644
--- a/recipes/regression_tests/phase2/round_robin_team_vs_active_backup_bond.xml
+++ b/recipes/regression_tests/phase2/round_robin_team_vs_active_backup_bond.xml
@@ -9,7 +9,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<team id="test_if">
<options>
<option name="teamd_config">
@@ -22,7 +21,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.1/24" />
@@ -35,7 +33,6 @@
<interfaces>
<eth id="eth1" label="tnet" />
<eth id="eth2" label="tnet" />
- <eth id="eth3" label="tnet" />
<bond id="test_if">
<options>
<option name="mode" value="active-backup" />
@@ -44,7 +41,6 @@
<slaves>
<slave id="eth1" />
<slave id="eth2" />
- <slave id="eth3" />
</slaves>
<addresses>
<address value="192.168.0.2/24" />
--
2.1.0
7 years, 11 months