* [Cluster-devel] [PATCH] fence_dli_pc8000: introduce dli_pc8000 fence agent
@ 2014-04-15 14:07 Andrea Bucci
2014-05-05 8:22 ` Marek Grac
0 siblings, 1 reply; 2+ messages in thread
From: Andrea Bucci @ 2014-04-15 14:07 UTC (permalink / raw)
To: cluster-devel.redhat.com
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 <alanoe@linux.vnet.ibm.com>
Signed-off-by: Eduardo Kienetz <ebacchi@linux.vnet.ibm.com>
Signed-off-by: Nathan Ozelim <natoze@linux.vnet.ibm.com>
Signed-off-by: Plinio Freire <pleirie@linux.vnet.ibm.com>
---
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)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [Cluster-devel] [PATCH] fence_dli_pc8000: introduce dli_pc8000 fence agent
2014-04-15 14:07 [Cluster-devel] [PATCH] fence_dli_pc8000: introduce dli_pc8000 fence agent Andrea Bucci
@ 2014-05-05 8:22 ` Marek Grac
0 siblings, 0 replies; 2+ messages in thread
From: Marek Grac @ 2014-05-05 8:22 UTC (permalink / raw)
To: cluster-devel.redhat.com
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 <alanoe@linux.vnet.ibm.com>
> Signed-off-by: Eduardo Kienetz <ebacchi@linux.vnet.ibm.com>
> Signed-off-by: Nathan Ozelim <natoze@linux.vnet.ibm.com>
> Signed-off-by: Plinio Freire <pleirie@linux.vnet.ibm.com>
> ---
> 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)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: dli_pc8000_adaptation.patch
Type: text/x-patch
Size: 8344 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/cluster-devel/attachments/20140505/0a16a431/attachment.bin>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2014-05-05 8:22 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-04-15 14:07 [Cluster-devel] [PATCH] fence_dli_pc8000: introduce dli_pc8000 fence agent Andrea Bucci
2014-05-05 8:22 ` Marek Grac
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).