-#!/usr/bin/python3
+#!/bin/bash
-"""
-libremanage - Lightweight, free software for remote side-chanel server management
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+set -eE -o pipefail
-Copyright (C) 2018 Alyssa Rosenzweig
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program 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 Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-"""
-
-USAGE = """
-Usage:
-
- $ libremanage [server name] [command]
-
-Example:
-
- $ libremanage web2 reboot
-
-Server names are defined in the accompanying config.py.
-
-Valid commands are as follows:
-
- - shutdown, reboot, poweron: Power management
- - tty: Open TTY in GNU Screen
- - sanity, sanity-sh: SSH sanity tests, which will whoami or just run sh.
-
-Define a configuration file in ~/.libremanage.json. See the included
-config.json for an example. Servers correspond to managed servers; managers
-correspond to single-board computers connecting the servers. libremanage SSHs
-into the manager to access the server through the side-channel.
-"""
-
-import sys
-import json
-import functools
-import subprocess
-import time
-import os.path
-
-def open_ssh(server, command, force_tty=False):
- print(command)
- config = server["ssh"]
- args = ["ssh"] + (["-t"] if force_tty else []) + [config["username"] + "@" + config["host"], "-p", str(config["port"]), command]
- subprocess.run(args)
-
-def die_with_usage(message):
- print(message)
- print(USAGE)
- sys.exit(1)
-
-def get_server_handle(name):
- if name == "localhost":
- return
-
- try:
- server = CONFIG["servers"][name]
- except KeyError:
- die_with_usage("Unknown server, please configure")
-
- # Associate manager configuration
- server["ssh"] = CONFIG["managers"][server["manager"]]
-
- # Meta access
- server["name"] = name
-
- return server
-
-def power_write(server, conf, state):
- if conf["type"] == "hidusb-relay-cmd":
- verb = "on" if state == 1 else "off"
- open_ssh(server, "hidusb-relay-cmd ID=" + conf["relay"] + " " + verb + " " + str(conf["channel"]))
- else:
- print("Unknown power type " + conf["type"])
-
-def power_button(server, conf, state):
- # Hold down the power to force off (via the EC),
- # or just flick on to turn on
-
- power_write(server, conf, 1)
- time.sleep(6 if state == POWER_OFF else 1)
- power_write(server, conf, 0)
+# verbose command
+v() {
+ printf "$0 running: %s\n" "$*"
+ "$@"
+}
-POWER_OFF = 0
-POWER_ON = 1
-POWER_REBOOT = 2
-def set_server_power(state, server):
- conf = server["power"]
+usage() {
+ cat <<EOF
+Usage: libremanage poweroff|poweron|reboot HOSTNAME|CHANNEL [BOARD_ID]
- # Act like a power button
+BOARD_ID is not needed if only 1 relay device is connected. To
+understand CHANNEL and BOARD_ID, run hidusb-relay-cmd and
+hidusb-relay-cmd state.
- if state == POWER_OFF or state == POWER_ON:
- power_button(server, conf, state)
- elif state == POWER_REBOOT:
- # Requires that we already be online.
- power_button(server, conf, POWER_OFF)
- power_button(server, conf, POWER_ON)
+Using a configuration (/etc/libremanage.conf) allows you to use HOSTNAME
+instead of CHANNEL and BOARD_ID.
-def open_tty(s):
- if s["tty"]["uncolor"]:
- # Broken serial port, workaround TTY garbage with libremanage-serial
- subprocess.run(["libremanage-serial", s["name"]])
- else:
- # Use native GNU screen
- return open_ssh(s, "screen " + s["tty"]["file"] + " " + str(s["tty"]["baud"]), force_tty=True),
+Note, the relay's channels default state when plugged in are off.
-COMMANDS = {
- # Power managemment
+WARNING: If you lose access to the machine controlling a relay and a
+relay channel is stuck on, you will need to manually replug usb power to
+the relay so the channel goes back to its default off state before you
+can turn on the server it is connected to, because its as if the servers
+power button is stuck being held down. That is why this script runs
+locally and ignores signals when its running.
- "shutdown": functools.partial(set_server_power, POWER_OFF),
- "poweron": functools.partial(set_server_power, POWER_ON),
- "reboot": functools.partial(set_server_power, POWER_REBOOT),
+Example config:
- # TTY access (or keyboard if wired as such)
+# This config is sourced from bash, so make sure its valid bash.
+# board_id is not needed if only 1 relay device is connected.
+cephserver3_channel=1
+cephserver3_board_id=HURTM
+cephserver2_channel=2
+cephserver2_board_id=HURTM
- "tty": open_tty,
- "tty-baud": lambda s: open_ssh(s, "stty -F "+ s["tty"]["file"] + " cs7 cstopb -ixon raw speed " + str(s["tty"]["baud"])),
- "tty-read": lambda s: open_ssh(s, "cat " + s["tty"]["file"], force_tty=True),
- "tty-write": lambda s: open_ssh(s, "stdbuf -o0 cat > " + s["tty"]["file"], force_tty=True),
-
- # SSH sanity tests
-
- "sanity": lambda s: open_ssh(s, "whoami"),
- "sanity-sh": lambda s: open_ssh(s, ""),
+EOF
+ if [[ -e /etc/libremanage.conf ]]; then
+ echo "Current config:"
+ v cat /etc/libremanage.conf
+ else
+ echo "Note: /etc/libremanage.conf does not exist on the current machine"
+ fi
+ exit 0
}
-def issue_command(server_name, command):
- server = get_server_handle(server_name)
-
- try:
- callback = COMMANDS[command]
- except KeyError:
- die_with_usage("Invalid command supplied")
-
- callback(server)
-
-# Load configuration, get command, and go!
-
-try:
- with open(os.path.expanduser("~/.libremanage.json")) as f:
- CONFIG = json.load(f)
-except FileNotFoundError:
- die_with_usage("Configuration file missing in ~/.libremanage.json")
-
-if len(sys.argv) != 3:
- die_with_usage("Incorrect number of arguments")
-issue_command(sys.argv[1], sys.argv[2])
+if (( $# < 2 )); then
+ usage
+fi
+
+read action chan board_id <<<"$@"
+
+if [[ -e /etc/libremanage.conf ]]; then
+ source /etc/libremanage.conf
+fi
+
+# we know HOSTNAME is given if it doesn't start with a number.
+if [[ $chan != [0-9]* ]]; then
+ if [[ ! $board_id ]]; then
+ board_id_var=${chan}_board_id
+ board_id=${!board_id_var}
+ fi
+ chan_var=${chan}_channel
+ chan=${!chan_var}
+
+ if [[ ! $chan ]]; then
+ echo "error: channel not found in /etc/libremanage.conf" >&2
+ exit 1
+ fi
+fi
+
+
+if hidusb-relay-cmd state | grep ON; then
+ printf "%s\n" "WARNING: output from hidusb-relay-cmd state shows an ON relay. this could mean another command instance is running, or it got stuck on due to an uncompleted command." >&2
+ # TODO: exit in this case and print out the command to turn it off.
+fi
+
+
+case $(hidusb-relay-cmd state|wc -l) in
+ 0)
+ echo "error: got 0 lines from running hidusb-relay-cmd state" >&2
+ exit 1
+ ;;
+ 1) : ;;
+ *)
+ if [[ ! $board_id ]]; then
+ echo "error: more than 1 relay device, so passing its id is required" >&2
+ exit 1
+ fi
+ ;;
+esac
+
+
+if [[ $board_id ]]; then
+ # leave this as an empty var if its not passed
+ board_id_arg="id=$board_id"
+fi
+
+# ignore errors and continue on if a command fails from here
+set +eE +o pipefail
+# ignore hup so we complete even if there is a connection problem, and
+# force anyone to kill -9 so we might complete in more cases, for
+# example if a reboot is happening
+trap '' HUP INT QUIT TERM
+echo "$0: doing $action. shell commands will be printed to the terminal."
+case $action in
+ poweroff)
+ v hidusb-relay-cmd $board_id_arg on $chan
+ v sleep 6
+ v hidusb-relay-cmd $board_id_arg off $chan
+ ;;
+ poweron)
+ v hidusb-relay-cmd $board_id_arg on $chan
+ v sleep 1
+ v hidusb-relay-cmd $board_id_arg off $chan
+ ;;
+ reboot)
+ v hidusb-relay-cmd $board_id_arg on $chan
+ v sleep 6
+ v hidusb-relay-cmd $board_id_arg off $chan
+ v sleep 1
+ v hidusb-relay-cmd $board_id_arg on $chan
+ v sleep 1
+ v hidusb-relay-cmd $board_id_arg off $chan
+ ;;
+ *)
+ echo "error: action arg not supported" >&2
+ exit 1
+esac
+echo "$0: script ended. exiting"
+++ /dev/null
-#!/bin/bash
-
-trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
-
-usage() {
- cat <<EOF
-Usage: relay-cmd poweroff|poweron|reboot HOSTNAME|CHANNEL [RELAY_ID]
-
-RELAY_ID is not needed if only 1 relay device is connected. To
-understand CHANNEL and RELAY_ID, run hidusb-relay-cmd and
-hidusb-relay-cmd state. I recommend writing a config in
-/etc/relay-cmd.conf , then using HOSTNAME instead of CHANNEL and
-RELAY_ID. It serves as documentation of what you connected the relay
-to. Example config:
-
-# This config is just sourced from bash, so make sure its valid bash.
-# relay_id is not needed if only 1 relay is connected.
-cephserver3_channel=1
-cephserver3_relay_id=HURTM
-cephserver2_channel=2
-cephserver2_relay_id=HURTM
-EOF
-}
-
-
-if (( $# < 2 )); then
- usage
-fi
-
-read action chan relay_id <<<"$@"
-
-if [[ -e /etc/relay-cmd.conf ]]; then
- source /etc/relay-cmd.conf
-fi
-
-# we know HOSTNAME is given if it doesn't start with a number.
-if [[ $chan != [0-9]* ]]; then
- if [[ ! $relay_id ]]; then
- relay_id_var=${chan}_relay_id
- relay_id=${!relay_id_var}
- fi
- chan_var=${chan}_channel
- chan=${!chan_var}
-
- if [[ ! $chan ]]; then
- echo "error: channel not found in /etc/relay-cmd.conf" >&2
- exit 1
- fi
-fi
-
-
-if hidusb-relay-cmd state | grep ON; then
- printf "%s\n" "WARNING: output from hidusb-relay-cmd state shows an ON relay. this could mean another command instance is running, or it got stuck on due to an uncompleted command." >&2
- # TODO: exit in this case and print out the command to turn it off.
-fi
-
-
-case $(hidusb-relay-cmd state|wc -l) in
- 0)
- echo "error: got 0 lines from running hidusb-relay-cmd state" >&2
- exit 1
- ;;
- 1) : ;;
- *)
- if [[ ! $relay_id ]]; then
- echo "error: more than 1 relay device, so passing its id is required" >&2
- exit 1
- fi
- ;;
-esac
-
-
-if [[ $relay_id ]]; then
- # leave this as an empty var if its not passed
- relay_id_arg="id=$relay_id"
-fi
-
-# verbose command
-v() {
- printf "$0 running: %s\n" "$*"
- "$@"
-}
-
-
-# ignore hup so we complete even if there is a connection problem.
-trap '' HUP
-echo "$0: doing $action. shell commands will be printed to the terminal."
-case $action in
- poweroff)
- v hidusb-relay-cmd $relay_id_arg on $chan
- v sleep 6
- v hidusb-relay-cmd $relay_id_arg off $chan
- ;;
- poweron)
- v hidusb-relay-cmd $relay_id_arg on $chan
- v sleep 1
- v hidusb-relay-cmd $relay_id_arg off $chan
- ;;
- reboot)
- v hidusb-relay-cmd $relay_id_arg on $chan
- v sleep 6
- v hidusb-relay-cmd $relay_id_arg off $chan
- v sleep 1
- v hidusb-relay-cmd $relay_id_arg on $chan
- v sleep 1
- v hidusb-relay-cmd $relay_id_arg off $chan
- ;;
- *)
- echo "error: action arg not supported" >&2
- exit 1
-esac
-echo "$0: script ended. exiting"