Mega update - includes support for new AP8959 and very old AP9210 pdus, pduclient now includes a delay ability, and the driver plugin mechanism has been totally revamped. Warning this still needs work.

Change-Id: I49b1c24c963ea78ffd5e1667a54309a88b694c01
diff --git a/etc/lavapdu/lavapdu.conf b/etc/lavapdu/lavapdu.conf
index acded88..ae8cbc5 100644
--- a/etc/lavapdu/lavapdu.conf
+++ b/etc/lavapdu/lavapdu.conf
@@ -5,21 +5,28 @@
         "dbhost": "127.0.0.1",
         "dbuser": "pdudaemon",
         "dbpass": "pdudaemon",
-        "dbname": "lavapdu"
+        "dbname": "lavapdu",
+        "retries": 3,
+        "logging_level": "DEBUG"
     },
     "pdus": {
-        "pdu14": {
-            "driver": "apc9218"
+        "192.168.10.2": {
+            "driver": "apc9210"
         },
-        "pdu15": {
-            "driver": "apc9218"
+        "192.168.10.3": {
+            "driver": "apc7952",
+            "telnetport": 5023
         },
-        "localhost": {
-            "driver": "apc9218"
+        "192.168.10.4": {
+            "driver": "apc7952"
         },
-        "pdu04": {
-            "driver": "blah",
-            "snmp": "this"
+        "192.168.10.5": {
+            "driver": "apc8959",
+            "this": "that",
+            "something": "else"
+        },
+        "192.168.25.52": {
+            "driver": "apc7952"
         }
     }
 }
\ No newline at end of file
diff --git a/lavapdu-listen b/lavapdu-listen
index c6a22f0..ec5b00b 100755
--- a/lavapdu-listen
+++ b/lavapdu-listen
@@ -48,18 +48,12 @@
     """
     Read settings from config file, to listen to all hosts, hostname should be 0.0.0.0
     """
-    settings = {}
+    #settings = {}
     print("Reading settings from %s" % conffile)
     with open(filename) as stream:
         jobdata = stream.read()
         json_data = json.loads(jobdata)
-        settings['port'] = json_data["daemon"]['port']
-        settings['hostname'] = json_data["daemon"]['hostname']
-        settings['dbuser'] = json_data["daemon"]['dbuser']
-        settings['dbpass'] = json_data["daemon"]['dbpass']
-        settings['dbname'] = json_data["daemon"]['dbname']
-        settings['dbhost'] = json_data["daemon"]['dbhost']
-    return settings
+    return json_data
 
 if __name__ == '__main__':
     # instance settings come from django - the coordinator doesn't use django and is
@@ -83,13 +77,14 @@
             print "No such directory for specified logfile '%s'" % logfile
     open(logfile, 'w').close()
     level = logging.DEBUG
-    if options.loglevel == "DEBUG":
+    settings = settings["daemon"]
+    if settings["logging_level"] == "DEBUG":
         level = logging.DEBUG
-    if options.loglevel == "WARNING":
+    if settings["logging_level"] == "WARNING":
         level = logging.WARNING
-    if options.loglevel == "ERROR":
+    if settings["logging_level"] == "ERROR":
         level = logging.ERROR
-    if options.loglevel == "INFO":
+    if settings["logging_level"] == "INFO":
         level = logging.INFO
     client_logger, watched_file_handler = getDaemonLogger(logfile, loglevel=level,
                                                           log_format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
@@ -107,10 +102,7 @@
         files_preserve=[watched_file_handler.stream],
         stderr=watched_file_handler.stream,
         stdout=watched_file_handler.stream)
-    starter = {"logging_level": level,
-               "settings": settings}
     with context:
         logging.info("Running LAVA PDU Listener %s %s %d."
                      % (logfile, settings['hostname'], settings['port']))
-        #logging.getLogger().setLevel(options.loglevel)
-        ListenerServer(starter).start()
\ No newline at end of file
+        ListenerServer(settings).start()
\ No newline at end of file
diff --git a/lavapdu-runner b/lavapdu-runner
index dadf9f3..b1730ff 100755
--- a/lavapdu-runner
+++ b/lavapdu-runner
@@ -54,15 +54,7 @@
     with open(filename) as stream:
         jobdata = stream.read()
         json_data = json.loads(jobdata)
-        settings['port'] = json_data["daemon"]['port']
-        settings['hostname'] = json_data["daemon"]['hostname']
-        settings['dbuser'] = json_data["daemon"]['dbuser']
-        settings['dbpass'] = json_data["daemon"]['dbpass']
-        settings['dbname'] = json_data["daemon"]['dbname']
-        settings['dbhost'] = json_data["daemon"]['dbhost']
-        pdus = json_data["pdus"]
-    return (settings, pdus)
-
+    return json_data
 
 if __name__ == '__main__':
     # instance settings come from django - the coordinator doesn't use django and is
@@ -70,7 +62,7 @@
     pidfile = "/var/run/lavapdu-runner.pid"
     logfile = "/var/log/lavapdu-runner.log"
     conffile = "/etc/lavapdu/lavapdu.conf"
-    settings, pdus = readSettings(conffile)
+    settings = readSettings(conffile)
     usage = "Usage: %prog [--logfile] --[loglevel]"
     description = "LAVA PDU request listener server, host and port are handled in %s" % conffile
     parser = optparse.OptionParser(usage=usage, description=description)
@@ -86,13 +78,14 @@
             print "No such directory for specified logfile '%s'" % logfile
     open(logfile, 'w').close()
     level = logging.DEBUG
-    if options.loglevel == "DEBUG":
+    daemon_settings = settings["daemon"]
+    if daemon_settings["logging_level"] == "DEBUG":
         level = logging.DEBUG
-    if options.loglevel == "WARNING":
+    if daemon_settings["logging_level"] == "WARNING":
         level = logging.WARNING
-    if options.loglevel == "ERROR":
+    if daemon_settings["logging_level"] == "ERROR":
         level = logging.ERROR
-    if options.loglevel == "INFO":
+    if daemon_settings["logging_level"] == "INFO":
         level = logging.INFO
     client_logger, watched_file_handler = getDaemonLogger(logfile, loglevel=level,
                                                           log_format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
@@ -110,11 +103,8 @@
         files_preserve=[watched_file_handler.stream],
         stderr=watched_file_handler.stream,
         stdout=watched_file_handler.stream)
-    starter = {"logging_level": level,
-               "settings": settings,
-               "pdus": pdus}
     with context:
         logging.info("Running LAVA PDU Runner %s dbhost: %s"
-                     % (logfile, settings["dbhost"]))
-        p = PDURunner(starter)
+                     % (logfile, settings["daemon"]["dbhost"]))
+        p = PDURunner(settings)
         p.run_me()
diff --git a/lavapdu/__init__.py b/lavapdu/__init__.py
index 6c6aaf6..fe1f8f3 100644
--- a/lavapdu/__init__.py
+++ b/lavapdu/__init__.py
@@ -17,6 +17,3 @@
 #  along with this program; if not, write to the Free Software
 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 #  MA 02110-1301, USA.
-
-import pdurunner
-import socketserver
\ No newline at end of file
diff --git a/lavapdu/dbhandler.py b/lavapdu/dbhandler.py
index bc89847..09c7058 100644
--- a/lavapdu/dbhandler.py
+++ b/lavapdu/dbhandler.py
@@ -51,7 +51,7 @@
 
     def get_next_job(self):
         now = int(time.time())
-        row = self.do_sql_with_fetch("select id,hostname,port,request from pdu_queue where exectime < %i order by id asc limit 1" % now)
+        row = self.do_sql_with_fetch("select id,hostname,port,request from pdu_queue where (exectime < %i or exectime is null) order by id asc limit 1" % now)
         return row
 
     def close(self):
diff --git a/lavapdu/drivers/__init__.py b/lavapdu/drivers/__init__.py
index 2524629..fe05efd 100644
--- a/lavapdu/drivers/__init__.py
+++ b/lavapdu/drivers/__init__.py
@@ -16,8 +16,4 @@
 #  You should have received a copy of the GNU General Public License
 #  along with this program; if not, write to the Free Software
 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-#  MA 02110-1301, USA.
-
-#from apc9218 import apc9218
-#from apc7952 import apc7952
-#from apc8959 import apc8959
+#  MA 02110-1301, USA.
\ No newline at end of file
diff --git a/lavapdu/drivers/apc7952.py b/lavapdu/drivers/apc7952.py
index 2e4abfb..4b9b234 100644
--- a/lavapdu/drivers/apc7952.py
+++ b/lavapdu/drivers/apc7952.py
@@ -19,11 +19,17 @@
 #  MA 02110-1301, USA.
 
 import logging
-from apcbase import APCBase
+from lavapdu.drivers.apcbase import APCBase
 
 
 class APC7952(APCBase):
 
+    @classmethod
+    def accepts(cls, drivername):
+        if drivername == "apc7952":
+            return True
+        return False
+
     def _pdu_logout(self):
         self._back_to_main()
         logging.debug("Logging out")
diff --git a/lavapdu/drivers/apc8959.py b/lavapdu/drivers/apc8959.py
index f5f64c7..e37559d 100644
--- a/lavapdu/drivers/apc8959.py
+++ b/lavapdu/drivers/apc8959.py
@@ -19,12 +19,21 @@
 #  MA 02110-1301, USA.
 
 import logging
-from apcbase import APCBase
+from lavapdu.drivers.apcbase import APCBase
 
 
 class APC8959(APCBase):
     pdu_commands = {"off": "olOff", "on": "olOn"}
 
+#    def __init__(self, hostname):
+#        super(APC8959, self).__init__(hostname)
+
+    @classmethod
+    def accepts(cls, drivername):
+        if drivername == "apc8959":
+            return True
+        return False
+
     def _pdu_logout(self):
         logging.debug("logging out")
         self.connection.send("\r")
@@ -41,4 +50,5 @@
         self._pdu_get_to_prompt()
         self.connection.sendline(self.pdu_commands[command] + (" %i" % port_number))
         self.connection.expect("E000: Success")
+        self._pdu_get_to_prompt()
         logging.debug("done")
\ No newline at end of file
diff --git a/lavapdu/drivers/apc9210.py b/lavapdu/drivers/apc9210.py
new file mode 100644
index 0000000..b86339a
--- /dev/null
+++ b/lavapdu/drivers/apc9210.py
@@ -0,0 +1,59 @@
+#! /usr/bin/python
+
+#  Copyright 2013 Linaro Limited
+#  Author Matt Hart <matthew.hart@linaro.org>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+
+import logging
+from lavapdu.drivers.apc7952 import APC7952
+
+
+class APC9210(APC7952):
+
+    @classmethod
+    def accepts(cls, drivername):
+        if drivername == "apc9210":
+            return True
+        return False
+
+    def _port_interaction(self, command, port_number):
+        print("Attempting command: %s port: %i" % (command, port_number))
+        ### make sure in main menu here
+        self._back_to_main()
+        self.connection.send("\r")
+        self.connection.expect("1- Outlet Manager")
+        self.connection.expect("> ")
+        logging.debug("Entering Outlet Manager")
+        self.connection.send("1\r")
+        self.connection.expect("------- Outlet Manager")
+        logging.debug("Got to Device Manager")
+        self._enter_outlet(port_number, False)
+        self.connection.expect(["1- Control of Outlet", "1- Outlet Control/Configuration"])
+        self.connection.expect("> ")
+        self.connection.send("1\r")
+        self.connection.expect("Turn Outlet On")
+        self.connection.expect("> ")
+        if command == "on":
+            self.connection.send("1\r")
+            self.connection.expect("Turn Outlet On")
+            self._do_it()
+        elif command == "off":
+            self.connection.send("2\r")
+            self.connection.expect("Turn Outlet Off")
+            self._do_it()
+        else:
+            logging.debug("Unknown command!")
\ No newline at end of file
diff --git a/lavapdu/drivers/apc9218.py b/lavapdu/drivers/apc9218.py
index 1dae89a..8535c85 100644
--- a/lavapdu/drivers/apc9218.py
+++ b/lavapdu/drivers/apc9218.py
@@ -19,10 +19,17 @@
 #  MA 02110-1301, USA.
 
 import logging
-from apc7952 import APC7952
+from lavapdu.drivers.apc7952 import APC7952
+
 
 class APC9218(APC7952):
 
+    @classmethod
+    def accepts(cls, drivername):
+        if drivername == "apc9218":
+            return True
+        return False
+
     def _port_interaction(self, command, port_number):
         print("Attempting command: %s port: %i" % (command, port_number))
         ### make sure in main menu here
diff --git a/lavapdu/drivers/apcbase.py b/lavapdu/drivers/apcbase.py
index d3dca6f..aaa19c5 100644
--- a/lavapdu/drivers/apcbase.py
+++ b/lavapdu/drivers/apcbase.py
@@ -20,37 +20,47 @@
 
 import logging
 import pexpect
-from driver import PDUDriver
+from lavapdu.drivers.driver import PDUDriver
 import sys
 
+
 class APCBase(PDUDriver):
     connection = None
 
-    def __init__(self, pdu_hostname, pdu_telnetport=23):
-        self.exec_string = "/usr/bin/telnet %s %d" % (pdu_hostname, pdu_telnetport)
+    def __init__(self, hostname, settings):
+        self.hostname = hostname
+        logging.debug(settings)
+        self.settings = settings
+        telnetport = 23
+        if "telnetport" in settings:
+            telnetport = settings["telnetport"]
+        self.exec_string = "/usr/bin/telnet %s %d" % (hostname, telnetport)
         self.get_connection()
-        super(APCBase, self).__init__(pdu_hostname)
+        super(APCBase, self).__init__()
 
-    #def port_on(self, port_number):
-    #    self.port_interaction("on", port_number)
-
-    #def port_off(self, port_number):
-    #    self.port_interaction("off", port_number)
+    @classmethod
+    def accepts(cls, drivername):
+        return False
 
     def port_interaction(self, command, port_number):
         logging.debug("Running port_interaction from APCBase")
         self._port_interaction(command, port_number)
-        self._cleanup()
+        #self._cleanup()
 
     def get_connection(self):
         logging.debug("Connecting to APC PDU with: %s" % self.exec_string)
-        self.connection = pexpect.spawn(self.exec_string, logfile=sys.stdout)
+        if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
+            self.connection = pexpect.spawn(self.exec_string, logfile=sys.stdout)
+        else:
+            self.connection = pexpect.spawn(self.exec_string)
         self._pdu_login("apc","apc")
 
     def _cleanup(self):
         self._pdu_logout()
-        logging.debug("Closing connection: %s" % self.connection)
-        self.connection.close(True)
+
+    def _bombout(self):
+        logging.debug("Bombing out of driver: %s" % self.connection)
+        self.connection.close(force=True)
         del(self)
 
     def _pdu_login(self, username, password):
diff --git a/lavapdu/drivers/driver.py b/lavapdu/drivers/driver.py
index 7325473..f93974e 100644
--- a/lavapdu/drivers/driver.py
+++ b/lavapdu/drivers/driver.py
@@ -18,30 +18,48 @@
 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 #  MA 02110-1301, USA.
 
-import time
 import logging
 
+
 class PDUDriver(object):
     connection = None
     hostname = ""
 
-    def __init__(self, pdu_hostname):
-        self.hostname = pdu_hostname
-        #super(PDUDriver,self).__init__(pdu_hostname)
+    def __init__(self):
+        super(PDUDriver, self).__init__()
+
+    @classmethod
+    def select(cls, drivername):
+        logging.debug("adding PDUDriver subclasses: %s" % cls.__subclasses__())
+        candidates = cls.__subclasses__()  # pylint: disable=no-member
+        for subc in cls.__subclasses__():
+            logging.debug("adding %s subclasses: %s" % (subc,subc.__subclasses__()))
+            candidates = candidates + (subc.__subclasses__())
+            for subsubc in subc.__subclasses__():
+                logging.debug("adding %s subclasses: %s" % (subsubc,subsubc.__subclasses__()))
+                candidates = candidates + (subsubc.__subclasses__())
+        logging.debug(candidates)
+        willing = [c for c in candidates if c.accepts(drivername)]
+        if len(willing) == 0:
+            raise NotImplementedError(
+                "No driver accepted the request "
+                "'%s' with the specified job parameters. %s" % (drivername, cls)
+            )
+        logging.debug("%s accepted the request" % willing[0])
+        return willing[0]
 
     def handle(self, request, port_number, delay=0):
-        logging.debug("Driving PDU: %s PORT: %s REQUEST: %s (delay %s)" %(self.hostname,port_number,request,delay))
-        if request == "reboot":
-            self.port_off(port_number)
-            time.sleep(delay)
-            self.port_on(port_number)
-        elif request == "on":
+        logging.debug("Driving PDU hostname: %s PORT: %s REQUEST: %s (delay %s)" %(self.hostname,port_number,request,delay))
+        if request == "on":
             self.port_on(port_number)
         elif request == "off":
             self.port_off(port_number)
         else:
             logging.debug("Unknown request to handle - oops")
-            raise
+            raise NotImplementedError(
+                "Driver doesn't know how to %s " % (request)
+            )
+        self._cleanup()
 
     #def _port_interaction(self, command, port_number):
     #    super(PDUDriver, self).port_interaction(command,port_number)
diff --git a/lavapdu/drivers/strategies.py b/lavapdu/drivers/strategies.py
new file mode 100644
index 0000000..962eaf3
--- /dev/null
+++ b/lavapdu/drivers/strategies.py
@@ -0,0 +1,24 @@
+#! /usr/bin/python
+
+#  Copyright 2013 Linaro Limited
+#  Author Matt Hart <matthew.hart@linaro.org>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+
+from lavapdu.drivers.apc7952 import APC7952
+from lavapdu.drivers.apc9218 import APC9218
+from lavapdu.drivers.apc8959 import APC8959
+from lavapdu.drivers.apc9210 import APC9210
\ No newline at end of file
diff --git a/lavapdu/engine.py b/lavapdu/engine.py
deleted file mode 100644
index 47c04f7..0000000
--- a/lavapdu/engine.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#! /usr/bin/python
-
-#  Copyright 2013 Linaro Limited
-#  Author Matt Hart <matthew.hart@linaro.org>
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-#  MA 02110-1301, USA.
-
-import pexpect
-import os
-import logging
-import pkgutil
-import sys
-
-
-class PDUEngine():
-    connection = None
-    prompt = 0
-    driver = None
-    firmware_dict = {}
-
-    def __init__(self, pdu_hostname):
-        pass
-
-if __name__ == "__main__":
-    logging.basicConfig(level=logging.DEBUG)
-    logging.getLogger().setLevel(logging.DEBUG)
-    pe = PDUEngine("pdu01")
-    pe.driver.port_off(1)
-    pe.driver.port_on(1)
-    pe.close()
-    pe = PDUEngine("pdu03")
-    pe.driver.port_off(3)
-    pe.driver.port_on(3)
-    pe.close()
-
-    #pe = PDUEngine("pdu16")
-    #pe.close()
\ No newline at end of file
diff --git a/lavapdu/pdurunner.py b/lavapdu/pdurunner.py
index d31b8fd..de53eef 100644
--- a/lavapdu/pdurunner.py
+++ b/lavapdu/pdurunner.py
@@ -20,17 +20,20 @@
 
 import logging
 import time
-from dbhandler import DBHandler
+import json
 import traceback
+from lavapdu.dbhandler import DBHandler
+from lavapdu.drivers.driver import PDUDriver
+import lavapdu.drivers.strategies
 
-class PDURunner():
+class PDURunner(object):
 
     def __init__(self, config):
-        logging.basicConfig(level=config["logging_level"])
-        logging.getLogger().setLevel(config["logging_level"])
-        logging.getLogger().name = "PDURunner"
-        self.config = config
         self.pdus = config["pdus"]
+        self.settings = config["daemon"]
+        logging.basicConfig(level=self.settings["logging_level"])
+        logging.getLogger().setLevel(self.settings["logging_level"])
+        logging.getLogger().name = "PDURunner"
 
     def get_one(self, db):
         job = db.get_next_job()
@@ -44,25 +47,31 @@
             logging.debug("Found nothing to do in database")
 
     def driver_from_hostname(self, hostname):
-        logging.debug("Trying to find a driver for %s" % hostname)
+        logging.debug("Trying to find a driver for hostname %s" % hostname)
         logging.debug(self.pdus)
-        driver = self.pdus[hostname]["driver"]
-        logging.debug("Driver: %s" % driver)
-        module = __import__("drivers.%s" % driver.lower(), fromlist=[driver])
-        class_ = getattr(module, driver)
-        return class_(hostname)
+        if hostname in self.pdus:
+            drivername = (self.pdus[hostname]["driver"])
+        else:
+            raise NotImplementedError("No configuration available for hostname %s\n"
+                                      "Is there a section in the lavapdu.conf?" % hostname)
+        logging.debug("Config file wants driver: %s" % drivername)
+        driver = PDUDriver.select(drivername)(hostname, self.pdus[hostname])
+        return driver
 
     def do_job(self, hostname, port, request, delay=0):
-        retries = 10
+        retries = self.settings["retries"]
+        driver = False
         while retries > 0:
             try:
                 driver = self.driver_from_hostname(hostname)
                 return driver.handle(request, port, delay)
             except Exception as e:
-                driver.cleanup()
                 logging.warn(traceback.format_exc())
                 logging.warn("Failed to execute job: %s %s %s (attempts left %i) error was %s" %
                              (hostname, port, request, retries, e.message))
+                if driver:
+                    #driver._cleanup()
+                    driver._bombout()
                 time.sleep(5)
                 retries -= 1
         return False
@@ -70,21 +79,20 @@
     def run_me(self):
         logging.info("Starting up the PDURunner")
         while 1:
-            db = DBHandler(self.config["settings"])
+            db = DBHandler(self.settings)
             self.get_one(db)
             db.close()
             del(db)
             time.sleep(2)
 
 if __name__ == "__main__":
-    starter = { "settings": {"dbhost": "127.0.0.1",
-               "dbuser": "pdudaemon",
-               "dbpass": "pdudaemon",
-               "dbname": "lavapdu"},
-                "pdus": { "pdu14": { "driver": "APC9218" },"pdu15": {"driver": "APC8959"} },
-               "logging_level": logging.DEBUG}
-    p = PDURunner(starter)
-    p.do_job("pdu15",18,"off",10)
-    p.do_job("pdu15",18,"reboot",10)
-    p.do_job("pdu15",18,"on",10)
-    #p.run_me()
+    settings = {}
+    filename = "/etc/lavapdu/lavapdu.conf"
+    print("Reading settings from %s" % filename)
+    with open(filename) as stream:
+        jobdata = stream.read()
+        json_data = json.loads(jobdata)
+
+    p = PDURunner(json_data)
+    #p.do_job("192.168.10.5",18,"reboot",2)
+    p.run_me()
diff --git a/lavapdu/socketserver.py b/lavapdu/socketserver.py
index 2ba149c..916b02d 100644
--- a/lavapdu/socketserver.py
+++ b/lavapdu/socketserver.py
@@ -22,18 +22,23 @@
 import logging
 import socket
 import time
+import json
 from dbhandler import DBHandler
 
-
 class ListenerServer(object):
 
-    def __init__(self, config):
-        self.server = TCPServer((config["settings"]["hostname"], config["settings"]["port"]), TCPRequestHandler)
+    def __init__(self, settings):
+        listen_host = settings["hostname"]
+        listen_port = settings["port"]
+
         logging.getLogger().name = "ListenerServer"
-        logging.getLogger().setLevel(config["logging_level"])
-        logging.info("listening on %s:%s" % (config["settings"]["hostname"], config["settings"]["port"]))
-        self.server.settings = config["settings"]
-        self.db = DBHandler(self.server.settings)
+        logging.getLogger().setLevel(settings["logging_level"])
+        logging.debug("ListenerServer __init__")
+        logging.info("listening on %s:%s" % (listen_host, listen_port))
+
+        self.server = TCPServer((listen_host, listen_port), TCPRequestHandler)
+        self.server.settings = settings
+        self.db = DBHandler(settings)
         self.create_db()
         self.db.close()
         del(self.db)
@@ -58,11 +63,12 @@
     #"One instance per connection.  Override handle(self) to customize action."
     def insert_request(self, data):
         logging.getLogger().name = "TCPRequestHandler"
+        logging.getLogger().setLevel(self.server.settings["logging_level"])
         array = data.split(" ")
         delay = 10
         custom_delay = False
         now = int(time.time())
-        if len(array) < 3:
+        if (len(array) < 3) or (len(array) > 4):
             logging.info("Wrong data size")
             raise Exception("Unexpected data")
         if len(array) == 4:
@@ -87,7 +93,7 @@
 
     def queue_request(self, hostname, port, request, exectime):
         db = DBHandler(self.server.settings)
-        sql = "insert into pdu_queue (hostname,port,request,exectime)" \
+        sql = "insert into pdu_queue (hostname,port,request,exectime) " \
               "values ('%s',%i,'%s',%i)" % (hostname,port,request,exectime)
         db.do_sql(sql)
         db.close()
@@ -103,7 +109,7 @@
             try:
                 request_host = socket.gethostbyaddr(ip)[0]
             except socket.herror as e:
-                logging.debug("Unable to resolve: %s error: %s" % (ip,e))
+                #logging.debug("Unable to resolve: %s error: %s" % (ip,e))
                 request_host = ip
             logging.info("Received a request from %s: '%s'" % (request_host, data))
             self.insert_request(data)
@@ -123,12 +129,19 @@
     logging.basicConfig(level=logging.DEBUG)
     logging.getLogger().setLevel(logging.DEBUG)
     logging.debug("Executing from __main__")
-    starter = {"hostname": "0.0.0.0",
-               "port":16421,
-               "dbhost":"127.0.0.1",
-               "dbuser":"pdudaemon",
-               "dbpass":"pdudaemon",
-               "dbname":"lavapdu",
-               "logging_level": logging.DEBUG}
-    ss = ListenerServer(starter)
+    settings = {}
+    filename = "/etc/lavapdu/lavapdu.conf"
+    print("Reading settings from %s" % filename)
+    with open(filename) as stream:
+        jobdata = stream.read()
+        json_data = json.loads(jobdata)
+
+    #starter = {"daemon": {"dbhost":"127.0.0.1",
+    #                      "dbuser":"pdudaemon",
+    #                      "dbpass":"pdudaemon",
+    #                     "dbname":"lavapdu",
+    #                     "logging_level": logging.DEBUG,
+    #                     "hostname": "0.0.0.0", "port": 16421}}
+    #ss = ListenerServer(starter)
+    ss = ListenerServer(json_data)
     ss.start()
\ No newline at end of file
diff --git a/pduclient b/pduclient
index 3347c60..143da8e 100755
--- a/pduclient
+++ b/pduclient
@@ -26,11 +26,11 @@
     description = "LAVA PDU daemon client"
     commands = ["reboot", "on", "off"]
     parser = optparse.OptionParser(usage=usage, description=description)
-    parser.add_option("--daemon", dest="pdudaemonhostname", action="store", type="string", help="LAVAPDU Daemon (ex: control)")
+    parser.add_option("--daemon", dest="pdudaemonhostname", action="store", type="string", help="LAVAPDU Daemon hostname (ex: localhost)")
     parser.add_option("--hostname", dest="pduhostname", action="store", type="string", help="PDU Hostname (ex: pdu05)")
     parser.add_option("--port", dest="pduportnum", action="store", type="string", help="PDU Portnumber (ex: 04)")
     parser.add_option("--command", dest="pducommand", action="store", type="string", help="PDU command (ex: reboot|on|off)")
-    parser.add_option("--delay", dest="pdudelay", action="store", type="int", help="Delay before command runs, or between off/on when rebooting")
+    parser.add_option("--delay", dest="pdudelay", action="store", type="int", help="Delay before command runs, or between off/on when rebooting (ex: 5)")
     (options, args) = parser.parse_args()
     if (not (options.pdudaemonhostname) or not(options.pduhostname) or not (options.pduportnum) or not (options.pducommand)):
         print("Missing option, try -h for help")
@@ -38,9 +38,8 @@
     if not (options.pducommand in commands):
         print("Unknown pdu command: %s" % options.pducommand)
         exit(1)
-    #print(options)
     if options.pdudelay:
-        string = ("%s %s %s" % (options.pduhostname, options.pduportnum, options.pducommand, options.pdudelay))
+        string = ("%s %s %s %s" % (options.pduhostname, options.pduportnum, options.pducommand, options.pdudelay))
     else:
         string = ("%s %s %s" % (options.pduhostname, options.pduportnum, options.pducommand))
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -52,7 +51,7 @@
         reply = sock.recv(16384).strip()  # limit reply to 16K
         sock.close()
     except Exception:
-        print ("Error sending command, wrong hostname?")
+        print ("Error sending command, wrong daemon hostname?")
         exit(1)
     if reply == "ack":
         print("Command sent successfully.")