From mboxrd@z Thu Jan 1 00:00:00 1970 From: Fabio M. Di Nitto Date: Sun, 17 Apr 2011 14:09:08 +0200 Subject: [Cluster-devel] [PATCH 1/3] New fencing script for Citrix XenServer and XCP. In-Reply-To: References: <1302324875-31166-1-git-send-email-mattjclark0407@hotmail.com>, <4DA28B29.2030303@redhat.com> , <4DA7FE17.80507@redhat.com> Message-ID: <4DAAD864.4030800@redhat.com> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit There are many ways to do it, but honestly, I don't have time to write a full git guideline ;) Easier way is to do a new clone from upstream and work on that one. It doesn't matter to me if it comes in 1 or 5 patches. Whatever you prefer is fine. Fabio On 04/17/2011 09:44 AM, Matt Clark wrote: > Sure, no problem. > > Is there a way to uncommit the three patches so that I can merge > everything into 1 patch, or do I need to add a 4th patch that fixes > everything up? I did have a bit of a search, but the only relevant info > I could find mentioned altering the most recent commit. Not rolling > three commits back or merging multiple commits together. > > Sorry, I am used to using svn and git seems to be a bit of a different > animal. > >> Date: Fri, 15 Apr 2011 10:13:11 +0200 >> From: fdinitto at redhat.com >> To: mattjclark0407 at hotmail.com >> CC: cluster-devel at redhat.com; mgrac at redhat.com >> Subject: Re: [Cluster-devel] [PATCH 1/3] New fencing script for Citrix > XenServer and XCP. >> >> hi Matt, >> >> Marek said the code is fine, can you please resubmit with the build fixes? >> >> Thanks >> fabio >> >> On 4/12/2011 4:46 AM, Matt Clark wrote: >> > Hi Fabio, >> > >> > Looks like I had a few problems moving the code over to git. Your >> > comments all sound perfectly valid. Do you want me to fix up and > resubmit? >> > >> > Cheers, >> > Matt. >> > >> >> Date: Mon, 11 Apr 2011 07:01:29 +0200 >> >> From: fdinitto at redhat.com >> >> To: cluster-devel at redhat.com; mgrac at redhat.com >> >> Subject: Re: [Cluster-devel] [PATCH 1/3] New fencing script for Citrix >> > XenServer and XCP. >> >> >> >> Hi Matt, >> >> >> >> I'll leave the comments to the code to Marek, but we will need to do a >> >> few adjustments here and there to get this into the tree. >> >> >> >> XenAPI.py being a fence library needs to move into the libs/ > section. It >> >> is not parsed/built/installed in this patch set. >> >> >> >> You sent us pre-built sources and that's not really ok. See for example >> >> the hardcoded path to the fence library and the addition of >> >> RELEASE_VERSION and BUILD_DATE at the end of the code for fence_xenapi. >> >> Those should be generated dynamically at build time to reflect the > build >> >> info. >> >> >> >> Because I really like to make sure that COPYRIGHT and licences >> >> information are as exact and clear as possible, please also update >> >> docs/COPYRIGHT in the "exception" area. While the information in the >> >> file are absolutely fine, most people tend to look for a >> >> COPYRIGHT/LICENCE file rather than editing the file itself. It's fair >> >> that your contribution deserves the right credit and such. >> >> >> >> Cheers >> >> Fabio >> >> >> >> On 04/09/2011 06:54 AM, Matt Clark wrote: >> >> > Fencing script that uses the XenAPI to allow remote switch, status >> > and list of virtual machines running on Citrix XenServer and Xen Cloud >> > Platform hosts. >> >> > --- >> >> > fence/agents/xenapi/Makefile.am | 17 +++ >> >> > fence/agents/xenapi/XenAPI.py | 209 ++++++++++++++++++++++++++++++++ >> >> > fence/agents/xenapi/fence_xenapi.py | 227 >> > +++++++++++++++++++++++++++++++++++ >> >> > 3 files changed, 453 insertions(+), 0 deletions(-) >> >> > create mode 100644 fence/agents/xenapi/Makefile.am >> >> > create mode 100755 fence/agents/xenapi/XenAPI.py >> >> > create mode 100644 fence/agents/xenapi/fence_xenapi.py >> >> > >> >> > diff --git a/fence/agents/xenapi/Makefile.am >> > b/fence/agents/xenapi/Makefile.am >> >> > new file mode 100644 >> >> > index 0000000..781975e >> >> > --- /dev/null >> >> > +++ b/fence/agents/xenapi/Makefile.am >> >> > @@ -0,0 +1,17 @@ >> >> > +MAINTAINERCLEANFILES = Makefile.in >> >> > + >> >> > +TARGET = fence_xenapi >> >> > + >> >> > +SRC = $(TARGET).py >> >> > + >> >> > +EXTRA_DIST = $(SRC) >> >> > + >> >> > +sbin_SCRIPTS = $(TARGET) >> >> > + >> >> > +man_MANS = $(TARGET).8 >> >> > + >> >> > +include $(top_srcdir)/make/fencebuild.mk >> >> > +include $(top_srcdir)/make/fenceman.mk >> >> > + >> >> > +clean-local: clean-man >> >> > + rm -f $(TARGET) >> >> > diff --git a/fence/agents/xenapi/XenAPI.py >> > b/fence/agents/xenapi/XenAPI.py >> >> > new file mode 100755 >> >> > index 0000000..4f27ef5 >> >> > --- /dev/null >> >> > +++ b/fence/agents/xenapi/XenAPI.py >> >> > @@ -0,0 +1,209 @@ >> >> > >> > > +#============================================================================ >> >> > +# This library is free software; you can redistribute it and/or >> >> > +# modify it under the terms of version 2.1 of the GNU Lesser >> > General Public >> >> > +# License as published by the Free Software Foundation. >> >> > +# >> >> > +# This library 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 >> >> > +# Lesser General Public License for more details. >> >> > +# >> >> > +# You should have received a copy of the GNU Lesser General Public >> >> > +# License along with this library; if not, write to the Free > Software >> >> > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA >> > 02111-1307 USA >> >> > >> > > +#============================================================================ >> >> > +# Copyright (C) 2006 XenSource Inc. >> >> > >> > > +#============================================================================ >> >> > +# >> >> > +# Parts of this file are based upon xmlrpclib.py, the XML-RPC client >> >> > +# interface included in the Python distribution. >> >> > +# >> >> > +# Copyright (c) 1999-2002 by Secret Labs AB >> >> > +# Copyright (c) 1999-2002 by Fredrik Lundh >> >> > +# >> >> > +# By obtaining, using, and/or copying this software and/or its >> >> > +# associated documentation, you agree that you have read, > understood, >> >> > +# and will comply with the following terms and conditions: >> >> > +# >> >> > +# Permission to use, copy, modify, and distribute this software and >> >> > +# its associated documentation for any purpose and without fee is >> >> > +# hereby granted, provided that the above copyright notice > appears in >> >> > +# all copies, and that both that copyright notice and this > permission >> >> > +# notice appear in supporting documentation, and that the name of >> >> > +# Secret Labs AB or the author not be used in advertising or > publicity >> >> > +# pertaining to distribution of the software without specific, > written >> >> > +# prior permission. >> >> > +# >> >> > +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD >> >> > +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- >> >> > +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE > AUTHOR >> >> > +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES > OR ANY >> >> > +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, >> >> > +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS >> >> > +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR > PERFORMANCE >> >> > +# OF THIS SOFTWARE. >> >> > +# > -------------------------------------------------------------------- >> >> > + >> >> > +import gettext >> >> > +import xmlrpclib >> >> > +import httplib >> >> > +import socket >> >> > + >> >> > +translation = gettext.translation('xen-xm', fallback = True) >> >> > + >> >> > +class Failure(Exception): >> >> > + def __init__(self, details): >> >> > + try: >> >> > + # If this failure is MESSAGE_PARAMETER_COUNT_MISMATCH, then we >> >> > + # correct the return values here, to account for the fact that we >> >> > + # transparently add the session handle as the first argument. >> >> > + if details[0] == 'MESSAGE_PARAMETER_COUNT_MISMATCH': >> >> > + details[2] = str(int(details[2]) - 1) >> >> > + details[3] = str(int(details[3]) - 1) >> >> > + >> >> > + self.details = details >> >> > + except Exception, exn: >> >> > + self.details = ['INTERNAL_ERROR', 'Client-side: ' + str(exn)] >> >> > + >> >> > + def __str__(self): >> >> > + try: >> >> > + return translation.ugettext(self.details[0]) % self._details_map() >> >> > + except TypeError, exn: >> >> > + return "Message database broken: %s.\nXen-API failure: %s" % \ >> >> > + (exn, str(self.details)) >> >> > + except Exception, exn: >> >> > + import sys >> >> > + print >>sys.stderr, exn >> >> > + return "Xen-API failure: %s" % str(self.details) >> >> > + >> >> > + def _details_map(self): >> >> > + return dict([(str(i), self.details[i]) >> >> > + for i in range(len(self.details))]) >> >> > + >> >> > + >> >> > +_RECONNECT_AND_RETRY = (lambda _ : ()) >> >> > + >> >> > +class UDSHTTPConnection(httplib.HTTPConnection): >> >> > + """ Stupid hacked up HTTPConnection subclass to allow HTTP over >> > Unix domain >> >> > + sockets. """ >> >> > + def connect(self): >> >> > + path = self.host.replace("_", "/") >> >> > + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) >> >> > + self.sock.connect(path) >> >> > + >> >> > +class UDSHTTP(httplib.HTTP): >> >> > + _connection_class = UDSHTTPConnection >> >> > + >> >> > +class UDSTransport(xmlrpclib.Transport): >> >> > + def make_connection(self, host): >> >> > + return UDSHTTP(host) >> >> > + >> >> > +class Session(xmlrpclib.ServerProxy): >> >> > + """A server proxy and session manager for communicating with Xend >> > using >> >> > + the Xen-API. >> >> > + >> >> > + Example: >> >> > + >> >> > + session = Session('http://localhost:9363/') >> >> > + session.login_with_password('me', 'mypassword') >> >> > + session.xenapi.VM.start(vm_uuid) >> >> > + session.xenapi.session.logout() >> >> > + >> >> > + For now, this class also supports the legacy XML-RPC API, using >> >> > + session.xend.domain('Domain-0') and similar. This support will >> > disappear >> >> > + once there is a working Xen-API replacement for every call in the >> > legacy >> >> > + API. >> >> > + """ >> >> > + >> >> > + def __init__(self, uri, transport=None, encoding=None, verbose=0, >> >> > + allow_none=1): >> >> > + xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, >> >> > + verbose, allow_none) >> >> > + self._session = None >> >> > + self.last_login_method = None >> >> > + self.last_login_params = None >> >> > + >> >> > + >> >> > + def xenapi_request(self, methodname, params): >> >> > + if methodname.startswith('login'): >> >> > + self._login(methodname, params) >> >> > + return None >> >> > + else: >> >> > + retry_count = 0 >> >> > + while retry_count < 3: >> >> > + full_params = (self._session,) + params >> >> > + result = _parse_result(getattr(self, methodname)(*full_params)) >> >> > + if result == _RECONNECT_AND_RETRY: >> >> > + retry_count += 1 >> >> > + if self.last_login_method: >> >> > + self._login(self.last_login_method, >> >> > + self.last_login_params) >> >> > + else: >> >> > + raise xmlrpclib.Fault(401, 'You must log in') >> >> > + else: >> >> > + return result >> >> > + raise xmlrpclib.Fault( >> >> > + 500, 'Tried 3 times to get a valid session, but failed') >> >> > + >> >> > + >> >> > + def _login(self, method, params): >> >> > + result = _parse_result(getattr(self, 'session.%s' % > method)(*params)) >> >> > + if result == _RECONNECT_AND_RETRY: >> >> > + raise xmlrpclib.Fault( >> >> > + 500, 'Received SESSION_INVALID when logging in') >> >> > + self._session = result >> >> > + self.last_login_method = method >> >> > + self.last_login_params = params >> >> > + >> >> > + >> >> > + def __getattr__(self, name): >> >> > + if name == 'xenapi': >> >> > + return _Dispatcher(self.xenapi_request, None) >> >> > + elif name.startswith('login'): >> >> > + return lambda *params: self._login(name, params) >> >> > + else: >> >> > + return xmlrpclib.ServerProxy.__getattr__(self, name) >> >> > + >> >> > +def xapi_local(): >> >> > + return Session("http://_var_xapi_xapi/", transport=UDSTransport()) >> >> > + >> >> > +def _parse_result(result): >> >> > + if type(result) != dict or 'Status' not in result: >> >> > + raise xmlrpclib.Fault(500, 'Missing Status in response from >> > server' + result) >> >> > + if result['Status'] == 'Success': >> >> > + if 'Value' in result: >> >> > + return result['Value'] >> >> > + else: >> >> > + raise xmlrpclib.Fault(500, >> >> > + 'Missing Value in response from server') >> >> > + else: >> >> > + if 'ErrorDescription' in result: >> >> > + if result['ErrorDescription'][0] == 'SESSION_INVALID': >> >> > + return _RECONNECT_AND_RETRY >> >> > + else: >> >> > + raise Failure(result['ErrorDescription']) >> >> > + else: >> >> > + raise xmlrpclib.Fault( >> >> > + 500, 'Missing ErrorDescription in response from server') >> >> > + >> >> > + >> >> > +# Based upon _Method from xmlrpclib. >> >> > +class _Dispatcher: >> >> > + def __init__(self, send, name): >> >> > + self.__send = send >> >> > + self.__name = name >> >> > + >> >> > + def __repr__(self): >> >> > + if self.__name: >> >> > + return '' % self.__name >> >> > + else: >> >> > + return '' >> >> > + >> >> > + def __getattr__(self, name): >> >> > + if self.__name is None: >> >> > + return _Dispatcher(self.__send, name) >> >> > + else: >> >> > + return _Dispatcher(self.__send, "%s.%s" % (self.__name, name)) >> >> > + >> >> > + def __call__(self, *args): >> >> > + return self.__send(self.__name, args) >> >> > diff --git a/fence/agents/xenapi/fence_xenapi.py >> > b/fence/agents/xenapi/fence_xenapi.py >> >> > new file mode 100644 >> >> > index 0000000..0657f4e >> >> > --- /dev/null >> >> > +++ b/fence/agents/xenapi/fence_xenapi.py >> >> > @@ -0,0 +1,227 @@ >> >> > +#!/usr/bin/python >> >> > +# >> >> > >> > > +############################################################################# >> >> > +# Copyright 2011 Matt Clark >> >> > +# This file is part of fence-xenserver >> >> > +# >> >> > +# fence-xenserver 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. >> >> > +# >> >> > +# fence-xenserver 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, see > . >> >> > + >> >> > +# Please let me know if you are using this script so that I can >> > work out >> >> > +# whether I should continue support for it. mattjclark0407 at >> > hotmail dot com >> >> > >> > > +############################################################################# >> >> > + >> >> > >> > > +############################################################################# >> >> > +# It's only just begun... >> >> > +# Current status: completely usable. This script is now working >> > well and, >> >> > +# has a lot of functionality as a result of the fencing.py library >> > and the >> >> > +# XenAPI libary. >> >> > + >> >> > >> > > +############################################################################# >> >> > +# Please let me know if you are using this script so that I can >> > work out >> >> > +# whether I should continue support for it. mattjclark0407 at >> > hotmail dot com >> >> > + >> >> > +import sys >> >> > +sys.path.append("/usr/lib/fence") >> >> > +from fencing import * >> >> > +import XenAPI >> >> > + >> >> > +EC_BAD_SESSION = 1 >> >> > +# Find the status of the port given in the -U flag of options. >> >> > +def get_power_fn(session, options): >> >> > + if options.has_key("-v"): >> >> > + verbose = True >> >> > + else: >> >> > + verbose = False >> >> > + >> >> > + try: >> >> > + # Get a reference to the vm specified in the UUID or vm_name/port >> > parameter >> >> > + vm = return_vm_reference(session, options) >> >> > + # Query the VM for its' associated parameters >> >> > + record = session.xenapi.VM.get_record(vm); >> >> > + # Check that we are not trying to manipulate a template or a > control >> >> > + # domain as they show up as VM's with specific properties. >> >> > + if not(record["is_a_template"]) and > not(record["is_control_domain"]): >> >> > + status = record["power_state"] >> >> > + if verbose: print "UUID:", record["uuid"], "NAME:", >> > record["name_label"], "POWER STATUS:", record["power_state"] >> >> > + # Note that the VM can be in the following states (from the XenAPI >> > document) >> >> > + # Halted: VM is offline and not using any resources. >> >> > + # Paused: All resources have been allocated but the VM itself is >> > paused and its vCPUs are not running >> >> > + # Running: Running >> >> > + # Paused: VM state has been saved to disk and it is nolonger >> > running. Note that disks remain in-Use while >> >> > + # We want to make sure that we only return the status "off" if the >> > machine is actually halted as the status >> >> > + # is checked before a fencing action. Only when the machine is >> > Halted is it not consuming resources which >> >> > + # may include whatever you are trying to protect with this fencing >> > action. >> >> > + return (status=="Halted" and "off" or "on") >> >> > + except Exception, exn: >> >> > + print str(exn) >> >> > + >> >> > + return "Error" >> >> > + >> >> > +# Set the state of the port given in the -U flag of options. >> >> > +def set_power_fn(session, options): >> >> > + action = options["-o"].lower() >> >> > + if options.has_key("-v"): >> >> > + verbose = True >> >> > + else: >> >> > + verbose = False >> >> > + >> >> > + try: >> >> > + # Get a reference to the vm specified in the UUID or vm_name/port >> > parameter >> >> > + vm = return_vm_reference(session, options) >> >> > + # Query the VM for its' associated parameters >> >> > + record = session.xenapi.VM.get_record(vm) >> >> > + # Check that we are not trying to manipulate a template or a > control >> >> > + # domain as they show up as VM's with specific properties. >> >> > + if not(record["is_a_template"]) and > not(record["is_control_domain"]): >> >> > + if( action == "on" ): >> >> > + # Start the VM >> >> > + session.xenapi.VM.start(vm, False, True) >> >> > + elif( action == "off" ): >> >> > + # Force shutdown the VM >> >> > + session.xenapi.VM.hard_shutdown(vm) >> >> > + elif( action == "reboot" ): >> >> > + # Force reboot the VM >> >> > + session.xenapi.VM.hard_reboot(vm) >> >> > + except Exception, exn: >> >> > + print str(exn); >> >> > + >> >> > +# Function to populate an array of virtual machines and their status >> >> > +def get_outlet_list(session, options): >> >> > + result = {} >> >> > + if options.has_key("-v"): >> >> > + verbose = True >> >> > + else: >> >> > + verbose = False >> >> > + >> >> > + try: >> >> > + # Return an array of all the VM's on the host >> >> > + vms = session.xenapi.VM.get_all() >> >> > + for vm in vms: >> >> > + # Query the VM for its' associated parameters >> >> > + record = session.xenapi.VM.get_record(vm); >> >> > + # Check that we are not trying to manipulate a template or a > control >> >> > + # domain as they show up as VM's with specific properties. >> >> > + if not(record["is_a_template"]) and > not(record["is_control_domain"]): >> >> > + name = record["name_label"] >> >> > + uuid = record["uuid"] >> >> > + status = record["power_state"] >> >> > + result[uuid] = (name, status) >> >> > + if verbose: print "UUID:", record["uuid"], "NAME:", name, "POWER >> > STATUS:", record["power_state"] >> >> > + except Exception, exn: >> >> > + print str(exn); >> >> > + >> >> > + return result >> >> > + >> >> > +# Function to initiate the XenServer session via the XenAPI library. >> >> > +def connect_and_login(options): >> >> > + url = options["-s"] >> >> > + username = options["-l"] >> >> > + password = options["-p"] >> >> > + >> >> > + try: >> >> > + # Create the XML RPC session to the specified URL. >> >> > + session = XenAPI.Session(url); >> >> > + # Login using the supplied credentials. >> >> > + session.xenapi.login_with_password(username, password); >> >> > + except Exception, exn: >> >> > + print str(exn); >> >> > + # http://sources.redhat.com/cluster/wiki/FenceAgentAPI says that >> > for no connectivity >> >> > + # the exit value should be 1. It doesn't say anything about failed >> > logins, so >> >> > + # until I hear otherwise it is best to keep this exit the same to >> > make sure that >> >> > + # anything calling this script (that uses the same information in >> > the web page >> >> > + # above) knows that this is an error condition, not a msg >> > signifying a down port. >> >> > + sys.exit(EC_BAD_SESSION); >> >> > + return session; >> >> > + >> >> > +# return a reference to the VM by either using the UUID or the >> > vm_name/port. If the UUID is set then >> >> > +# this is tried first as this is the only properly unique > identifier. >> >> > +# Exceptions are not handled in this function, code that calls this >> > must be ready to handle them. >> >> > +def return_vm_reference(session, options): >> >> > + if options.has_key("-v"): >> >> > + verbose = True >> >> > + else: >> >> > + verbose = False >> >> > + >> >> > + # Case where the UUID has been specified >> >> > + if options.has_key("-U"): >> >> > + uuid = options["-U"].lower() >> >> > + # When using the -n parameter for name, we get an error message >> > (in verbose >> >> > + # mode) that tells us that we didn't find a VM. To immitate that >> > here we >> >> > + # need to catch and re-raise the exception produced by get_by_uuid. >> >> > + try: >> >> > + return session.xenapi.VM.get_by_uuid(uuid) >> >> > + except Exception,exn: >> >> > + if verbose: print "No VM's found with a UUID of \"%s\"" %uuid >> >> > + raise >> >> > + >> >> > + >> >> > + # Case where the vm_name/port has been specified >> >> > + if options.has_key("-n"): >> >> > + vm_name = options["-n"] >> >> > + vm_arr = session.xenapi.VM.get_by_name_label(vm_name) >> >> > + # Need to make sure that we only have one result as the vm_name may >> >> > + # not be unique. Average case, so do it first. >> >> > + if len(vm_arr) == 1: >> >> > + return vm_arr[0] >> >> > + else: >> >> > + if len(vm_arr) == 0: >> >> > + if verbose: print "No VM's found with a name of \"%s\"" %vm_name >> >> > + # NAME_INVALID used as the XenAPI throws a UUID_INVALID if it >> > can't find >> >> > + # a VM with the specified UUID. This should make the output look >> > fairly >> >> > + # consistent. >> >> > + raise Exception("NAME_INVALID") >> >> > + else: >> >> > + if verbose: print "Multiple VM's have the name \"%s\", use UUID >> > instead" %vm_name >> >> > + raise Exception("MULTIPLE_VMS_FOUND") >> >> > + >> >> > + # We should never get to this case as the input processing checks >> > that either the UUID or >> >> > + # the name parameter is set. Regardless of whether or not a VM is >> > found the above if >> >> > + # statements will return to the calling function (either by >> > exception or by a reference >> >> > + # to the VM). >> >> > + raise Exception("VM_LOGIC_ERROR") >> >> > + >> >> > +def main(): >> >> > + >> >> > + device_opt = [ "help", "version", "agent", "quiet", "verbose", >> > "debug", "action", >> >> > + "login", "passwd", "passwd_script", "port", "test", "separator", >> >> > + "no_login", "no_password", "power_timeout", "shell_timeout", >> >> > + "login_timeout", "power_wait", "session_url", "uuid" ] >> >> > + >> >> > + atexit.register(atexit_handler) >> >> > + >> >> > + options=process_input(device_opt) >> >> > + >> >> > + options = check_input(device_opt, options) >> >> > + >> >> > + docs = { } >> >> > + docs["shortdesc"] = "XenAPI based fencing for the Citrix XenServer >> > virtual machines." >> >> > + docs["longdesc"] = "\ >> >> > +fence_cxs is an I/O Fencing agent used on Citrix XenServer hosts. \ >> >> > +It uses the XenAPI, supplied by Citrix, to establish an XML-RPC >> > sesssion \ >> >> > +to a XenServer host. Once the session is established, further > XML-RPC \ >> >> > +commands are issued in order to switch on, switch off, restart and >> > query \ >> >> > +the status of virtual machines running on the host." >> >> > + show_docs(options, docs) >> >> > + >> >> > + xenSession = connect_and_login(options) >> >> > + >> >> > + # Operate the fencing device >> >> > + result = fence_action(xenSession, options, set_power_fn, >> > get_power_fn, get_outlet_list) >> >> > + >> >> > + sys.exit(result) >> >> > + >> >> > +if __name__ == "__main__": >> >> > + main() >> >> > +RELEASE_VERSION="3.1.2.11-2b5b-dirty" >> >> > +BUILD_DATE="(built Fri Mar 25 22:57:28 EST 2011)" >> >> >>