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