Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F2022273
fencing.py.py
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
48 KB
Referenced Files
None
Subscribers
None
fencing.py.py
View Options
#!@PYTHON@ -tt
import
sys
,
getopt
,
time
,
os
,
uuid
,
pycurl
,
stat
import
pexpect
,
re
,
syslog
import
logging
import
subprocess
import
threading
import
shlex
import
socket
import
textwrap
import
__main__
RELEASE_VERSION
=
"@RELEASE_VERSION@"
__all__
=
[
'atexit_handler'
,
'check_input'
,
'process_input'
,
'all_opt'
,
'show_docs'
,
'fence_login'
,
'fence_action'
,
'fence_logout'
]
EC_OK
=
0
EC_GENERIC_ERROR
=
1
EC_BAD_ARGS
=
2
EC_LOGIN_DENIED
=
3
EC_CONNECTION_LOST
=
4
EC_TIMED_OUT
=
5
EC_WAITING_ON
=
6
EC_WAITING_OFF
=
7
EC_STATUS
=
8
EC_STATUS_HMC
=
9
EC_PASSWORD_MISSING
=
10
EC_INVALID_PRIVILEGES
=
11
EC_FETCH_VM_UUID
=
12
LOG_FORMAT
=
"
%(asctime)-15s
%(levelname)s
:
%(message)s
"
all_opt
=
{
"help"
:
{
"getopt"
:
"h"
,
"longopt"
:
"help"
,
"help"
:
"-h, --help Display this help and exit"
,
"required"
:
"0"
,
"shortdesc"
:
"Display help and exit"
,
"order"
:
54
},
"version"
:
{
"getopt"
:
"V"
,
"longopt"
:
"version"
,
"help"
:
"-V, --version Display version information and exit"
,
"required"
:
"0"
,
"shortdesc"
:
"Display version information and exit"
,
"order"
:
53
},
"verbose"
:
{
"getopt"
:
"v"
,
"longopt"
:
"verbose"
,
"help"
:
"-v, --verbose Verbose mode"
,
"required"
:
"0"
,
"order"
:
51
},
"debug"
:
{
"getopt"
:
"D:"
,
"longopt"
:
"debug-file"
,
"help"
:
"-D, --debug-file=[debugfile] Debugging to output file"
,
"required"
:
"0"
,
"shortdesc"
:
"Write debug information to given file"
,
"order"
:
52
},
"delay"
:
{
"getopt"
:
":"
,
"longopt"
:
"delay"
,
"type"
:
"second"
,
"help"
:
"--delay=[seconds] Wait X seconds before fencing is started"
,
"required"
:
"0"
,
"default"
:
"0"
,
"order"
:
200
},
"agent"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"web"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"force_on"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"action"
:
{
"getopt"
:
"o:"
,
"longopt"
:
"action"
,
"help"
:
"-o, --action=[action] Action: status, reboot (default), off or on"
,
"required"
:
"1"
,
"shortdesc"
:
"Fencing action"
,
"default"
:
"reboot"
,
"order"
:
1
},
"fabric_fencing"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"ipaddr"
:
{
"getopt"
:
"a:"
,
"longopt"
:
"ip"
,
"help"
:
"-a, --ip=[ip] IP address or hostname of fencing device"
,
"required"
:
"1"
,
"order"
:
1
},
"ipport"
:
{
"getopt"
:
"u:"
,
"longopt"
:
"ipport"
,
"type"
:
"integer"
,
"help"
:
"-u, --ipport=[port] TCP/UDP port to use for connection"
,
"required"
:
"0"
,
"shortdesc"
:
"TCP/UDP port to use for connection with device"
,
"order"
:
1
},
"login"
:
{
"getopt"
:
"l:"
,
"longopt"
:
"username"
,
"help"
:
"-l, --username=[name] Login name"
,
"required"
:
"?"
,
"order"
:
1
},
"no_login"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"no_password"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"no_port"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"no_status"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"no_on"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"no_off"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"telnet"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"diag"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"passwd"
:
{
"getopt"
:
"p:"
,
"longopt"
:
"password"
,
"help"
:
"-p, --password=[password] Login password or passphrase"
,
"required"
:
"0"
,
"order"
:
1
},
"passwd_script"
:
{
"getopt"
:
"S:"
,
"longopt"
:
"password-script"
,
"help"
:
"-S, --password-script=[script] Script to run to retrieve password"
,
"required"
:
"0"
,
"order"
:
1
},
"identity_file"
:
{
"getopt"
:
"k:"
,
"longopt"
:
"identity-file"
,
"help"
:
"-k, --identity-file=[filename] Identity file (private key) for SSH"
,
"required"
:
"0"
,
"order"
:
1
},
"cmd_prompt"
:
{
"getopt"
:
"c:"
,
"longopt"
:
"command-prompt"
,
"help"
:
"-c, --command-prompt=[prompt] Force Python regex for command prompt"
,
"required"
:
"0"
,
"order"
:
1
},
"secure"
:
{
"getopt"
:
"x"
,
"longopt"
:
"ssh"
,
"help"
:
"-x, --ssh Use SSH connection"
,
"required"
:
"0"
,
"order"
:
1
},
"ssh_options"
:
{
"getopt"
:
":"
,
"longopt"
:
"ssh-options"
,
"help"
:
"--ssh-options=[options] SSH options to use"
,
"required"
:
"0"
,
"order"
:
1
},
"ssl"
:
{
"getopt"
:
"z"
,
"longopt"
:
"ssl"
,
"help"
:
"-z, --ssl Use SSL connection with verifying certificate"
,
"required"
:
"0"
,
"order"
:
1
},
"ssl_insecure"
:
{
"getopt"
:
""
,
"longopt"
:
"ssl-insecure"
,
"help"
:
"--ssl-insecure Use SSL connection without verifying certificate"
,
"required"
:
"0"
,
"order"
:
1
},
"ssl_secure"
:
{
"getopt"
:
""
,
"longopt"
:
"ssl-secure"
,
"help"
:
"--ssl-secure Use SSL connection with verifying certificate"
,
"required"
:
"0"
,
"order"
:
1
},
"notls"
:
{
"getopt"
:
"t"
,
"longopt"
:
"notls"
,
"help"
:
"-t, --notls "
"Disable TLS negotiation and force SSL3.0. "
"This should only be used for devices that do not support TLS1.0 and up."
,
"required"
:
"0"
,
"order"
:
1
},
"tls1.0"
:
{
"getopt"
:
""
,
"longopt"
:
"tls1.0"
,
"help"
:
"--tls1.0 "
"Disable TLS negotiation and force TLS1.0. "
"This should only be used for devices that do not support TLS1.1 and up."
,
"required"
:
"0"
,
"order"
:
1
},
"port"
:
{
"getopt"
:
"n:"
,
"longopt"
:
"plug"
,
"help"
:
"-n, --plug=[id] "
"Physical plug number on device, UUID or identification of machine"
,
"required"
:
"1"
,
"order"
:
1
},
"switch"
:
{
"getopt"
:
"s:"
,
"longopt"
:
"switch"
,
"help"
:
"-s, --switch=[id] Physical switch number on device"
,
"required"
:
"0"
,
"order"
:
1
},
"exec"
:
{
"getopt"
:
"e:"
,
"longopt"
:
"exec"
,
"help"
:
"-e, --exec=[command] Command to execute"
,
"required"
:
"0"
,
"order"
:
1
},
"vmware_type"
:
{
"getopt"
:
"d:"
,
"longopt"
:
"vmware_type"
,
"help"
:
"-d, --vmware_type=[type] Type of VMware to connect"
,
"required"
:
"0"
,
"order"
:
1
},
"vmware_datacenter"
:
{
"getopt"
:
"s:"
,
"longopt"
:
"vmware-datacenter"
,
"help"
:
"-s, --vmware-datacenter=[dc] VMWare datacenter filter"
,
"required"
:
"0"
,
"order"
:
2
},
"snmp_version"
:
{
"getopt"
:
"d:"
,
"longopt"
:
"snmp-version"
,
"help"
:
"-d, --snmp-version=[version] Specifies SNMP version to use (1|2c|3)"
,
"required"
:
"0"
,
"shortdesc"
:
"Specifies SNMP version to use"
,
"choices"
:
[
"1"
,
"2c"
,
"3"
],
"order"
:
1
},
"community"
:
{
"getopt"
:
"c:"
,
"longopt"
:
"community"
,
"help"
:
"-c, --community=[community] Set the community string"
,
"required"
:
"0"
,
"order"
:
1
},
"snmp_auth_prot"
:
{
"getopt"
:
"b:"
,
"longopt"
:
"snmp-auth-prot"
,
"help"
:
"-b, --snmp-auth-prot=[prot] Set authentication protocol (MD5|SHA)"
,
"required"
:
"0"
,
"shortdesc"
:
"Set authentication protocol"
,
"choices"
:
[
"MD5"
,
"SHA"
],
"order"
:
1
},
"snmp_sec_level"
:
{
"getopt"
:
"E:"
,
"longopt"
:
"snmp-sec-level"
,
"help"
:
"-E, --snmp-sec-level=[level] "
"Set security level (noAuthNoPriv|authNoPriv|authPriv)"
,
"required"
:
"0"
,
"shortdesc"
:
"Set security level"
,
"choices"
:
[
"noAuthNoPriv"
,
"authNoPriv"
,
"authPriv"
],
"order"
:
1
},
"snmp_priv_prot"
:
{
"getopt"
:
"B:"
,
"longopt"
:
"snmp-priv-prot"
,
"help"
:
"-B, --snmp-priv-prot=[prot] Set privacy protocol (DES|AES)"
,
"required"
:
"0"
,
"shortdesc"
:
"Set privacy protocol"
,
"choices"
:
[
"DES"
,
"AES"
],
"order"
:
1
},
"snmp_priv_passwd"
:
{
"getopt"
:
"P:"
,
"longopt"
:
"snmp-priv-passwd"
,
"help"
:
"-P, --snmp-priv-passwd=[pass] Set privacy protocol password"
,
"required"
:
"0"
,
"order"
:
1
},
"snmp_priv_passwd_script"
:
{
"getopt"
:
"R:"
,
"longopt"
:
"snmp-priv-passwd-script"
,
"help"
:
"-R, --snmp-priv-passwd-script Script to run to retrieve privacy password"
,
"required"
:
"0"
,
"order"
:
1
},
"inet4_only"
:
{
"getopt"
:
"4"
,
"longopt"
:
"inet4-only"
,
"help"
:
"-4, --inet4-only Forces agent to use IPv4 addresses only"
,
"required"
:
"0"
,
"order"
:
1
},
"inet6_only"
:
{
"getopt"
:
"6"
,
"longopt"
:
"inet6-only"
,
"help"
:
"-6, --inet6-only Forces agent to use IPv6 addresses only"
,
"required"
:
"0"
,
"order"
:
1
},
"separator"
:
{
"getopt"
:
"C:"
,
"longopt"
:
"separator"
,
"help"
:
"-C, --separator=[char] Separator for CSV created by 'list' operation"
,
"default"
:
","
,
"required"
:
"0"
,
"order"
:
100
},
"login_timeout"
:
{
"getopt"
:
":"
,
"longopt"
:
"login-timeout"
,
"type"
:
"second"
,
"help"
:
"--login-timeout=[seconds] Wait X seconds for cmd prompt after login"
,
"default"
:
"5"
,
"required"
:
"0"
,
"order"
:
200
},
"shell_timeout"
:
{
"getopt"
:
":"
,
"longopt"
:
"shell-timeout"
,
"type"
:
"second"
,
"help"
:
"--shell-timeout=[seconds] Wait X seconds for cmd prompt after issuing command"
,
"default"
:
"3"
,
"required"
:
"0"
,
"order"
:
200
},
"power_timeout"
:
{
"getopt"
:
":"
,
"longopt"
:
"power-timeout"
,
"type"
:
"second"
,
"help"
:
"--power-timeout=[seconds] Test X seconds for status change after ON/OFF"
,
"default"
:
"20"
,
"required"
:
"0"
,
"order"
:
200
},
"power_wait"
:
{
"getopt"
:
":"
,
"longopt"
:
"power-wait"
,
"type"
:
"second"
,
"help"
:
"--power-wait=[seconds] Wait X seconds after issuing ON/OFF"
,
"default"
:
"0"
,
"required"
:
"0"
,
"order"
:
200
},
"missing_as_off"
:
{
"getopt"
:
""
,
"longopt"
:
"missing-as-off"
,
"help"
:
"--missing-as-off Missing port returns OFF instead of failure"
,
"required"
:
"0"
,
"order"
:
200
},
"retry_on"
:
{
"getopt"
:
":"
,
"longopt"
:
"retry-on"
,
"type"
:
"integer"
,
"help"
:
"--retry-on=[attempts] Count of attempts to retry power on"
,
"default"
:
"1"
,
"required"
:
"0"
,
"order"
:
201
},
"session_url"
:
{
"getopt"
:
"s:"
,
"longopt"
:
"session-url"
,
"help"
:
"-s, --session-url URL to connect to XenServer on"
,
"required"
:
"1"
,
"order"
:
1
},
"sudo"
:
{
"getopt"
:
""
,
"longopt"
:
"use-sudo"
,
"help"
:
"--use-sudo Use sudo (without password) when calling 3rd party software"
,
"required"
:
"0"
,
"order"
:
205
},
"method"
:
{
"getopt"
:
"m:"
,
"longopt"
:
"method"
,
"help"
:
"-m, --method=[method] Method to fence (onoff|cycle) (Default: onoff)"
,
"required"
:
"0"
,
"shortdesc"
:
"Method to fence"
,
"default"
:
"onoff"
,
"choices"
:
[
"onoff"
,
"cycle"
],
"order"
:
1
},
"telnet_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"telnet-path"
,
"help"
:
"--telnet-path=[path] Path to telnet binary"
,
"required"
:
"0"
,
"default"
:
"@TELNET_PATH@"
,
"order"
:
300
},
"ssh_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"ssh-path"
,
"help"
:
"--ssh-path=[path] Path to ssh binary"
,
"required"
:
"0"
,
"default"
:
"@SSH_PATH@"
,
"order"
:
300
},
"gnutlscli_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"gnutlscli-path"
,
"help"
:
"--gnutlscli-path=[path] Path to gnutls-cli binary"
,
"required"
:
"0"
,
"default"
:
"@GNUTLSCLI_PATH@"
,
"order"
:
300
},
"sudo_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"sudo-path"
,
"help"
:
"--sudo-path=[path] Path to sudo binary"
,
"required"
:
"0"
,
"default"
:
"@SUDO_PATH@"
,
"order"
:
300
},
"snmpwalk_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"snmpwalk-path"
,
"help"
:
"--snmpwalk-path=[path] Path to snmpwalk binary"
,
"required"
:
"0"
,
"default"
:
"@SNMPWALK_PATH@"
,
"order"
:
300
},
"snmpset_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"snmpset-path"
,
"help"
:
"--snmpset-path=[path] Path to snmpset binary"
,
"required"
:
"0"
,
"default"
:
"@SNMPSET_PATH@"
,
"order"
:
300
},
"snmpget_path"
:
{
"getopt"
:
":"
,
"longopt"
:
"snmpget-path"
,
"help"
:
"--snmpget-path=[path] Path to snmpget binary"
,
"required"
:
"0"
,
"default"
:
"@SNMPGET_PATH@"
,
"order"
:
300
},
"snmp"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"port_as_ip"
:
{
"getopt"
:
""
,
"longopt"
:
"port-as-ip"
,
"help"
:
"--port-as-ip Make
\"
port/plug
\"
to be an alias to IP address"
,
"required"
:
"0"
,
"order"
:
200
},
"on_target"
:
{
"getopt"
:
""
,
"help"
:
""
,
"order"
:
1
},
"quiet"
:
{
"getopt"
:
"q"
,
"longopt"
:
"quiet"
,
"help"
:
"-q, --quiet Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog."
,
"required"
:
"0"
,
"order"
:
50
}
}
# options which are added automatically if 'key' is encountered ("default" is always added)
DEPENDENCY_OPT
=
{
"default"
:
[
"help"
,
"debug"
,
"verbose"
,
"version"
,
"action"
,
"agent"
,
\
"power_timeout"
,
"shell_timeout"
,
"login_timeout"
,
"power_wait"
,
"retry_on"
,
\
"delay"
,
"quiet"
],
"passwd"
:
[
"passwd_script"
],
"sudo"
:
[
"sudo_path"
],
"secure"
:
[
"identity_file"
,
"ssh_options"
,
"ssh_path"
],
"telnet"
:
[
"telnet_path"
],
"ipaddr"
:
[
"ipport"
,
"inet4_only"
,
"inet6_only"
],
"port"
:
[
"separator"
],
"ssl"
:
[
"ssl_secure"
,
"ssl_insecure"
,
"gnutlscli_path"
],
"snmp"
:
[
"snmp_auth_prot"
,
"snmp_sec_level"
,
"snmp_priv_prot"
,
\
"snmp_priv_passwd"
,
"snmp_priv_passwd_script"
,
"community"
,
\
"snmpset_path"
,
"snmpget_path"
,
"snmpwalk_path"
]
}
class
fspawn
(
pexpect
.
spawn
):
def
__init__
(
self
,
options
,
command
,
**
kwargs
):
if
sys
.
version_info
[
0
]
>
2
:
kwargs
.
setdefault
(
'encoding'
,
'utf-8'
)
logging
.
info
(
"Running command:
%s
"
,
command
)
pexpect
.
spawn
.
__init__
(
self
,
command
,
**
kwargs
)
self
.
opt
=
options
def
log_expect
(
self
,
pattern
,
timeout
):
result
=
self
.
expect
(
pattern
,
timeout
)
logging
.
debug
(
"Received:
%s
"
,
self
.
before
+
self
.
after
)
return
result
def
send
(
self
,
message
):
logging
.
debug
(
"Sent:
%s
"
,
message
)
return
pexpect
.
spawn
.
send
(
self
,
message
)
# send EOL according to what was detected in login process (telnet)
def
send_eol
(
self
,
message
):
return
self
.
send
(
message
+
self
.
opt
[
"eol"
])
def
frun
(
command
,
timeout
=
30
,
withexitstatus
=
False
,
events
=
None
,
extra_args
=
None
,
logfile
=
None
,
cwd
=
None
,
env
=
None
,
**
kwargs
):
if
sys
.
version_info
[
0
]
>
2
:
kwargs
.
setdefault
(
'encoding'
,
'utf-8'
)
return
pexpect
.
run
(
command
,
timeout
=
timeout
,
withexitstatus
=
withexitstatus
,
events
=
events
,
extra_args
=
extra_args
,
logfile
=
logfile
,
cwd
=
cwd
,
env
=
env
,
**
kwargs
)
def
atexit_handler
():
try
:
sys
.
stdout
.
close
()
os
.
close
(
1
)
except
IOError
:
logging
.
error
(
"
%s
failed to close standard output
\n
"
,
sys
.
argv
[
0
])
sys
.
exit
(
EC_GENERIC_ERROR
)
def
_add_dependency_options
(
options
):
## Add also options which are available for every fence agent
added_opt
=
[]
for
opt
in
options
+
[
"default"
]:
if
opt
in
DEPENDENCY_OPT
:
added_opt
.
extend
([
y
for
y
in
DEPENDENCY_OPT
[
opt
]
if
options
.
count
(
y
)
==
0
])
if
not
"port"
in
(
options
+
added_opt
)
and
\
not
"nodename"
in
(
options
+
added_opt
)
and
\
"ipaddr"
in
(
options
+
added_opt
):
added_opt
.
append
(
"port_as_ip"
)
all_opt
[
"port"
][
"help"
]
=
"-n, --plug=[ip] IP address or hostname of fencing device "
\
"(together with --port-as-ip)"
return
added_opt
def
fail_usage
(
message
=
""
,
stop
=
True
):
if
len
(
message
)
>
0
:
logging
.
error
(
"
%s
\n
"
,
message
)
if
stop
:
logging
.
error
(
"Please use '-h' for usage
\n
"
)
sys
.
exit
(
EC_GENERIC_ERROR
)
def
fail
(
error_code
):
message
=
{
EC_LOGIN_DENIED
:
"Unable to connect/login to fencing device"
,
EC_CONNECTION_LOST
:
"Connection lost"
,
EC_TIMED_OUT
:
"Connection timed out"
,
EC_WAITING_ON
:
"Failed: Timed out waiting to power ON"
,
EC_WAITING_OFF
:
"Failed: Timed out waiting to power OFF"
,
EC_STATUS
:
"Failed: Unable to obtain correct plug status or plug is not available"
,
EC_STATUS_HMC
:
"Failed: Either unable to obtain correct plug status, "
"partition is not available or incorrect HMC version used"
,
EC_PASSWORD_MISSING
:
"Failed: You have to set login password"
,
EC_INVALID_PRIVILEGES
:
"Failed: The user does not have the correct privileges to do the requested action."
,
EC_FETCH_VM_UUID
:
"Failed: Can not find VM UUID by its VM name given in the <plug> parameter."
}[
error_code
]
+
"
\n
"
logging
.
error
(
"
%s
\n
"
,
message
)
sys
.
exit
(
EC_GENERIC_ERROR
)
def
usage
(
avail_opt
):
print
(
"Usage:"
)
print
(
"
\t
"
+
os
.
path
.
basename
(
sys
.
argv
[
0
])
+
" [options]"
)
print
(
"Options:"
)
sorted_list
=
[(
key
,
all_opt
[
key
])
for
key
in
avail_opt
]
sorted_list
.
sort
(
key
=
lambda
x
:
x
[
1
][
"order"
])
for
key
,
value
in
sorted_list
:
if
len
(
value
[
"help"
])
!=
0
:
print
(
" "
+
_join_wrap
([
value
[
"help"
]],
first_indent
=
3
))
def
metadata
(
options
,
avail_opt
,
docs
):
# avail_opt has to be unique, if there are duplicities then they should be removed
sorted_list
=
[(
key
,
all_opt
[
key
])
for
key
in
list
(
set
(
avail_opt
))
if
"longopt"
in
all_opt
[
key
]]
# Find keys that are going to replace inconsistent names
mapping
=
dict
([(
opt
[
"longopt"
]
.
replace
(
"-"
,
"_"
),
key
)
for
(
key
,
opt
)
in
sorted_list
if
(
key
!=
opt
[
"longopt"
]
.
replace
(
"-"
,
"_"
))])
new_options
=
[(
key
,
all_opt
[
mapping
[
key
]])
for
key
in
mapping
]
sorted_list
.
extend
(
new_options
)
sorted_list
.
sort
(
key
=
lambda
x
:
(
x
[
1
][
"order"
],
x
[
0
]))
if
options
[
"--action"
]
==
"metadata"
:
docs
[
"longdesc"
]
=
re
.
sub
(
"\.P|\.TP|\.br
\n
"
,
""
,
docs
[
"longdesc"
])
print
(
"<?xml version=
\"
1.0
\"
?>"
)
print
(
"<resource-agent name=
\"
"
+
os
.
path
.
basename
(
sys
.
argv
[
0
])
+
\
"
\"
shortdesc=
\"
"
+
docs
[
"shortdesc"
]
+
"
\"
>"
)
for
(
symlink
,
desc
)
in
docs
.
get
(
"symlink"
,
[]):
print
(
"<symlink name=
\"
"
+
symlink
+
"
\"
shortdesc=
\"
"
+
desc
+
"
\"
/>"
)
print
(
"<longdesc>"
+
docs
[
"longdesc"
]
+
"</longdesc>"
)
print
(
"<vendor-url>"
+
docs
[
"vendorurl"
]
+
"</vendor-url>"
)
print
(
"<parameters>"
)
for
(
key
,
opt
)
in
sorted_list
:
info
=
""
if
key
in
all_opt
:
if
key
!=
all_opt
[
key
]
.
get
(
'longopt'
,
key
)
.
replace
(
"-"
,
"_"
):
info
=
"deprecated=
\"
1
\"
"
else
:
info
=
"obsoletes=
\"
%s
\"
"
%
(
mapping
.
get
(
key
))
if
"help"
in
opt
and
len
(
opt
[
"help"
])
>
0
:
if
info
!=
""
:
info
=
" "
+
info
print
(
"
\t
<parameter name=
\"
"
+
key
+
"
\"
unique=
\"
0
\"
required=
\"
"
+
opt
[
"required"
]
+
"
\"
"
+
info
+
">"
)
default
=
""
if
"default"
in
opt
:
default
=
"default=
\"
"
+
_encode_html_entities
(
str
(
opt
[
"default"
]))
+
"
\"
"
mixed
=
opt
[
"help"
]
## split it between option and help text
res
=
re
.
compile
(
r"^(.*?--\S+)\s+"
,
re
.
IGNORECASE
|
re
.
S
)
.
search
(
mixed
)
if
None
!=
res
:
mixed
=
res
.
group
(
1
)
mixed
=
_encode_html_entities
(
mixed
)
if
not
"shortdesc"
in
opt
:
shortdesc
=
re
.
sub
(
"\s\s+"
,
" "
,
opt
[
"help"
][
31
:])
else
:
shortdesc
=
opt
[
"shortdesc"
]
print
(
"
\t\t
<getopt mixed=
\"
"
+
mixed
+
"
\"
/>"
)
if
"choices"
in
opt
:
print
(
"
\t\t
<content type=
\"
select
\"
"
+
default
+
" >"
)
for
choice
in
opt
[
"choices"
]:
print
(
"
\t\t\t
<option value=
\"
%s
\"
/>"
%
(
choice
))
print
(
"
\t\t
</content>"
)
elif
opt
[
"getopt"
]
.
count
(
":"
)
>
0
:
t
=
opt
.
get
(
"type"
,
"string"
)
print
(
"
\t\t
<content type=
\"
%s
\"
"
%
(
t
)
+
default
+
" />"
)
else
:
print
(
"
\t\t
<content type=
\"
boolean
\"
"
+
default
+
" />"
)
print
(
"
\t\t
<shortdesc lang=
\"
en
\"
>"
+
shortdesc
+
"</shortdesc>"
)
print
(
"
\t
</parameter>"
)
print
(
"</parameters>"
)
print
(
"<actions>"
)
(
available_actions
,
_
)
=
_get_available_actions
(
avail_opt
)
if
"on"
in
available_actions
:
available_actions
.
remove
(
"on"
)
on_target
=
' on_target="1"'
if
avail_opt
.
count
(
"on_target"
)
else
''
print
(
"
\t
<action name=
\"
on
\"
%s
automatic=
\"
%d
\"
/>"
%
(
on_target
,
avail_opt
.
count
(
"fabric_fencing"
)))
for
action
in
available_actions
:
print
(
"
\t
<action name=
\"
%s
\"
/>"
%
(
action
))
print
(
"</actions>"
)
print
(
"</resource-agent>"
)
def
process_input
(
avail_opt
):
avail_opt
.
extend
(
_add_dependency_options
(
avail_opt
))
# @todo: this should be put elsewhere?
os
.
putenv
(
"LANG"
,
"C"
)
os
.
putenv
(
"LC_ALL"
,
"C"
)
if
"port_as_ip"
in
avail_opt
:
avail_opt
.
append
(
"port"
)
if
len
(
sys
.
argv
)
>
1
:
opt
=
_parse_input_cmdline
(
avail_opt
)
else
:
opt
=
_parse_input_stdin
(
avail_opt
)
if
"--port-as-ip"
in
opt
and
"--plug"
in
opt
:
opt
[
"--ip"
]
=
opt
[
"--plug"
]
return
opt
##
## This function checks input and answers if we want to have same answers
## in each of the fencing agents. It looks for possible errors and run
## password script to set a correct password
######
def
check_input
(
device_opt
,
opt
,
other_conditions
=
False
):
device_opt
.
extend
(
_add_dependency_options
(
device_opt
))
options
=
dict
(
opt
)
options
[
"device_opt"
]
=
device_opt
_update_metadata
(
options
)
options
=
_set_default_values
(
options
)
options
[
"--action"
]
=
options
[
"--action"
]
.
lower
()
## In special cases (show help, metadata or version) we don't need to check anything
#####
# OCF compatibility
if
options
[
"--action"
]
==
"meta-data"
:
options
[
"--action"
]
=
"metadata"
if
options
[
"--action"
]
in
[
"metadata"
,
"manpage"
]
or
any
(
k
in
options
for
k
in
(
"--help"
,
"--version"
)):
return
options
if
"--verbose"
in
options
:
logging
.
getLogger
()
.
setLevel
(
logging
.
DEBUG
)
formatter
=
logging
.
Formatter
(
LOG_FORMAT
)
## add logging to syslog
logging
.
getLogger
()
.
addHandler
(
SyslogLibHandler
())
if
"--quiet"
not
in
options
:
## add logging to stderr
stderrHandler
=
logging
.
StreamHandler
(
sys
.
stderr
)
stderrHandler
.
setFormatter
(
formatter
)
logging
.
getLogger
()
.
addHandler
(
stderrHandler
)
(
acceptable_actions
,
_
)
=
_get_available_actions
(
device_opt
)
if
1
==
device_opt
.
count
(
"fabric_fencing"
):
acceptable_actions
.
extend
([
"enable"
,
"disable"
])
if
0
==
acceptable_actions
.
count
(
options
[
"--action"
]):
fail_usage
(
"Failed: Unrecognised action '"
+
options
[
"--action"
]
+
"'"
)
## Compatibility layer
#####
if
options
[
"--action"
]
==
"enable"
:
options
[
"--action"
]
=
"on"
if
options
[
"--action"
]
==
"disable"
:
options
[
"--action"
]
=
"off"
if
options
[
"--action"
]
==
"validate-all"
and
not
other_conditions
:
if
not
_validate_input
(
options
,
False
):
fail_usage
(
"validate-all failed"
)
sys
.
exit
(
EC_OK
)
else
:
_validate_input
(
options
,
True
)
if
"--debug-file"
in
options
:
try
:
debug_file
=
logging
.
FileHandler
(
options
[
"--debug-file"
])
debug_file
.
setLevel
(
logging
.
DEBUG
)
debug_file
.
setFormatter
(
formatter
)
logging
.
getLogger
()
.
addHandler
(
debug_file
)
except
IOError
:
logging
.
error
(
"Unable to create file
%s
"
,
options
[
"--debug-file"
])
fail_usage
(
"Failed: Unable to create file "
+
options
[
"--debug-file"
])
if
"--snmp-priv-passwd-script"
in
options
:
options
[
"--snmp-priv-passwd"
]
=
os
.
popen
(
options
[
"--snmp-priv-passwd-script"
])
.
read
()
.
rstrip
()
if
"--password-script"
in
options
:
options
[
"--password"
]
=
os
.
popen
(
options
[
"--password-script"
])
.
read
()
.
rstrip
()
return
options
## Obtain a power status from possibly more than one plug
## "on" is returned if at least one plug is ON
######
def
get_multi_power_fn
(
connection
,
options
,
get_power_fn
):
status
=
"off"
plugs
=
options
[
"--plugs"
]
if
"--plugs"
in
options
else
[
""
]
for
plug
in
plugs
:
try
:
options
[
"--uuid"
]
=
str
(
uuid
.
UUID
(
plug
))
except
ValueError
:
pass
except
KeyError
:
pass
options
[
"--plug"
]
=
plug
plug_status
=
get_power_fn
(
connection
,
options
)
if
plug_status
!=
"off"
:
status
=
plug_status
return
status
def
async_set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
retry_attempts
):
plugs
=
options
[
"--plugs"
]
if
"--plugs"
in
options
else
[
""
]
for
_
in
range
(
retry_attempts
):
for
plug
in
plugs
:
try
:
options
[
"--uuid"
]
=
str
(
uuid
.
UUID
(
plug
))
except
ValueError
:
pass
except
KeyError
:
pass
options
[
"--plug"
]
=
plug
set_power_fn
(
connection
,
options
)
time
.
sleep
(
int
(
options
[
"--power-wait"
]))
for
_
in
range
(
int
(
options
[
"--power-timeout"
])):
if
get_multi_power_fn
(
connection
,
options
,
get_power_fn
)
!=
options
[
"--action"
]:
time
.
sleep
(
1
)
else
:
return
True
return
False
def
sync_set_multi_power_fn
(
connection
,
options
,
sync_set_power_fn
,
retry_attempts
):
success
=
True
plugs
=
options
[
"--plugs"
]
if
"--plugs"
in
options
else
[
""
]
for
plug
in
plugs
:
try
:
options
[
"--uuid"
]
=
str
(
uuid
.
UUID
(
plug
))
except
ValueError
:
pass
except
KeyError
:
pass
options
[
"--plug"
]
=
plug
for
retry
in
range
(
retry_attempts
):
if
sync_set_power_fn
(
connection
,
options
):
break
if
retry
==
retry_attempts
-
1
:
success
=
False
time
.
sleep
(
int
(
options
[
"--power-wait"
]))
return
success
def
set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
sync_set_power_fn
,
retry_attempts
=
1
):
if
set_power_fn
!=
None
:
if
get_power_fn
!=
None
:
return
async_set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
retry_attempts
)
elif
sync_set_power_fn
!=
None
:
return
sync_set_multi_power_fn
(
connection
,
options
,
sync_set_power_fn
,
retry_attempts
)
return
False
def
show_docs
(
options
,
docs
=
None
):
device_opt
=
options
[
"device_opt"
]
if
docs
==
None
:
docs
=
{}
docs
[
"shortdesc"
]
=
"Fence agent"
docs
[
"longdesc"
]
=
""
if
"--help"
in
options
:
usage
(
device_opt
)
sys
.
exit
(
0
)
if
options
.
get
(
"--action"
,
""
)
in
[
"metadata"
,
"manpage"
]:
if
"port_as_ip"
in
device_opt
:
device_opt
.
remove
(
"separator"
)
metadata
(
options
,
device_opt
,
docs
)
sys
.
exit
(
0
)
if
"--version"
in
options
:
print
(
RELEASE_VERSION
)
sys
.
exit
(
0
)
def
fence_action
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
get_outlet_list
=
None
,
reboot_cycle_fn
=
None
,
sync_set_power_fn
=
None
):
result
=
0
try
:
if
"--plug"
in
options
:
options
[
"--plugs"
]
=
options
[
"--plug"
]
.
split
(
","
)
## Process options that manipulate fencing device
#####
if
(
options
[
"--action"
]
in
[
"list"
,
"list-status"
])
or
\
((
options
[
"--action"
]
==
"monitor"
)
and
1
==
options
[
"device_opt"
]
.
count
(
"port"
)
and
\
0
==
options
[
"device_opt"
]
.
count
(
"port_as_ip"
)):
if
0
==
options
[
"device_opt"
]
.
count
(
"port"
):
print
(
"N/A"
)
elif
get_outlet_list
==
None
:
## @todo: exception?
## This is just temporal solution, we will remove default value
## None as soon as all existing agent will support this operation
print
(
"NOTICE: List option is not working on this device yet"
)
else
:
options
[
"--original-action"
]
=
options
[
"--action"
]
options
[
"--action"
]
=
"list"
outlets
=
get_outlet_list
(
connection
,
options
)
options
[
"--action"
]
=
options
[
"--original-action"
]
del
options
[
"--original-action"
]
## keys can be numbers (port numbers) or strings (names of VM, UUID)
for
outlet_id
in
list
(
outlets
.
keys
()):
(
alias
,
status
)
=
outlets
[
outlet_id
]
if
status
is
None
or
(
not
status
.
upper
()
in
[
"ON"
,
"OFF"
]):
status
=
"UNKNOWN"
status
=
status
.
upper
()
if
options
[
"--action"
]
==
"list"
:
print
(
outlet_id
+
options
[
"--separator"
]
+
alias
)
elif
options
[
"--action"
]
==
"list-status"
:
print
(
outlet_id
+
options
[
"--separator"
]
+
alias
+
options
[
"--separator"
]
+
status
)
return
if
options
[
"--action"
]
==
"monitor"
and
not
"port"
in
options
[
"device_opt"
]
and
"no_status"
in
options
[
"device_opt"
]:
# Unable to do standard monitoring because 'status' action is not available
return
0
status
=
None
if
not
"no_status"
in
options
[
"device_opt"
]:
status
=
get_multi_power_fn
(
connection
,
options
,
get_power_fn
)
if
status
!=
"on"
and
status
!=
"off"
:
fail
(
EC_STATUS
)
if
options
[
"--action"
]
==
status
:
if
not
(
status
==
"on"
and
"force_on"
in
options
[
"device_opt"
]):
print
(
"Success: Already
%s
"
%
(
status
.
upper
()))
return
0
if
options
[
"--action"
]
==
"on"
:
if
set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
sync_set_power_fn
,
1
+
int
(
options
[
"--retry-on"
])):
print
(
"Success: Powered ON"
)
else
:
fail
(
EC_WAITING_ON
)
elif
options
[
"--action"
]
==
"off"
:
if
set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
sync_set_power_fn
):
print
(
"Success: Powered OFF"
)
else
:
fail
(
EC_WAITING_OFF
)
elif
options
[
"--action"
]
==
"reboot"
:
power_on
=
False
if
options
.
get
(
"--method"
,
""
)
.
lower
()
==
"cycle"
and
reboot_cycle_fn
is
not
None
:
for
_
in
range
(
1
,
1
+
int
(
options
[
"--retry-on"
])):
if
reboot_cycle_fn
(
connection
,
options
):
power_on
=
True
break
if
not
power_on
:
fail
(
EC_TIMED_OUT
)
else
:
if
status
!=
"off"
:
options
[
"--action"
]
=
"off"
if
not
set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
sync_set_power_fn
):
fail
(
EC_WAITING_OFF
)
options
[
"--action"
]
=
"on"
try
:
power_on
=
set_multi_power_fn
(
connection
,
options
,
set_power_fn
,
get_power_fn
,
sync_set_power_fn
,
int
(
options
[
"--retry-on"
]))
except
Exception
as
ex
:
# an error occured during power ON phase in reboot
# fence action was completed succesfully even in that case
logging
.
warning
(
"
%s
"
,
str
(
ex
))
# switch back to original action for the case it is used lateron
options
[
"--action"
]
=
"reboot"
if
power_on
==
False
:
# this should not fail as node was fenced succesfully
logging
.
error
(
'Timed out waiting to power ON
\n
'
)
print
(
"Success: Rebooted"
)
elif
options
[
"--action"
]
==
"status"
:
print
(
"Status: "
+
status
.
upper
())
if
status
.
upper
()
==
"OFF"
:
result
=
2
elif
options
[
"--action"
]
==
"monitor"
:
pass
except
pexpect
.
EOF
:
fail
(
EC_CONNECTION_LOST
)
except
pexpect
.
TIMEOUT
:
fail
(
EC_TIMED_OUT
)
except
pycurl
.
error
as
ex
:
logging
.
error
(
"
%s
\n
"
,
str
(
ex
))
fail
(
EC_TIMED_OUT
)
except
socket
.
timeout
as
ex
:
logging
.
error
(
"
%s
\n
"
,
str
(
ex
))
fail
(
EC_TIMED_OUT
)
return
result
def
fence_login
(
options
,
re_login_string
=
r"(login\s*: )|((?!Last )Login Name: )|(username: )|(User Name :)"
):
run_delay
(
options
)
if
"eol"
not
in
options
:
options
[
"eol"
]
=
"
\r\n
"
if
"--command-prompt"
in
options
and
type
(
options
[
"--command-prompt"
])
is
not
list
:
options
[
"--command-prompt"
]
=
[
options
[
"--command-prompt"
]]
try
:
if
"--ssl"
in
options
:
conn
=
_open_ssl_connection
(
options
)
elif
"--ssh"
in
options
and
"--identity-file"
not
in
options
:
conn
=
_login_ssh_with_password
(
options
,
re_login_string
)
elif
"--ssh"
in
options
and
"--identity-file"
in
options
:
conn
=
_login_ssh_with_identity_file
(
options
)
else
:
conn
=
_login_telnet
(
options
,
re_login_string
)
except
pexpect
.
EOF
as
exception
:
logging
.
debug
(
"
%s
"
,
str
(
exception
))
fail
(
EC_LOGIN_DENIED
)
except
pexpect
.
TIMEOUT
as
exception
:
logging
.
debug
(
"
%s
"
,
str
(
exception
))
fail
(
EC_LOGIN_DENIED
)
return
conn
def
is_executable
(
path
):
if
os
.
path
.
exists
(
path
):
stats
=
os
.
stat
(
path
)
if
stat
.
S_ISREG
(
stats
.
st_mode
)
and
os
.
access
(
path
,
os
.
X_OK
):
return
True
return
False
def
run_command
(
options
,
command
,
timeout
=
None
,
env
=
None
,
log_command
=
None
):
if
timeout
is
None
and
"--power-timeout"
in
options
:
timeout
=
options
[
"--power-timeout"
]
if
timeout
is
not
None
:
timeout
=
float
(
timeout
)
logging
.
info
(
"Executing:
%s
\n
"
,
log_command
or
command
)
try
:
process
=
subprocess
.
Popen
(
shlex
.
split
(
command
),
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
env
=
env
,
# decodes newlines and in python3 also converts bytes to str
universal_newlines
=
(
sys
.
version_info
[
0
]
>
2
))
except
OSError
:
fail_usage
(
"Unable to run
%s
\n
"
%
command
)
thread
=
threading
.
Thread
(
target
=
process
.
wait
)
thread
.
start
()
thread
.
join
(
timeout
)
if
thread
.
is_alive
():
process
.
kill
()
fail
(
EC_TIMED_OUT
)
status
=
process
.
wait
()
(
pipe_stdout
,
pipe_stderr
)
=
process
.
communicate
()
process
.
stdout
.
close
()
process
.
stderr
.
close
()
logging
.
debug
(
"
%s
%s
%s
\n
"
,
str
(
status
),
str
(
pipe_stdout
),
str
(
pipe_stderr
))
return
(
status
,
pipe_stdout
,
pipe_stderr
)
def
run_delay
(
options
,
reserve
=
0
,
result
=
0
):
## Delay is important for two-node clusters fencing
## but we do not need to delay 'status' operations
## and get us out quickly if we already know that we are gonna fail
## still wanna do something right before fencing? - reserve some time
if
options
[
"--action"
]
in
[
"off"
,
"reboot"
]
\
and
options
[
"--delay"
]
!=
"0"
\
and
result
==
0
\
and
reserve
>=
0
:
time_left
=
1
+
int
(
options
[
"--delay"
])
-
(
time
.
time
()
-
run_delay
.
time_start
)
-
reserve
if
time_left
>
0
:
logging
.
info
(
"Delay
%d
second(s) before logging in to the fence device"
,
time_left
)
time
.
sleep
(
time_left
)
# mark time when fence-agent is started
run_delay
.
time_start
=
time
.
time
()
def
fence_logout
(
conn
,
logout_string
,
sleep
=
0
):
# Logout is not required part of fencing but we should attempt to do it properly
# In some cases our 'exit' command is faster and we can not close connection as it
# was already closed by fencing device
try
:
conn
.
send_eol
(
logout_string
)
time
.
sleep
(
sleep
)
conn
.
close
()
except
OSError
:
pass
except
pexpect
.
ExceptionPexpect
:
pass
# Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is
# in format a.b.c.d...z and returned dict has key only z
def
array_to_dict
(
array
):
return
dict
([[
x
[
0
]
.
split
(
"."
)[
-
1
],
x
[
1
]]
for
x
in
array
])
## Own logger handler that uses old-style syslog handler as otherwise everything is sourced
## from /dev/syslog
class
SyslogLibHandler
(
logging
.
StreamHandler
):
"""
A handler class that correctly push messages into syslog
"""
def
emit
(
self
,
record
):
syslog_level
=
{
logging
.
CRITICAL
:
syslog
.
LOG_CRIT
,
logging
.
ERROR
:
syslog
.
LOG_ERR
,
logging
.
WARNING
:
syslog
.
LOG_WARNING
,
logging
.
INFO
:
syslog
.
LOG_INFO
,
logging
.
DEBUG
:
syslog
.
LOG_DEBUG
,
logging
.
NOTSET
:
syslog
.
LOG_DEBUG
,
}[
record
.
levelno
]
msg
=
self
.
format
(
record
)
# syslos.syslog can not have 0x00 character inside or exception is thrown
syslog
.
syslog
(
syslog_level
,
msg
.
replace
(
"
\x00
"
,
"
\n
"
))
return
def
_open_ssl_connection
(
options
):
gnutls_opts
=
""
ssl_opts
=
""
if
"--notls"
in
options
:
gnutls_opts
=
"--priority
\"
NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:-VERS-TLS1.0:+VERS-SSL3.0
\"
"
elif
"--tls1.0"
in
options
:
gnutls_opts
=
"--priority
\"
NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:+VERS-TLS1.0:%LATEST_RECORD_VERSION
\"
"
# --ssl is same as the --ssl-secure; it means we want to verify certificate in these cases
if
"--ssl-insecure"
in
options
:
ssl_opts
=
"--insecure"
command
=
'
%s
%s
%s
--crlf -p
%s
%s
'
%
\
(
options
[
"--gnutlscli-path"
],
gnutls_opts
,
ssl_opts
,
options
[
"--ipport"
],
options
[
"--ip"
])
try
:
conn
=
fspawn
(
options
,
command
)
except
pexpect
.
ExceptionPexpect
as
ex
:
logging
.
error
(
"
%s
\n
"
,
str
(
ex
))
sys
.
exit
(
EC_GENERIC_ERROR
)
return
conn
def
_login_ssh_with_identity_file
(
options
):
if
"--inet6-only"
in
options
:
force_ipvx
=
"-6 "
elif
"--inet4-only"
in
options
:
force_ipvx
=
"-4 "
else
:
force_ipvx
=
""
command
=
'
%s
%s
%s
@
%s
-i
%s
-p
%s
'
%
\
(
options
[
"--ssh-path"
],
force_ipvx
,
options
[
"--username"
],
options
[
"--ip"
],
\
options
[
"--identity-file"
],
options
[
"--ipport"
])
if
"--ssh-options"
in
options
:
command
+=
' '
+
options
[
"--ssh-options"
]
conn
=
fspawn
(
options
,
command
)
result
=
conn
.
log_expect
([
"Enter passphrase for key '"
+
options
[
"--identity-file"
]
+
"':"
,
\
"Are you sure you want to continue connecting (yes/no)?"
]
+
\
options
[
"--command-prompt"
],
int
(
options
[
"--login-timeout"
]))
if
result
==
1
:
conn
.
sendline
(
"yes"
)
result
=
conn
.
log_expect
(
[
"Enter passphrase for key '"
+
options
[
"--identity-file"
]
+
"':"
]
+
\
options
[
"--command-prompt"
],
int
(
options
[
"--login-timeout"
]))
if
result
==
0
:
if
"--password"
in
options
:
conn
.
sendline
(
options
[
"--password"
])
conn
.
log_expect
(
options
[
"--command-prompt"
],
int
(
options
[
"--login-timeout"
]))
else
:
fail_usage
(
"Failed: You have to enter passphrase (-p) for identity file"
)
return
conn
def
_login_telnet
(
options
,
re_login_string
):
re_login
=
re
.
compile
(
re_login_string
,
re
.
IGNORECASE
)
re_pass
=
re
.
compile
(
"(password)|(pass phrase)"
,
re
.
IGNORECASE
)
conn
=
fspawn
(
options
,
options
[
"--telnet-path"
])
conn
.
send
(
"set binary
\n
"
)
conn
.
send
(
"open
%s
-
%s
\n
"
%
(
options
[
"--ip"
],
options
[
"--ipport"
]))
conn
.
log_expect
(
re_login
,
int
(
options
[
"--login-timeout"
]))
conn
.
send_eol
(
options
[
"--username"
])
## automatically change end of line separator
screen
=
conn
.
read_nonblocking
(
size
=
100
,
timeout
=
int
(
options
[
"--shell-timeout"
]))
if
re_login
.
search
(
screen
)
!=
None
:
options
[
"eol"
]
=
"
\n
"
conn
.
send_eol
(
options
[
"--username"
])
conn
.
log_expect
(
re_pass
,
int
(
options
[
"--login-timeout"
]))
elif
re_pass
.
search
(
screen
)
==
None
:
conn
.
log_expect
(
re_pass
,
int
(
options
[
"--shell-timeout"
]))
try
:
conn
.
send_eol
(
options
[
"--password"
])
valid_password
=
conn
.
log_expect
([
re_login
]
+
\
options
[
"--command-prompt"
],
int
(
options
[
"--shell-timeout"
]))
if
valid_password
==
0
:
## password is invalid or we have to change EOL separator
options
[
"eol"
]
=
"
\r
"
conn
.
send_eol
(
""
)
screen
=
conn
.
read_nonblocking
(
size
=
100
,
timeout
=
int
(
options
[
"--shell-timeout"
]))
## after sending EOL the fence device can either show 'Login' or 'Password'
if
re_login
.
search
(
conn
.
after
+
screen
)
!=
None
:
conn
.
send_eol
(
""
)
conn
.
send_eol
(
options
[
"--username"
])
conn
.
log_expect
(
re_pass
,
int
(
options
[
"--login-timeout"
]))
conn
.
send_eol
(
options
[
"--password"
])
conn
.
log_expect
(
options
[
"--command-prompt"
],
int
(
options
[
"--login-timeout"
]))
except
KeyError
:
fail
(
EC_PASSWORD_MISSING
)
return
conn
def
_login_ssh_with_password
(
options
,
re_login_string
):
re_login
=
re
.
compile
(
re_login_string
,
re
.
IGNORECASE
)
re_pass
=
re
.
compile
(
"(password)|(pass phrase)"
,
re
.
IGNORECASE
)
if
"--inet6-only"
in
options
:
force_ipvx
=
"-6 "
elif
"--inet4-only"
in
options
:
force_ipvx
=
"-4 "
else
:
force_ipvx
=
""
command
=
'
%s
%s
%s
@
%s
-p
%s
-o PubkeyAuthentication=no'
%
\
(
options
[
"--ssh-path"
],
force_ipvx
,
options
[
"--username"
],
options
[
"--ip"
],
options
[
"--ipport"
])
if
"--ssh-options"
in
options
:
command
+=
' '
+
options
[
"--ssh-options"
]
conn
=
fspawn
(
options
,
command
)
if
"telnet_over_ssh"
in
options
:
# This is for stupid ssh servers (like ALOM) which behave more like telnet
# (ignore name and display login prompt)
result
=
conn
.
log_expect
(
\
[
re_login
,
"Are you sure you want to continue connecting (yes/no)?"
],
int
(
options
[
"--login-timeout"
]))
if
result
==
1
:
conn
.
sendline
(
"yes"
)
# Host identity confirm
conn
.
log_expect
(
re_login
,
int
(
options
[
"--login-timeout"
]))
conn
.
sendline
(
options
[
"--username"
])
conn
.
log_expect
(
re_pass
,
int
(
options
[
"--login-timeout"
]))
else
:
result
=
conn
.
log_expect
(
\
[
"ssword:"
,
"Are you sure you want to continue connecting (yes/no)?"
],
int
(
options
[
"--login-timeout"
]))
if
result
==
1
:
conn
.
sendline
(
"yes"
)
conn
.
log_expect
(
"ssword:"
,
int
(
options
[
"--login-timeout"
]))
conn
.
sendline
(
options
[
"--password"
])
conn
.
log_expect
(
options
[
"--command-prompt"
],
int
(
options
[
"--login-timeout"
]))
return
conn
#
# To update metadata, we change values in all_opt
def
_update_metadata
(
options
):
device_opt
=
options
[
"device_opt"
]
if
device_opt
.
count
(
"login"
)
and
device_opt
.
count
(
"no_login"
)
==
0
:
all_opt
[
"login"
][
"required"
]
=
"1"
else
:
all_opt
[
"login"
][
"required"
]
=
"0"
if
device_opt
.
count
(
"port_as_ip"
):
all_opt
[
"ipaddr"
][
"required"
]
=
"0"
all_opt
[
"port"
][
"required"
]
=
"0"
(
available_actions
,
default_value
)
=
_get_available_actions
(
device_opt
)
all_opt
[
"action"
][
"default"
]
=
default_value
actions_with_default
=
\
[
x
if
not
x
==
all_opt
[
"action"
][
"default"
]
else
x
+
" (default)"
for
x
in
available_actions
]
all_opt
[
"action"
][
"help"
]
=
\
"-o, --action=[action] Action:
%s
"
%
(
_join_wrap
(
actions_with_default
,
last_separator
=
" or "
))
if
device_opt
.
count
(
"ipport"
):
default_value
=
None
default_string
=
None
if
"default"
in
all_opt
[
"ipport"
]:
default_value
=
all_opt
[
"ipport"
][
"default"
]
elif
device_opt
.
count
(
"web"
)
and
device_opt
.
count
(
"ssl"
):
default_value
=
"80"
default_string
=
"(default 80, 443 if --ssl option is used)"
elif
device_opt
.
count
(
"telnet"
)
and
device_opt
.
count
(
"secure"
):
default_value
=
"23"
default_string
=
"(default 23, 22 if --ssh option is used)"
else
:
tcp_ports
=
{
"community"
:
"161"
,
"secure"
:
"22"
,
"telnet"
:
"23"
,
"web"
:
"80"
,
"ssl"
:
"443"
}
# all cases where next command returns multiple results are covered by previous blocks
protocol
=
[
x
for
x
in
[
"community"
,
"secure"
,
"ssl"
,
"web"
,
"telnet"
]
if
device_opt
.
count
(
x
)][
0
]
default_value
=
tcp_ports
[
protocol
]
if
default_string
is
None
:
all_opt
[
"ipport"
][
"help"
]
=
"-u, --ipport=[port] TCP/UDP port to use (default
%s
)"
%
\
(
default_value
)
else
:
all_opt
[
"ipport"
][
"help"
]
=
"-u, --ipport=[port] TCP/UDP port to use
\n
"
+
" "
*
40
+
default_string
def
_set_default_values
(
options
):
if
"ipport"
in
options
[
"device_opt"
]:
if
not
"--ipport"
in
options
:
if
"default"
in
all_opt
[
"ipport"
]:
options
[
"--ipport"
]
=
all_opt
[
"ipport"
][
"default"
]
elif
"community"
in
options
[
"device_opt"
]:
options
[
"--ipport"
]
=
"161"
elif
"--ssh"
in
options
or
all_opt
[
"secure"
]
.
get
(
"default"
,
"0"
)
==
"1"
:
options
[
"--ipport"
]
=
"22"
elif
"--ssl"
in
options
or
all_opt
[
"ssl"
]
.
get
(
"default"
,
"0"
)
==
"1"
:
options
[
"--ipport"
]
=
"443"
elif
"--ssl-secure"
in
options
or
all_opt
[
"ssl_secure"
]
.
get
(
"default"
,
"0"
)
==
"1"
:
options
[
"--ipport"
]
=
"443"
elif
"--ssl-insecure"
in
options
or
all_opt
[
"ssl_insecure"
]
.
get
(
"default"
,
"0"
)
==
"1"
:
options
[
"--ipport"
]
=
"443"
elif
"web"
in
options
[
"device_opt"
]:
options
[
"--ipport"
]
=
"80"
elif
"telnet"
in
options
[
"device_opt"
]:
options
[
"--ipport"
]
=
"23"
if
"--ipport"
in
options
:
all_opt
[
"ipport"
][
"default"
]
=
options
[
"--ipport"
]
for
opt
in
options
[
"device_opt"
]:
if
"default"
in
all_opt
[
opt
]
and
not
opt
==
"ipport"
:
getopt_long
=
"--"
+
all_opt
[
opt
][
"longopt"
]
if
getopt_long
not
in
options
:
options
[
getopt_long
]
=
all_opt
[
opt
][
"default"
]
return
options
# stop = True/False : exit fence agent when problem is encountered
def
_validate_input
(
options
,
stop
=
True
):
device_opt
=
options
[
"device_opt"
]
valid_input
=
True
if
"--username"
not
in
options
and
\
device_opt
.
count
(
"login"
)
and
(
device_opt
.
count
(
"no_login"
)
==
0
):
valid_input
=
False
fail_usage
(
"Failed: You have to set login name"
,
stop
)
if
device_opt
.
count
(
"ipaddr"
)
and
"--ip"
not
in
options
and
"--managed"
not
in
options
and
"--target"
not
in
options
:
valid_input
=
False
fail_usage
(
"Failed: You have to enter fence address"
,
stop
)
if
device_opt
.
count
(
"no_password"
)
==
0
:
if
0
==
device_opt
.
count
(
"identity_file"
):
if
not
(
"--password"
in
options
or
"--password-script"
in
options
):
valid_input
=
False
fail_usage
(
"Failed: You have to enter password or password script"
,
stop
)
else
:
if
not
(
"--password"
in
options
or
\
"--password-script"
in
options
or
"--identity-file"
in
options
):
valid_input
=
False
fail_usage
(
"Failed: You have to enter password, password script or identity file"
,
stop
)
if
"--ssh"
not
in
options
and
"--identity-file"
in
options
:
valid_input
=
False
fail_usage
(
"Failed: You have to use identity file together with ssh connection (-x)"
,
stop
)
if
"--identity-file"
in
options
and
not
os
.
path
.
isfile
(
options
[
"--identity-file"
]):
valid_input
=
False
fail_usage
(
"Failed: Identity file "
+
options
[
"--identity-file"
]
+
" does not exist"
,
stop
)
if
(
0
==
[
"list"
,
"list-status"
,
"monitor"
]
.
count
(
options
[
"--action"
]))
and
\
"--plug"
not
in
options
and
device_opt
.
count
(
"port"
)
and
\
device_opt
.
count
(
"no_port"
)
==
0
and
not
device_opt
.
count
(
"port_as_ip"
):
valid_input
=
False
fail_usage
(
"Failed: You have to enter plug number or machine identification"
,
stop
)
if
"--plug"
in
options
and
len
(
options
[
"--plug"
]
.
split
(
","
))
>
1
and
\
"--method"
in
options
and
options
[
"--method"
]
==
"cycle"
:
valid_input
=
False
fail_usage
(
"Failed: Cannot use --method cycle for more than 1 plug"
,
stop
)
for
failed_opt
in
_get_opts_with_invalid_choices
(
options
):
valid_input
=
False
fail_usage
(
"Failed: You have to enter a valid choice for
%s
from the valid values:
%s
"
%
\
(
"--"
+
all_opt
[
failed_opt
][
"longopt"
],
str
(
all_opt
[
failed_opt
][
"choices"
])),
stop
)
for
failed_opt
in
_get_opts_with_invalid_types
(
options
):
valid_input
=
False
if
all_opt
[
failed_opt
][
"type"
]
==
"second"
:
fail_usage
(
"Failed: The value you have entered for
%s
is not a valid time in seconds"
%
\
(
"--"
+
all_opt
[
failed_opt
][
"longopt"
]),
stop
)
else
:
fail_usage
(
"Failed: The value you have entered for
%s
is not a valid
%s
"
%
\
(
"--"
+
all_opt
[
failed_opt
][
"longopt"
],
all_opt
[
failed_opt
][
"type"
]),
stop
)
return
valid_input
def
_encode_html_entities
(
text
):
return
text
.
replace
(
"&"
,
"&"
)
.
replace
(
'"'
,
"""
)
.
replace
(
'<'
,
"<"
)
.
\
replace
(
'>'
,
">"
)
.
replace
(
"'"
,
"'"
)
def
_prepare_getopt_args
(
options
):
getopt_string
=
""
longopt_list
=
[]
for
k
in
options
:
if
k
in
all_opt
and
all_opt
[
k
][
"getopt"
]
!=
":"
:
# getopt == ":" means that opt is without short getopt, but has value
getopt_string
+=
all_opt
[
k
][
"getopt"
]
elif
k
not
in
all_opt
:
fail_usage
(
"Parse error: unknown option '"
+
k
+
"'"
)
if
k
in
all_opt
and
"longopt"
in
all_opt
[
k
]:
if
all_opt
[
k
][
"getopt"
]
.
endswith
(
":"
):
longopt_list
.
append
(
all_opt
[
k
][
"longopt"
]
+
"="
)
else
:
longopt_list
.
append
(
all_opt
[
k
][
"longopt"
])
return
(
getopt_string
,
longopt_list
)
def
_parse_input_stdin
(
avail_opt
):
opt
=
{}
name
=
""
mapping_longopt_names
=
dict
([(
all_opt
[
o
]
.
get
(
"longopt"
),
o
)
for
o
in
avail_opt
])
for
line
in
sys
.
stdin
.
readlines
():
line
=
line
.
strip
()
if
(
line
.
startswith
(
"#"
))
or
(
len
(
line
)
==
0
):
continue
(
name
,
value
)
=
(
line
+
"="
)
.
split
(
"="
,
1
)
value
=
value
[:
-
1
]
if
name
.
replace
(
"-"
,
"_"
)
in
mapping_longopt_names
:
name
=
mapping_longopt_names
[
name
.
replace
(
"-"
,
"_"
)]
elif
name
.
replace
(
"_"
,
"-"
)
in
mapping_longopt_names
:
name
=
mapping_longopt_names
[
name
.
replace
(
"_"
,
"-"
)]
if
avail_opt
.
count
(
name
)
==
0
and
name
in
[
"nodename"
]:
continue
elif
avail_opt
.
count
(
name
)
==
0
:
logging
.
warning
(
"Parse error: Ignoring unknown option '
%s
'
\n
"
,
line
)
continue
if
all_opt
[
name
][
"getopt"
]
.
endswith
(
":"
):
opt
[
"--"
+
all_opt
[
name
][
"longopt"
]
.
rstrip
(
":"
)]
=
value
elif
value
.
lower
()
in
[
"1"
,
"yes"
,
"on"
,
"true"
]:
opt
[
"--"
+
all_opt
[
name
][
"longopt"
]]
=
"1"
else
:
logging
.
warning
(
"Parse error: Ignoring option '
%s
' because it does not have value
\n
"
,
name
)
return
opt
def
_parse_input_cmdline
(
avail_opt
):
filtered_opts
=
{}
_verify_unique_getopt
(
avail_opt
)
(
getopt_string
,
longopt_list
)
=
_prepare_getopt_args
(
avail_opt
)
try
:
(
entered_opt
,
left_arg
)
=
getopt
.
gnu_getopt
(
sys
.
argv
[
1
:],
getopt_string
,
longopt_list
)
if
len
(
left_arg
)
>
0
:
logging
.
warning
(
"Unused arguments on command line:
%s
"
%
(
str
(
left_arg
)))
except
getopt
.
GetoptError
as
error
:
fail_usage
(
"Parse error: "
+
error
.
msg
)
for
opt
in
avail_opt
:
filtered_opts
.
update
({
opt
:
all_opt
[
opt
]})
# Short and long getopt names are changed to consistent "--" + long name (e.g. --username)
long_opts
=
{}
for
arg_name
in
list
(
dict
(
entered_opt
)
.
keys
()):
all_key
=
[
key
for
(
key
,
value
)
in
list
(
filtered_opts
.
items
())
\
if
"--"
+
value
.
get
(
"longopt"
,
""
)
==
arg_name
or
"-"
+
value
.
get
(
"getopt"
,
""
)
.
rstrip
(
":"
)
==
arg_name
][
0
]
long_opts
[
"--"
+
filtered_opts
[
all_key
][
"longopt"
]]
=
dict
(
entered_opt
)[
arg_name
]
# This test is specific because it does not apply to input on stdin
if
"port_as_ip"
in
avail_opt
and
not
"--port-as-ip"
in
long_opts
and
"--plug"
in
long_opts
:
fail_usage
(
"Parser error: option -n/--plug is not recognized"
)
return
long_opts
# for ["John", "Mary", "Eli"] returns "John, Mary and Eli"
def
_join2
(
words
,
normal_separator
=
", "
,
last_separator
=
" and "
):
if
len
(
words
)
<=
1
:
return
""
.
join
(
words
)
else
:
return
last_separator
.
join
([
normal_separator
.
join
(
words
[:
-
1
]),
words
[
-
1
]])
def
_join_wrap
(
words
,
normal_separator
=
", "
,
last_separator
=
" and "
,
first_indent
=
42
):
x
=
_join2
(
words
,
normal_separator
,
last_separator
)
wrapper
=
textwrap
.
TextWrapper
()
wrapper
.
initial_indent
=
" "
*
first_indent
wrapper
.
subsequent_indent
=
" "
*
40
wrapper
.
width
=
85
wrapper
.
break_on_hyphens
=
False
wrapper
.
break_long_words
=
False
wrapped_text
=
""
for
line
in
wrapper
.
wrap
(
x
):
wrapped_text
+=
line
+
"
\n
"
return
wrapped_text
.
lstrip
()
.
rstrip
(
"
\n
"
)
def
_get_opts_with_invalid_choices
(
options
):
options_failed
=
[]
device_opt
=
options
[
"device_opt"
]
for
opt
in
device_opt
:
if
"choices"
in
all_opt
[
opt
]:
longopt
=
"--"
+
all_opt
[
opt
][
"longopt"
]
possible_values_upper
=
[
y
.
upper
()
for
y
in
all_opt
[
opt
][
"choices"
]]
if
longopt
in
options
:
options
[
longopt
]
=
options
[
longopt
]
.
upper
()
if
not
options
[
"--"
+
all_opt
[
opt
][
"longopt"
]]
in
possible_values_upper
:
options_failed
.
append
(
opt
)
return
options_failed
def
_get_opts_with_invalid_types
(
options
):
options_failed
=
[]
device_opt
=
options
[
"device_opt"
]
for
opt
in
device_opt
:
if
"type"
in
all_opt
[
opt
]:
longopt
=
"--"
+
all_opt
[
opt
][
"longopt"
]
if
longopt
in
options
:
if
all_opt
[
opt
][
"type"
]
in
[
"integer"
,
"second"
]:
try
:
number
=
int
(
options
[
"--"
+
all_opt
[
opt
][
"longopt"
]])
except
ValueError
:
options_failed
.
append
(
opt
)
return
options_failed
def
_verify_unique_getopt
(
avail_opt
):
used_getopt
=
set
()
for
opt
in
avail_opt
:
getopt_value
=
all_opt
[
opt
]
.
get
(
"getopt"
,
""
)
.
rstrip
(
":"
)
if
getopt_value
and
getopt_value
in
used_getopt
:
fail_usage
(
"Short getopt for
%s
(-
%s
) is not unique"
%
(
opt
,
getopt_value
))
else
:
used_getopt
.
add
(
getopt_value
)
def
_get_available_actions
(
device_opt
):
available_actions
=
[
"on"
,
"off"
,
"reboot"
,
"status"
,
"list"
,
"list-status"
,
\
"monitor"
,
"metadata"
,
"manpage"
,
"validate-all"
]
default_value
=
"reboot"
if
device_opt
.
count
(
"fabric_fencing"
):
available_actions
.
remove
(
"reboot"
)
default_value
=
"off"
if
device_opt
.
count
(
"no_status"
):
available_actions
.
remove
(
"status"
)
if
device_opt
.
count
(
"no_on"
):
available_actions
.
remove
(
"on"
)
if
device_opt
.
count
(
"no_off"
):
available_actions
.
remove
(
"off"
)
if
not
device_opt
.
count
(
"separator"
):
available_actions
.
remove
(
"list"
)
available_actions
.
remove
(
"list-status"
)
if
device_opt
.
count
(
"diag"
):
available_actions
.
append
(
"diag"
)
return
(
available_actions
,
default_value
)
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Dec 23, 10:49 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1125237
Default Alt Text
fencing.py.py (48 KB)
Attached To
Mode
rF Fence Agents
Attached
Detach File
Event Timeline
Log In to Comment