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@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@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
lnst-developers@lists.fedorahosted.org