Hi,

I have done review of that fence agent and it looks almost fine. I have prepared a patch that have to be tested (and finished) as I do not have access to such device. There were two main issues: documentation (show_docs() used for XML metadata / manual pages) and do_action() that I replaced by fence_action. The later change was bigger one. Using that standard function instead of non-generic one, is perhaps a liitle bit hacky but all timetouts and sleeps works as expected. Please take a look and finish proposed patch.

m,


On 04/15/2014 04:07 PM, Andrea Bucci wrote:
dli-pc8000 is a fence-agent to Digital Loggers Inc's Ethernet Power Controller 
III (http://www.digital-loggers.com/epcr3.html)
Based on 2007 Perl code by Andy Whitcroft

Signed-off-by: Alan Evangelista <[email protected]>
Signed-off-by: Eduardo Kienetz <[email protected]>
Signed-off-by: Nathan Ozelim <[email protected]>
Signed-off-by: Plinio Freire <[email protected]>
---
  configure.ac                                |    1 +
  fence/agents/dli_pc8000/Makefile.am         |   16 ++
  fence/agents/dli_pc8000/fence_dli_pc8000.py |  233 +++++++++++++++++++++++++++
  3 files changed, 250 insertions(+)
  create mode 100644 fence/agents/dli_pc8000/Makefile.am
  create mode 100755 fence/agents/dli_pc8000/fence_dli_pc8000.py

diff --git a/configure.ac b/configure.ac
index c24198c..51f86be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -260,6 +260,7 @@ AC_CONFIG_FILES([Makefile
                 fence/agents/cisco_mds/Makefile
                 fence/agents/cisco_ucs/Makefile
                 fence/agents/cpint/Makefile
+                fence/agents/dli_pc8000/Makefile
                 fence/agents/drac/Makefile
                 fence/agents/drac5/Makefile
                 fence/agents/dummy/Makefile
diff --git a/fence/agents/dli_pc8000/Makefile.am 
b/fence/agents/dli_pc8000/Makefile.am
new file mode 100644
index 0000000..ce92b6f
--- /dev/null
+++ b/fence/agents/dli_pc8000/Makefile.am
@@ -0,0 +1,16 @@
+MAINTAINERCLEANFILES   = Makefile.in
+
+TARGET                 = fence_dli_pc8000
+
+SRC                    = $(TARGET).py
+
+EXTRA_DIST             = $(SRC)
+
+sbin_SCRIPTS           = $(TARGET)
+
+include $(top_srcdir)/make/fencebuild.mk
+include $(top_srcdir)/make/fenceman.mk
+
+clean-local: clean-man
+       rm -f $(TARGET)
+
diff --git a/fence/agents/dli_pc8000/fence_dli_pc8000.py 
b/fence/agents/dli_pc8000/fence_dli_pc8000.py
new file mode 100755
index 0000000..8e2ab7c
--- /dev/null
+++ b/fence/agents/dli_pc8000/fence_dli_pc8000.py
@@ -0,0 +1,233 @@
+#!/usr/bin/python
+
+"""
+Connect to dli-pc8000 remote management module via http and perform power
+management operations.
+"""
+
+import base64
+import sys
+import urllib2
+
+sys.path.append("/usr/share/fence")
+from fencing import *
+
+def http_get_file(server, credentials, filename):
+    """
+    Authenticate to server and gets file
+
+    @param string server server IP address
+    @param string credentials base64 built string with username and password
+    @param string filename name of the file to be read
+    @return tuple http status code and content/error reason
+    """
+
+    request = urllib2.Request("http://"; + server + filename)
+    request.add_header("Authorization", "Basic %s" % credentials)
+
+    try:
+        response = urllib2.urlopen(request)
+        content = response.read()
+        return response.code, content.strip()
+    except urllib2.HTTPError, e:
+        return e.code, e.reason
+    except urllib2.URLError, e:
+        return -1, e.reason
+
+
+def check_status(program, ip, credentials, port):
+    """
+    Verify if port is up and checks the url format
+
+    @param string program program name
+    @param string ip server IP address
+    @param string credentials base64 built string with username and password
+    @param string port Port number to be tested/changed
+    @return tuple status of the port and url format
+    """
+
+    http_code, response_body = http_get_file(ip, credentials, '/')
+
+    if http_code != 200:
+        print "Error trying to access %s. Error is: '%s'" % (ip, response_body)
+        return "INVALID", None
+
+    # outlet control page not found: get the new one and try again
+    if not response_body or "URL=/" in response_body:
+        prefix = "URL="
+        sufix = "\">"
+        start = response_body.index(prefix) + len(prefix)
+        end = response_body.index(sufix, start)
+        url = response_body[start:end]
+        http_code, response_body = http_get_file(ip, credentials, url)
+
+        # outlet control page still not found: unable to proceed
+        if not response_body:
+            print "%s: unable to retrieve HTTP root from %s\n" % (program, ip)
+            return "INVALID", None
+
+    # check both new and old url formats
+    new_string = "outlet?%s=" % port
+    old_string_on = "outletoff%s=" % port
+    old_string_off = "outleton%s=" % port
+
+    # test if port is present and url format
+    if new_string in response_body:
+        prefix = new_string
+        link_type = "new"
+    elif old_string_on in response_body:
+        prefix = old_string_on
+        link_type = "old"
+    elif old_string_off in response_body:
+        prefix = old_string_off
+        link_type = "old"
+    else:
+        return "INVALID", None
+
+    # get status if port if valid
+    sufix = ">Switch"
+    start = response_body.index(prefix) + len(prefix)
+    end = response_body.index(sufix, start)
+    control_table = response_body[start:end]
+
+    # found string "Switch ON": status is OFF
+    if "ON" in control_table:
+        status = "OFF"
+    # found string "Switch OFF": status is ON
+    elif "OFF" in control_table:
+        status = "ON"
+    # found something else: status is INVALID
+    else:
+        status = "INVALID"
+
+    return status, link_type
+
+
+def get_commands_urls(program, port, link_type):
+    """
+    Build available command urls
+
+    @param string program program name
+    @param string port port number
+    @param string link_type if link_type is old or new
+    @return triple composed strings off_cmd, on_cmd, cycle_cmd
+    """
+
+    # compose the power management commands urls
+    if link_type == "old":
+        off_cmd = "/outletoff?%s" % port
+        on_cmd = "/outleton?%s" % port
+        cycle_cmd = "/outletccl?%s" % port
+    else:
+        off_cmd = "/outlet?%s=OFF" % port
+        on_cmd = "/outlet?%s=ON" % port
+        cycle_cmd = "/outlet?%s=CCL" % port
+
+    return off_cmd, on_cmd, cycle_cmd
+
+
+def do_action(program, ip, port, on_cmd, off_cmd, cycle_cmd, is_on, action):
+    """
+    Perform power command on system
+
+    @param string program program name
+    @param string ip server IP address
+    @param string port port number
+    @param string on_cmd command to power system on
+    @param string off_cmd command to power system off
+    @param string cycle_cmd command to power system off and on again
+    @param string status if system is ON or OFF
+    @param string action action to be performed
+    """
+
+    # action is not recognized
+    if action not in ("on", "off", "reboot"):
+        print "%s: %s:%s: Invalid action '%s'\n" % (program, ip, port, action)
+        return
+
+    # system is off, requested action is turning it off
+    if not is_on and action == "off":
+        return
+
+    # system is on, requested action is turning it on
+    if is_on and action == "on":
+        return
+
+    # system is on, requested action is turning it off
+    if is_on and action == "off":
+        print "%s: %s:%s: outlet ON, switching OFF ... %s\n" \
+        % (program, ip, port, off_cmd)
+        status = http_get_file(ip, credentials, off_cmd)
+
+    # system is off, requested action is turning it on
+    if not is_on and action == "on":
+        print "%s: %s:%s: switching ON ... %s\n" % (program, ip, port, on_cmd)
+        status = http_get_file(ip, credentials, on_cmd)
+
+    # requested action is restarting it
+    if action == "reboot":
+        # Port is on, switch it off and on
+        if is_on:
+            print "%s: %s:%s: switching OFF and then ON ... %s\n" \
+            % (program, ip, port, cycle_cmd)
+            status = http_get_file(ip, credentials, cycle_cmd)
+        # Port is off, switch it on
+        else:
+            message_header = "%s: %s:%s:" % (program, ip, port)
+            print "%s switching ON... %s\n" % (message_header, cycle_cmd)
+            status = http_get_file(ip, credentials, on_cmd)
+
+    print "%s: %s:%s: action '%s' complete\n" % (program, ip, port, action)
+
+
+def usage(program):
+    """
+    Displays usage
+
+    @param string program program name
+    """
+
+    message_header = "Usage: %s <ip> <port> <username> <password>" % program
+    action = "<action: on|off|reboot>"
+    print "%s %s \n" % (message_header, action)
+
+
+# program name
+program = sys.argv[0]
+
+device_opt = [ "ipaddr", "login", "passwd", "ipport" ]
+options = check_input(device_opt, process_input(device_opt))
+
+action = options['--action']
+login = options['--username']
+passwd = options['--password']
+ipaddr = options['--ip']
+port = options['--ipport']
+
+# build the authentication string
+credentials = base64.encodestring('%s:%s' % (login, passwd)).strip()
+
+print "%s: %s:%s: checking port status\n" % (program, ipaddr, port)
+
+# get the status of the port and link_type
+port_status, link_type = check_status(program, ipaddr, credentials, port)
+
+# port is not found: exit dli-pc8000
+if port_status == "INVALID":
+    print "%s: %s: port not found." % (program, port)
+    sys.exit(1)
+
+if action == "status":
+    print "System status is %s" % (port_status)
+    sys.exit(1)
+
+message_header = "%s: %s:%s:" % (program, ipaddr, port)
+print "%s port found, attempting action '%s'\n" % (message_header, action)
+
+# get urls of available commands
+off_cmd, on_cmd, cycle_cmd = get_commands_urls(program, port, link_type)
+
+is_system_on = True if port_status == "ON" else False
+
+# switch port on/off
+do_action(program, ipaddr, port, on_cmd, off_cmd, cycle_cmd, is_system_on, 
action)

diff --git a/fence/agents/dli_pc8000/fence_dli_pc8000.py b/fence/agents/dli_pc8000/fence_dli_pc8000.py
index 8e2ab7c..e11ad23 100755
--- a/fence/agents/dli_pc8000/fence_dli_pc8000.py
+++ b/fence/agents/dli_pc8000/fence_dli_pc8000.py
@@ -11,8 +11,9 @@ import urllib2
 
 sys.path.append("/usr/share/fence")
 from fencing import *
+from fencing import run_delay
 
-def http_get_file(server, credentials, filename):
+def http_get_file(options, filename):
     """
     Authenticate to server and gets file
 
@@ -22,8 +23,8 @@ def http_get_file(server, credentials, filename):
     @return tuple http status code and content/error reason
     """
 
-    request = urllib2.Request("http://"; + server + filename)
-    request.add_header("Authorization", "Basic %s" % credentials)
+    request = urllib2.Request("http://"; + options["--ip"] + filename)
+    request.add_header("Authorization", "Basic %s" % options["credentials"])
 
     try:
         response = urllib2.urlopen(request)
@@ -35,7 +36,7 @@ def http_get_file(server, credentials, filename):
         return -1, e.reason
 
 
-def check_status(program, ip, credentials, port):
+def check_status(program, options):
     """
     Verify if port is up and checks the url format
 
@@ -46,10 +47,10 @@ def check_status(program, ip, credentials, port):
     @return tuple status of the port and url format
     """
 
-    http_code, response_body = http_get_file(ip, credentials, '/')
+    http_code, response_body = http_get_file(options, '/')
 
     if http_code != 200:
-        print "Error trying to access %s. Error is: '%s'" % (ip, response_body)
+        print "Error trying to access %s. Error is: '%s'" % (options["--ip"], response_body)
         return "INVALID", None
 
     # outlet control page not found: get the new one and try again
@@ -59,17 +60,17 @@ def check_status(program, ip, credentials, port):
         start = response_body.index(prefix) + len(prefix)
         end = response_body.index(sufix, start)
         url = response_body[start:end]
-        http_code, response_body = http_get_file(ip, credentials, url)
+        http_code, response_body = http_get_file(options, url)
 
         # outlet control page still not found: unable to proceed
         if not response_body:
-            print "%s: unable to retrieve HTTP root from %s\n" % (program, ip)
+            print "%s: unable to retrieve HTTP root from %s\n" % (program, options["--ip"])
             return "INVALID", None
 
     # check both new and old url formats
-    new_string = "outlet?%s=" % port
-    old_string_on = "outletoff%s=" % port
-    old_string_off = "outleton%s=" % port
+    new_string = "outlet?%s=" % options["--plug"]
+    old_string_on = "outletoff%s=" % options["--plug"]
+    old_string_off = "outleton%s=" % options["--plug"]
 
     # test if port is present and url format
     if new_string in response_body:
@@ -103,7 +104,7 @@ def check_status(program, ip, credentials, port):
     return status, link_type
 
 
-def get_commands_urls(program, port, link_type):
+def get_commands_urls(port, link_type):
     """
     Build available command urls
 
@@ -125,109 +126,53 @@ def get_commands_urls(program, port, link_type):
 
     return off_cmd, on_cmd, cycle_cmd
 
+def set_power_status(conn, options):
+    del conn
 
-def do_action(program, ip, port, on_cmd, off_cmd, cycle_cmd, is_on, action):
-    """
-    Perform power command on system
-
-    @param string program program name
-    @param string ip server IP address
-    @param string port port number
-    @param string on_cmd command to power system on
-    @param string off_cmd command to power system off
-    @param string cycle_cmd command to power system off and on again
-    @param string status if system is ON or OFF
-    @param string action action to be performed
-    """
-
-    # action is not recognized
-    if action not in ("on", "off", "reboot"):
-        print "%s: %s:%s: Invalid action '%s'\n" % (program, ip, port, action)
-        return
-
-    # system is off, requested action is turning it off
-    if not is_on and action == "off":
-        return
-
-    # system is on, requested action is turning it on
-    if is_on and action == "on":
-        return
-
-    # system is on, requested action is turning it off
-    if is_on and action == "off":
-        print "%s: %s:%s: outlet ON, switching OFF ... %s\n" \
-        % (program, ip, port, off_cmd)
-        status = http_get_file(ip, credentials, off_cmd)
-
-    # system is off, requested action is turning it on
-    if not is_on and action == "on":
-        print "%s: %s:%s: switching ON ... %s\n" % (program, ip, port, on_cmd)
-        status = http_get_file(ip, credentials, on_cmd)
-
-    # requested action is restarting it
-    if action == "reboot":
-        # Port is on, switch it off and on
-        if is_on:
-            print "%s: %s:%s: switching OFF and then ON ... %s\n" \
-            % (program, ip, port, cycle_cmd)
-            status = http_get_file(ip, credentials, cycle_cmd)
-        # Port is off, switch it on
-        else:
-            message_header = "%s: %s:%s:" % (program, ip, port)
-            print "%s switching ON... %s\n" % (message_header, cycle_cmd)
-            status = http_get_file(ip, credentials, on_cmd)
-
-    print "%s: %s:%s: action '%s' complete\n" % (program, ip, port, action)
-
-
-def usage(program):
-    """
-    Displays usage
-
-    @param string program program name
-    """
-
-    message_header = "Usage: %s <ip> <port> <username> <password>" % program
-    action = "<action: on|off|reboot>"
-    print "%s %s \n" % (message_header, action)
+    action = {
+        'on' : options["on_cmd"],
+        'off': options["off_cmd"]
+    }[options["--action"]]
 
+    http_get_file(options, action)
 
-# program name
-program = sys.argv[0]
+def get_power_status(conn, options):
+    del conn
+    
+    (status, _) = check_status(, options)
+    return status
 
-device_opt = [ "ipaddr", "login", "passwd", "ipport" ]
-options = check_input(device_opt, process_input(device_opt))
+def main():
+    device_opt = ["ipaddr", "login", "passwd", "port"]
+    atexit.register(atexit_handler)
 
-action = options['--action']
-login = options['--username']
-passwd = options['--password']
-ipaddr = options['--ip']
-port = options['--ipport']
+    options = check_input(device_opt, process_input(device_opt))
 
-# build the authentication string
-credentials = base64.encodestring('%s:%s' % (login, passwd)).strip()
+    docs = {}
+    docs["shortdesc"] = "Fence agent ..."
+    docs["longdesc"] = "..."
+    docs["vendorurl"] = "..."
+    show_docs(options, docs)
 
-print "%s: %s:%s: checking port status\n" % (program, ipaddr, port)
+    # program name
+    program = sys.argv[0]
 
-# get the status of the port and link_type
-port_status, link_type = check_status(program, ipaddr, credentials, port)
+    # build the authentication string
+    options["credentials"] = base64.encodestring('%s:%s' % (options["--username"], options["--password"])).strip()
 
-# port is not found: exit dli-pc8000
-if port_status == "INVALID":
-    print "%s: %s: port not found." % (program, port)
-    sys.exit(1)
+    print "%s: %s:%s: checking port status\n" % (program, options["--ip"], options["--plug"])
 
-if action == "status":
-    print "System status is %s" % (port_status)
-    sys.exit(1)
+    # get the status of the port and link_type
+    port_status, link_type = check_status(program, options)
 
-message_header = "%s: %s:%s:" % (program, ipaddr, port)
-print "%s port found, attempting action '%s'\n" % (message_header, action)
+    message_header = "%s: %s:%s:" % (program, options["--ip"], options["--plug"])
+    print "%s port found, attempting action '%s'\n" % (message_header, options["--action"])
 
-# get urls of available commands
-off_cmd, on_cmd, cycle_cmd = get_commands_urls(program, port, link_type)
+    # get urls of available commands
+    options["off_cmd"], options["on_cmd"], options["cycle_cmd"] = get_commands_urls(options["--plug"], link_type)
 
-is_system_on = True if port_status == "ON" else False
+    result = fence_action(None, options, set_power_status, get_power_status, None)
+    sys.exit(result)
 
-# switch port on/off
-do_action(program, ipaddr, port, on_cmd, off_cmd, cycle_cmd, is_system_on, action)
+if __name__ == "__main__":
+    main()

Reply via email to