summaryrefslogtreecommitdiffstats
path: root/agents/rcd_serial/fence_rcd_serial.py
blob: 2614772ff754c41f776798580231bc309372ed02 (plain)
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
#!@PYTHON@ -tt

# Copyright 2018 Infoxchange, Danielle Madeley, Sam McLeod-Jones

# Controls an RCD serial device
# Ported from stonith/rcd_serial.c

# The Following Agent Has Been Tested On:
# CentOS Linux release 7.5.1804

# Resource example:
# primitive stonith_node_1 ocf:rcd_serial_py params port="/dev/ttyS0" time=1000 hostlist=stonith_node_1 stonith-timeout=5s

import sys
import atexit
import os
import struct
import logging
import time
from fcntl import ioctl
from termios import TIOCMBIC, TIOCMBIS, TIOCM_RTS, TIOCM_DTR
from time import sleep

sys.path.append("@FENCEAGENTSLIBDIR@")
from fencing import *

class RCDSerial(object):
	"""Control class for serial device"""

	def __init__(self, port='/dev/ttyS0'):
		self.fd = fd = os.open(port, os.O_RDONLY | os.O_NDELAY)
		logging.debug("Opened %s on fd %i", port, fd)
		ioctl(fd, TIOCMBIC, struct.pack('I', TIOCM_RTS | TIOCM_DTR))

	def close(self):
		"""Close the serial device"""
		logging.debug("Closing serial device")
		ret = os.close(self.fd)

		return ret

	def toggle_pin(self, pin=TIOCM_DTR, time=1000):
		"""Toggle the pin high for the time specified"""

		logging.debug("Set pin high")
		ioctl(self.fd, TIOCMBIS, struct.pack('I', pin))

		sleep(float(time) / 1000.)

		logging.debug("Set pin low")
		ioctl(self.fd, TIOCMBIC, struct.pack('I', pin))

def reboot_device(conn, options):
	conn.toggle_pin(time=options["--power-wait"])
	return True

def main():
	device_opt = ["serial_port", "no_status", "no_password", "no_login", "method", "no_on", "no_off"]

	atexit.register(atexit_handler)

	all_opt["serial_port"] = {
		"getopt" : ":",
		"longopt" : "serial-port",
		"help":"--serial-port=[port]           Port of the serial device (e.g. /dev/ttyS0)",
		"required" : "1",
		"shortdesc" : "Port of the serial device",
		"default" : "/dev/ttyS0",
		"order": 1
	}

	all_opt["method"]["default"] = "cycle"
	all_opt["power_wait"]["default"] = "2"
	all_opt["method"]["help"] = "-m, --method=[method]          Method to fence (onoff|cycle) (Default: cycle)"

	options = check_input(device_opt, process_input(device_opt))

	docs = {}
	docs["shortdesc"] = "rcd_serial fence agent"
	docs["longdesc"] = "fence_rcd_serial operates a serial cable that toggles a \
reset of an opposing server using the reset switch on its motherboard. The \
cable itself is simple with no power, network or moving parts. An example of \
the cable is available here: https://smcleod.net/rcd-stonith/ and the circuit \
design is available in the fence-agents src as SVG"
	docs["vendorurl"] = "https://github.com/sammcj/fence_rcd_serial"
	show_docs(options, docs)

	if options["--action"] in ["off", "reboot"]:
		time.sleep(int(options["--delay"]))

	## Operate the fencing device
	conn = RCDSerial(port=options["--serial-port"])
	result = fence_action(conn, options, None, None, reboot_cycle_fn=reboot_device)
	conn.close()

	sys.exit(result)

if __name__ == "__main__":
	main()