/usr/sbin/fence_xenapi is in fence-agents 3.1.5-2ubuntu2.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | #!/usr/bin/python
#
#############################################################################
# Copyright 2011 Matthew 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 <http://www.gnu.org/licenses/>.
# 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/share/fence")
from fencing import *
import XenAPI
#BEGIN_VERSION_GENERATION
RELEASE_VERSION="3.1.5"
BUILD_DATE="(built Mon Feb 6 23:36:22 UTC 2012)"
REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved."
#END_VERSION_GENERATION
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()
|