#!/usr/bin/python3.13 -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("/usr/share/fence")
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 is a Power Fencing agent that \
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()

