Getting latest code into git for Steve

Change-Id: Ie7c63c227cc4805b2d13a327964edfb5896777a8
diff --git a/.gitignore b/.gitignore
index 6501940..362c6b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+dist/
 *.pyc
 .idea/
 *.egg-info/
diff --git a/etc/lavapdu.conf b/etc/lavapdu.conf
deleted file mode 100644
index fba2a26..0000000
--- a/etc/lavapdu.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "hostname": "0.0.0.0",
-    "port": 16421,
-    "dbhost": "127.0.0.1",
-    "dbuser": "pdudaemon",
-    "dbpass": "pdudaemon",
-    "dbname": "lavapdu"
-}
\ No newline at end of file
diff --git a/etc/lavapdu/lavapdu.conf b/etc/lavapdu/lavapdu.conf
new file mode 100644
index 0000000..acded88
--- /dev/null
+++ b/etc/lavapdu/lavapdu.conf
@@ -0,0 +1,25 @@
+{
+    "daemon": {
+        "hostname": "0.0.0.0",
+        "port": 16421,
+        "dbhost": "127.0.0.1",
+        "dbuser": "pdudaemon",
+        "dbpass": "pdudaemon",
+        "dbname": "lavapdu"
+    },
+    "pdus": {
+        "pdu14": {
+            "driver": "apc9218"
+        },
+        "pdu15": {
+            "driver": "apc9218"
+        },
+        "localhost": {
+            "driver": "apc9218"
+        },
+        "pdu04": {
+            "driver": "blah",
+            "snmp": "this"
+        }
+    }
+}
\ No newline at end of file
diff --git a/lavapdu-listen b/lavapdu-listen
index c1e7852..c6a22f0 100755
--- a/lavapdu-listen
+++ b/lavapdu-listen
@@ -48,23 +48,17 @@
     """
     Read settings from config file, to listen to all hosts, hostname should be 0.0.0.0
     """
-    settings = {"port": 16421, "hostname": "0.0.0.0", "dbuser": "pdudaemon",
-                "dbpass": "pdudaemon", "dbname": "pdu_queue", "dbhost": "127.0.0.1"}
+    settings = {}
+    print("Reading settings from %s" % conffile)
     with open(filename) as stream:
         jobdata = stream.read()
-        json_default = json.loads(jobdata)
-    if "port" in json_default:
-        settings['port'] = json_default['port']
-    if "hostname" in json_default:
-        settings['hostname'] = json_default['hostname']
-    if "dbuser" in json_default:
-        settings['dbuser'] = json_default['dbuser']
-    if "dbpass" in json_default:
-        settings['dbpass'] = json_default['dbpass']
-    if "dbname" in json_default:
-        settings['dbname'] = json_default['dbname']
-    if "dbhost" in json_default:
-        settings['dbhost'] = json_default['dbhost']
+        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
 
 if __name__ == '__main__':
@@ -72,7 +66,7 @@
     # not necessarily per-instance, so use the command line and a default conf file.
     pidfile = "/var/run/lavapdu-listen.pid"
     logfile = "/var/log/lavapdu-listener.log"
-    conffile = "/etc/lavapdu.conf"
+    conffile = "/etc/lavapdu/lavapdu.conf"
     settings = readSettings(conffile)
     usage = "Usage: %prog [--logfile] --[loglevel]"
     description = "LAVA PDU request listener server, host and port are handled in %s" % conffile
@@ -114,12 +108,7 @@
         stderr=watched_file_handler.stream,
         stdout=watched_file_handler.stream)
     starter = {"logging_level": level,
-               "hostname": settings['hostname'],
-               "port": settings['port'],
-               "dbhost": settings["dbhost"],
-               "dbuser": settings["dbuser"],
-               "dbpass": settings["dbpass"],
-               "dbname": settings["dbname"]}
+               "settings": settings}
     with context:
         logging.info("Running LAVA PDU Listener %s %s %d."
                      % (logfile, settings['hostname'], settings['port']))
diff --git a/lavapdu-runner b/lavapdu-runner
index 9ad9d62..dadf9f3 100755
--- a/lavapdu-runner
+++ b/lavapdu-runner
@@ -48,27 +48,29 @@
     """
     Read settings from config file, to listen to all hosts, hostname should be 0.0.0.0
     """
-    settings = {"dbuser": "pdudaemon", "dbpass": "pdudaemon", "dbname": "pdu_queue", "dbhost": "127.0.0.1"}
+    pdus = {}
+    settings = {}
+    print("Reading settings from %s" % conffile)
     with open(filename) as stream:
         jobdata = stream.read()
-        json_default = json.loads(jobdata)
-    if "dbuser" in json_default:
-        settings['dbuser'] = json_default['dbuser']
-    if "dbpass" in json_default:
-        settings['dbpass'] = json_default['dbpass']
-    if "dbname" in json_default:
-        settings['dbname'] = json_default['dbname']
-    if "dbhost" in json_default:
-        settings['dbhost'] = json_default['dbhost']
-    return settings
+        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)
+
 
 if __name__ == '__main__':
     # instance settings come from django - the coordinator doesn't use django and is
     # not necessarily per-instance, so use the command line and a default conf file.
     pidfile = "/var/run/lavapdu-runner.pid"
     logfile = "/var/log/lavapdu-runner.log"
-    conffile = "/etc/lavapdu.conf"
-    settings = readSettings(conffile)
+    conffile = "/etc/lavapdu/lavapdu.conf"
+    settings, pdus = 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)
@@ -109,10 +111,8 @@
         stderr=watched_file_handler.stream,
         stdout=watched_file_handler.stream)
     starter = {"logging_level": level,
-               "dbhost": settings["dbhost"],
-               "dbuser": settings["dbuser"],
-               "dbpass": settings["dbpass"],
-               "dbname": settings["dbname"]}
+               "settings": settings,
+               "pdus": pdus}
     with context:
         logging.info("Running LAVA PDU Runner %s dbhost: %s"
                      % (logfile, settings["dbhost"]))
diff --git a/lavapdu/drivers/__init__.py b/lavapdu/drivers/__init__.py
index e69de29..2524629 100644
--- a/lavapdu/drivers/__init__.py
+++ b/lavapdu/drivers/__init__.py
@@ -0,0 +1,23 @@
+#! /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 apc9218 import apc9218
+#from apc7952 import apc7952
+#from apc8959 import apc8959
diff --git a/lavapdu/drivers/apc7952.py b/lavapdu/drivers/apc7952.py
index 447bac1..2e4abfb 100644
--- a/lavapdu/drivers/apc7952.py
+++ b/lavapdu/drivers/apc7952.py
@@ -19,9 +19,10 @@
 #  MA 02110-1301, USA.
 
 import logging
-from driver import PDUDriver
+from apcbase import APCBase
 
-class apc7952(PDUDriver):
+
+class APC7952(APCBase):
 
     def _pdu_logout(self):
         self._back_to_main()
@@ -30,6 +31,7 @@
 
     def _back_to_main(self):
         logging.debug("Returning to main menu")
+        self.connection.send("\r")
         self.connection.expect('>')
         for i in range(1, 20):
             self.connection.send("\x1B")
@@ -89,7 +91,4 @@
         self.connection.expect("Enter 'YES' to continue or <ENTER> to cancel :")
         self.connection.send("YES\r")
         self.connection.expect("Press <ENTER> to continue...")
-        self.connection.send("\r")
-
-    class Meta():
-        handled_firmware = ["v3.7.3"]
\ No newline at end of file
+        self.connection.send("\r")
\ No newline at end of file
diff --git a/lavapdu/drivers/apc8959.py b/lavapdu/drivers/apc8959.py
index a3f1e29..f5f64c7 100644
--- a/lavapdu/drivers/apc8959.py
+++ b/lavapdu/drivers/apc8959.py
@@ -19,10 +19,10 @@
 #  MA 02110-1301, USA.
 
 import logging
-from driver import PDUDriver
+from apcbase import APCBase
 
 
-class apc8959(PDUDriver):
+class APC8959(APCBase):
     pdu_commands = {"off": "olOff", "on": "olOn"}
 
     def _pdu_logout(self):
@@ -41,7 +41,4 @@
         self._pdu_get_to_prompt()
         self.connection.sendline(self.pdu_commands[command] + (" %i" % port_number))
         self.connection.expect("E000: Success")
-        logging.debug("done")
-
-    class Meta():
-        handled_firmware = ["v5.1.9"]
\ No newline at end of file
+        logging.debug("done")
\ No newline at end of file
diff --git a/lavapdu/drivers/apc9218.py b/lavapdu/drivers/apc9218.py
index a81f6d7..1dae89a 100644
--- a/lavapdu/drivers/apc9218.py
+++ b/lavapdu/drivers/apc9218.py
@@ -19,10 +19,9 @@
 #  MA 02110-1301, USA.
 
 import logging
-from apc7952 import apc7952
+from apc7952 import APC7952
 
-class apc9218(apc7952):
-    handled_firmware = ["v3.0.3","v3.0.1"]
+class APC9218(APC7952):
 
     def _port_interaction(self, command, port_number):
         print("Attempting command: %s port: %i" % (command, port_number))
@@ -56,7 +55,4 @@
             self.connection.expect("Immediate Off")
             self._do_it()
         else:
-            logging.debug("Unknown command!")
-
-    class Meta():
-        handled_firmware = ["v3.0.3","v3.0.1"]
\ No newline at end of file
+            logging.debug("Unknown command!")
\ No newline at end of file
diff --git a/lavapdu/drivers/apcbase.py b/lavapdu/drivers/apcbase.py
new file mode 100644
index 0000000..d3dca6f
--- /dev/null
+++ b/lavapdu/drivers/apcbase.py
@@ -0,0 +1,62 @@
+#! /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
+import pexpect
+from 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)
+        self.get_connection()
+        super(APCBase, self).__init__(pdu_hostname)
+
+    #def port_on(self, port_number):
+    #    self.port_interaction("on", port_number)
+
+    #def port_off(self, port_number):
+    #    self.port_interaction("off", port_number)
+
+    def port_interaction(self, command, port_number):
+        logging.debug("Running port_interaction from APCBase")
+        self._port_interaction(command, port_number)
+        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)
+        self._pdu_login("apc","apc")
+
+    def _cleanup(self):
+        self._pdu_logout()
+        logging.debug("Closing connection: %s" % self.connection)
+        self.connection.close(True)
+        del(self)
+
+    def _pdu_login(self, username, password):
+        logging.debug("attempting login with username %s, password %s" % (username, password))
+        self.connection.send("\r")
+        self.connection.expect("User Name :")
+        self.connection.send("%s\r" % username)
+        self.connection.expect("Password  :")
+        self.connection.send("%s\r" % password)
diff --git a/lavapdu/drivers/driver.py b/lavapdu/drivers/driver.py
index 7be55e9..7325473 100644
--- a/lavapdu/drivers/driver.py
+++ b/lavapdu/drivers/driver.py
@@ -18,29 +18,36 @@
 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 #  MA 02110-1301, USA.
 
+import time
 import logging
 
-
-class NoDriverException(Exception):
-    pass
-
-
 class PDUDriver(object):
     connection = None
-    firmware_dict = {}
+    hostname = ""
 
-    def __init__(self, connection):
-        self.connection = connection
+    def __init__(self, pdu_hostname):
+        self.hostname = pdu_hostname
+        #super(PDUDriver,self).__init__(pdu_hostname)
 
-        # return the driver that provides the firmware version
-    def _port_interaction(self, command, port_number):
-        pass
+    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":
+            self.port_on(port_number)
+        elif request == "off":
+            self.port_off(port_number)
+        else:
+            logging.debug("Unknown request to handle - oops")
+            raise
 
-    def _pdu_logout(self):
-        pass
+    #def _port_interaction(self, command, port_number):
+    #    super(PDUDriver, self).port_interaction(command,port_number)
 
     def port_on(self, port_number):
-        self._port_interaction("on", port_number)
+        self.port_interaction("on", port_number)
 
     def port_off(self, port_number):
-        self._port_interaction("off", port_number)
\ No newline at end of file
+        self.port_interaction("off", port_number)
\ No newline at end of file
diff --git a/lavapdu/engine.py b/lavapdu/engine.py
index 7bcbfa7..47c04f7 100644
--- a/lavapdu/engine.py
+++ b/lavapdu/engine.py
@@ -31,80 +31,8 @@
     driver = None
     firmware_dict = {}
 
-    def __init__(self, pdu_hostname, pdu_telnetport=23):
-        self.exec_string = "/usr/bin/telnet %s %d" % (pdu_hostname, pdu_telnetport)
-        logging.debug("Created new PDUEngine: %s" % self.exec_string)
-        required_version = self._pdu_login("apc", "apc")
-        logging.debug("Got firmware version: %s" % required_version)
-        driver_list = self.load_all_modules_from_dir("drivers")
-        for driver in driver_list:
-            handled = []
-            exec("handled = %s.Meta.handled_firmware" % driver)
-            for firmware_value in handled:
-                self.firmware_dict[firmware_value] = driver
-
-        logging.debug("Firmware versions supported: %s" % self.firmware_dict)
-        if self.firmware_dict[required_version]:
-            driver = self.firmware_dict[required_version]
-            logging.debug("Using driver %s for version: %s" % (driver, required_version))
-            exec("self.driver = %s(self.connection)" % driver)
-        else:
-            self.driver = None
-
-    def load_all_modules_from_dir(self, dirname):
-        module_list = []
-        for importer, package_name, _ in pkgutil.iter_modules([dirname]):
-            full_package_name = '%s.%s' % (dirname, package_name)
-            if full_package_name not in sys.modules and (not full_package_name == "%s.driver" % dirname):
-                import_string = "global %s\nfrom %s import %s" % (package_name,full_package_name,package_name)
-                exec (import_string)
-                logging.debug(import_string)
-            if (not full_package_name == "%s.driver" % dirname):
-                module_list.append(package_name)
-        return module_list
-
-    def pduconnect(self):
-        self.connection = self.get_connection(self.exec_string)
-
-    def pduclose(self):
-        logging.debug("Closing connection: %s" % self.connection)
-        self.connection.close(True)
-
-    def pdureconnect(self):
-        self.pduclose()
-        self.pduconnect()
-
-    def get_connection(self, exec_string):
-        connection = pexpect.spawn(exec_string)
-        #connection.logfile = sys.stdout
-        return connection
-
-    def is_busy(self):
-        if os.path.exists("/proc/%i" % self.connection.pid):
-            return True
-        return False
-
-    def close(self):
-        self.driver._pdu_logout()
-        self.firmware_dict = {}
-        del(self)
-
-    def _pdu_login(self, username, password):
-        logging.debug("attempting login with username %s, password %s" % (username, password))
-        self.pduconnect()
-        self.connection.send("\r")
-        self.connection.expect("User Name :")
-        self.connection.send("apc\r")
-        self.connection.expect("Password  :")
-        self.connection.send("apc\r")
-        output = self.connection.read(250)
-        #print "OUTPUT: %s" % output
-        a = output.split("AOS")[1].split()[0]
-        #print "A: %s" % a
-        b = a.strip()
-        #print "B: %s" % b
-        version = b
-        return version.strip()
+    def __init__(self, pdu_hostname):
+        pass
 
 if __name__ == "__main__":
     logging.basicConfig(level=logging.DEBUG)
diff --git a/lavapdu/pdurunner.py b/lavapdu/pdurunner.py
index 8a83f12..d31b8fd 100644
--- a/lavapdu/pdurunner.py
+++ b/lavapdu/pdurunner.py
@@ -20,9 +20,8 @@
 
 import logging
 import time
-from engine import PDUEngine
 from dbhandler import DBHandler
-
+import traceback
 
 class PDURunner():
 
@@ -31,6 +30,7 @@
         logging.getLogger().setLevel(config["logging_level"])
         logging.getLogger().name = "PDURunner"
         self.config = config
+        self.pdus = config["pdus"]
 
     def get_one(self, db):
         job = db.get_next_job()
@@ -38,51 +38,53 @@
             job_id, hostname, port, request = job
             logging.debug(job)
             logging.info("Processing queue item: (%s %s) on hostname: %s" % (request, port, hostname))
-            #logging.debug(id, hostname, request, port)
             res = self.do_job(hostname, port, request)
             db.delete_row(job_id)
         else:
             logging.debug("Found nothing to do in database")
 
-    def do_job(self, hostname, port, request):
+    def driver_from_hostname(self, hostname):
+        logging.debug("Trying to find a driver for %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)
+
+    def do_job(self, hostname, port, request, delay=0):
         retries = 10
         while retries > 0:
             try:
-                pe = PDUEngine(hostname, 23)
-                if request == "on":
-                    pe.driver.port_on(port)
-                    return true
-                elif request == "off":
-                    pe.driver.port_off(port)
-                    return true
-                else:
-                    logging.debug("Unknown request type: %s" % request)
-                    return false
-                pe.pduclose()
-                del(pe)
-                retries = 0
+                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))
-                #logging.warn(e)
                 time.sleep(5)
                 retries -= 1
-        return false
+        return False
 
     def run_me(self):
         logging.info("Starting up the PDURunner")
         while 1:
-            db = DBHandler(self.config)
+            db = DBHandler(self.config["settings"])
             self.get_one(db)
             db.close()
             del(db)
             time.sleep(2)
 
 if __name__ == "__main__":
-    starter = {"dbhost": "127.0.0.1",
+    starter = { "settings": {"dbhost": "127.0.0.1",
                "dbuser": "pdudaemon",
                "dbpass": "pdudaemon",
-               "dbname": "lavapdu",
+               "dbname": "lavapdu"},
+                "pdus": { "pdu14": { "driver": "APC9218" },"pdu15": {"driver": "APC8959"} },
                "logging_level": logging.DEBUG}
     p = PDURunner(starter)
-    p.run_me()
+    p.do_job("pdu15",18,"off",10)
+    p.do_job("pdu15",18,"reboot",10)
+    p.do_job("pdu15",18,"on",10)
+    #p.run_me()
diff --git a/lavapdu/socketserver.py b/lavapdu/socketserver.py
index 62551bc..2ba149c 100644
--- a/lavapdu/socketserver.py
+++ b/lavapdu/socketserver.py
@@ -28,12 +28,12 @@
 class ListenerServer(object):
 
     def __init__(self, config):
-        self.server = TCPServer((config["hostname"], config["port"]), TCPRequestHandler)
+        self.server = TCPServer((config["settings"]["hostname"], config["settings"]["port"]), TCPRequestHandler)
         logging.getLogger().name = "ListenerServer"
         logging.getLogger().setLevel(config["logging_level"])
-        logging.info("listening on %s:%s" % (config["hostname"], config["port"]))
-        self.server.config = config
-        self.db = DBHandler(config)
+        logging.info("listening on %s:%s" % (config["settings"]["hostname"], config["settings"]["port"]))
+        self.server.settings = config["settings"]
+        self.db = DBHandler(self.server.settings)
         self.create_db()
         self.db.close()
         del(self.db)
@@ -86,9 +86,9 @@
                 self.queue_request(hostname,port,request,now)
 
     def queue_request(self, hostname, port, request, exectime):
-        db = DBHandler(self.server.config)
+        db = DBHandler(self.server.settings)
         sql = "insert into pdu_queue (hostname,port,request,exectime)" \
-              "values ('%s',%i,'%s')" % (hostname,port,request,exectime)
+              "values ('%s',%i,'%s',%i)" % (hostname,port,request,exectime)
         db.do_sql(sql)
         db.close()
         del(db)
@@ -109,7 +109,8 @@
             self.insert_request(data)
             self.request.sendall("ack\n")
         except Exception as e:
-            logging.debug(e)
+            logging.debug(e.__class__)
+            logging.debug(e.message)
             self.request.sendall("nack\n")
         self.request.close()
 
diff --git a/setup.py b/setup.py
index 577345f..a74ae5f 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@
 
 setup(
     name='lavapdu',
-    version="0.0.3",
+    version="0.0.4",
     author="Matthew Hart",
     author_email="matthew.hart@linaro.org",
     license="GPL2+",