Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3153026
fence_pve.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
fence_pve.py
View Options
#!@PYTHON@ -tt
# This agent uses Proxmox VE API
# Thanks to Frank Brendel (author of original perl fence_pve)
# for help with writing and testing this agent.
import
sys
import
json
import
pycurl
import
io
import
atexit
import
logging
sys
.
path
.
append
(
"@FENCEAGENTSLIBDIR@"
)
from
fencing
import
fail
,
fail_usage
,
EC_LOGIN_DENIED
,
atexit_handler
,
all_opt
,
check_input
,
process_input
,
show_docs
,
fence_action
,
run_delay
if
sys
.
version_info
[
0
]
>
2
:
import
urllib.parse
as
urllib
else
:
import
urllib
def
get_power_status
(
conn
,
options
):
del
conn
state
=
{
"running"
:
"on"
,
"stopped"
:
"off"
}
if
options
[
"--pve-node"
]
is
None
:
nodes
=
send_cmd
(
options
,
"nodes"
)
if
type
(
nodes
)
is
not
dict
or
"data"
not
in
nodes
or
type
(
nodes
[
"data"
])
is
not
list
:
return
None
for
node
in
nodes
[
"data"
]:
# lookup the node holding the vm
if
type
(
node
)
is
not
dict
or
"node"
not
in
node
:
return
None
options
[
"--pve-node"
]
=
node
[
"node"
]
status
=
get_power_status
(
None
,
options
)
if
status
is
not
None
:
logging
.
info
(
"vm found on node: "
+
options
[
"--pve-node"
])
break
else
:
options
[
"--pve-node"
]
=
None
return
status
else
:
cmd
=
"nodes/"
+
options
[
"--pve-node"
]
+
"/"
+
options
[
"--vmtype"
]
+
"/"
+
options
[
"--plug"
]
+
"/status/current"
result
=
send_cmd
(
options
,
cmd
)
if
type
(
result
)
is
dict
and
"data"
in
result
:
if
type
(
result
[
"data"
])
is
dict
and
"status"
in
result
[
"data"
]:
if
result
[
"data"
][
"status"
]
in
state
:
return
state
[
result
[
"data"
][
"status"
]]
return
None
def
set_power_status
(
conn
,
options
):
del
conn
action
=
{
'on'
:
"start"
,
'off'
:
"stop"
}[
options
[
"--action"
]]
cmd
=
"nodes/"
+
options
[
"--pve-node"
]
+
"/"
+
options
[
"--vmtype"
]
+
"/"
+
options
[
"--plug"
]
+
"/status/"
+
action
send_cmd
(
options
,
cmd
,
post
=
{
"skiplock"
:
1
})
def
reboot_cycle
(
conn
,
options
):
del
conn
cmd
=
"nodes/"
+
options
[
"--pve-node"
]
+
"/"
+
options
[
"--vmtype"
]
+
"/"
+
options
[
"--plug"
]
+
"/status/reset"
result
=
send_cmd
(
options
,
cmd
,
post
=
{
"skiplock"
:
1
})
return
type
(
result
)
is
dict
and
"data"
in
result
def
get_outlet_list
(
conn
,
options
):
del
conn
nodes
=
send_cmd
(
options
,
"nodes"
)
outlets
=
dict
()
if
type
(
nodes
)
is
not
dict
or
"data"
not
in
nodes
or
type
(
nodes
[
"data"
])
is
not
list
:
return
None
for
node
in
nodes
[
"data"
]:
if
type
(
node
)
is
not
dict
or
"node"
not
in
node
:
return
None
vms
=
send_cmd
(
options
,
"nodes/"
+
node
[
"node"
]
+
"/"
+
options
[
"--vmtype"
])
if
type
(
vms
)
is
not
dict
or
"data"
not
in
vms
or
type
(
vms
[
"data"
])
is
not
list
:
return
None
for
vm
in
vms
[
"data"
]:
outlets
[
vm
[
"vmid"
]]
=
[
vm
[
"name"
],
vm
[
"status"
]]
return
outlets
def
get_ticket
(
options
):
post
=
{
'username'
:
options
[
"--username"
],
'password'
:
options
[
"--password"
]}
result
=
send_cmd
(
options
,
"access/ticket"
,
post
=
post
)
if
type
(
result
)
is
dict
and
"data"
in
result
:
if
type
(
result
[
"data"
])
is
dict
and
"ticket"
in
result
[
"data"
]
and
"CSRFPreventionToken"
in
result
[
"data"
]:
return
{
"ticket"
:
str
(
"PVEAuthCookie="
+
result
[
"data"
][
"ticket"
]
+
"; "
+
\
"version=0; path=/; domain="
+
options
[
"--ip"
]
+
\
"; port="
+
str
(
options
[
"--ipport"
])
+
"; path_spec=0; secure=1; "
+
\
"expires=7200; discard=0"
),
"CSRF_token"
:
str
(
"CSRFPreventionToken: "
+
result
[
"data"
][
"CSRFPreventionToken"
])
}
return
None
def
send_cmd
(
options
,
cmd
,
post
=
None
):
url
=
options
[
"url"
]
+
cmd
conn
=
pycurl
.
Curl
()
output_buffer
=
io
.
BytesIO
()
if
logging
.
getLogger
()
.
getEffectiveLevel
()
<
logging
.
WARNING
:
conn
.
setopt
(
pycurl
.
VERBOSE
,
True
)
conn
.
setopt
(
pycurl
.
HTTPGET
,
1
)
conn
.
setopt
(
pycurl
.
URL
,
url
.
encode
(
"ascii"
))
if
"auth"
in
options
and
options
[
"auth"
]
is
not
None
:
conn
.
setopt
(
pycurl
.
COOKIE
,
options
[
"auth"
][
"ticket"
])
conn
.
setopt
(
pycurl
.
HTTPHEADER
,
[
options
[
"auth"
][
"CSRF_token"
]])
if
post
is
not
None
:
if
"skiplock"
in
post
:
conn
.
setopt
(
conn
.
CUSTOMREQUEST
,
'POST'
)
else
:
conn
.
setopt
(
pycurl
.
POSTFIELDS
,
urllib
.
urlencode
(
post
))
conn
.
setopt
(
pycurl
.
WRITEFUNCTION
,
output_buffer
.
write
)
conn
.
setopt
(
pycurl
.
TIMEOUT
,
int
(
options
[
"--shell-timeout"
]))
if
"--ssl-insecure"
in
options
:
conn
.
setopt
(
pycurl
.
SSL_VERIFYPEER
,
0
)
conn
.
setopt
(
pycurl
.
SSL_VERIFYHOST
,
0
)
else
:
conn
.
setopt
(
pycurl
.
SSL_VERIFYPEER
,
1
)
conn
.
setopt
(
pycurl
.
SSL_VERIFYHOST
,
2
)
logging
.
debug
(
"URL: "
+
url
)
try
:
conn
.
perform
()
result
=
output_buffer
.
getvalue
()
.
decode
()
logging
.
debug
(
"RESULT ["
+
str
(
conn
.
getinfo
(
pycurl
.
RESPONSE_CODE
))
+
\
"]: "
+
result
)
conn
.
close
()
return
json
.
loads
(
result
)
except
pycurl
.
error
:
logging
.
error
(
"Connection failed"
)
except
:
logging
.
error
(
"Cannot parse json"
)
return
None
def
main
():
atexit
.
register
(
atexit_handler
)
all_opt
[
"pve_node_auto"
]
=
{
"getopt"
:
"A"
,
"longopt"
:
"pve-node-auto"
,
"help"
:
"-A, --pve-node-auto "
"Automatically select proxmox node"
,
"required"
:
"0"
,
"shortdesc"
:
"Automatically select proxmox node. "
"(This option overrides --pve-node)"
,
"type"
:
"boolean"
,
"order"
:
2
}
all_opt
[
"pve_node"
]
=
{
"getopt"
:
"N:"
,
"longopt"
:
"pve-node"
,
"help"
:
"-N, --pve-node=[node_name] "
"Proxmox node name on which machine is located"
,
"required"
:
"0"
,
"shortdesc"
:
"Proxmox node name on which machine is located. "
"(Must be specified if not using --pve-node-auto)"
,
"order"
:
2
}
all_opt
[
"node_name"
]
=
{
"getopt"
:
":"
,
"longopt"
:
"nodename"
,
"help"
:
"--nodename "
"Replaced by --pve-node"
,
"required"
:
"0"
,
"shortdesc"
:
"Replaced by --pve-node"
,
"order"
:
3
}
all_opt
[
"vmtype"
]
=
{
"getopt"
:
":"
,
"longopt"
:
"vmtype"
,
"default"
:
"qemu"
,
"help"
:
"--vmtype "
"Virtual machine type lxc or qemu (default: qemu)"
,
"required"
:
"1"
,
"shortdesc"
:
"Virtual machine type lxc or qemu. "
"(Default: qemu)"
,
"order"
:
2
}
device_opt
=
[
"ipaddr"
,
"login"
,
"passwd"
,
"ssl"
,
"web"
,
"port"
,
"pve_node"
,
"pve_node_auto"
,
"node_name"
,
"vmtype"
,
"method"
]
all_opt
[
"login"
][
"required"
]
=
"0"
all_opt
[
"login"
][
"default"
]
=
"root@pam"
all_opt
[
"ipport"
][
"default"
]
=
"8006"
all_opt
[
"ssl"
][
"default"
]
=
"1"
all_opt
[
"port"
][
"shortdesc"
]
=
"Id of the virtual machine."
all_opt
[
"ipaddr"
][
"shortdesc"
]
=
"IP Address or Hostname of a node "
+
\
"within the Proxmox cluster."
options
=
check_input
(
device_opt
,
process_input
(
device_opt
))
docs
=
{}
docs
[
"shortdesc"
]
=
"Fencing agent for the Proxmox Virtual Environment"
docs
[
"longdesc"
]
=
"The fence_pve agent can be used to fence virtual
\
machines acting as nodes in a virtualized cluster."
docs
[
"vendorurl"
]
=
"http://www.proxmox.com/"
show_docs
(
options
,
docs
)
run_delay
(
options
)
if
"--pve-node-auto"
in
options
:
# Force pve-node to None to allow autodiscovery
options
[
"--pve-node"
]
=
None
elif
"--pve-node"
in
options
and
options
[
"--pve-node"
]:
# Leave pve-node alone
pass
elif
"--nodename"
in
options
and
options
[
"--nodename"
]:
# map nodename into pve-node to support legacy implementations
options
[
"--pve-node"
]
=
options
[
"--nodename"
]
else
:
fail_usage
(
"At least one of pve-node-auto or pve-node must be supplied"
)
if
options
[
"--vmtype"
]
!=
"qemu"
:
# For vmtypes other than qemu, only the onoff method is valid
options
[
"--method"
]
=
"onoff"
options
[
"url"
]
=
"https://"
+
options
[
"--ip"
]
+
":"
+
str
(
options
[
"--ipport"
])
+
"/api2/json/"
options
[
"auth"
]
=
get_ticket
(
options
)
if
options
[
"auth"
]
is
None
:
fail
(
EC_LOGIN_DENIED
)
# Workaround for unsupported API call on some Proxmox hosts
outlets
=
get_outlet_list
(
None
,
options
)
# Unsupported API-Call will result in value: None
if
outlets
is
None
:
result
=
fence_action
(
None
,
options
,
set_power_status
,
get_power_status
,
None
,
reboot_cycle
)
sys
.
exit
(
result
)
result
=
fence_action
(
None
,
options
,
set_power_status
,
get_power_status
,
get_outlet_list
,
reboot_cycle
)
sys
.
exit
(
result
)
if
__name__
==
"__main__"
:
main
()
File Metadata
Details
Attached
Mime Type
text/x-script.python
Expires
Tue, Feb 25, 7:59 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464749
Default Alt Text
fence_pve.py (7 KB)
Attached To
Mode
rF Fence Agents
Attached
Detach File
Event Timeline
Log In to Comment