Document sanity tests
[libremanage.git] / libremanage
1 #!/usr/bin/python3
2
3 """
4 libremanage - Lightweight, free software for remote side-chanel server management
5
6 Copyright (C) 2018 Alyssa Rosenzweig
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Affero General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program. If not, see <https://www.gnu.org/licenses/>.
20 """
21
22 USAGE = """
23 Usage:
24
25 $ libremanage [server name] [command]
26
27 Example:
28
29 $ libremanage web2 reboot
30
31 Server names are defined in the accompanying config.py.
32
33 Valid commands are as follows:
34
35 - shutdown, reboot, poweron: Power management
36 - tty: Open TTY in GNU Screen
37 - sanity, sanity-sh: SSH sanity tests, ignore
38 """
39
40 import sys
41 import json
42 import functools
43 import subprocess
44
45 def open_ssh(server, command, force_tty=False):
46 config = server["ssh"]
47 subprocess.run(["ssh", "-t" if force_tty else "", config["username"] + "@" + config["host"], "-p", str(config["port"]), command])
48
49 def die_with_usage(message):
50 print(message)
51 print(USAGE)
52 sys.exit(1)
53
54 def get_server_handle(name):
55 try:
56 server = CONFIG["servers"][name]
57 except KeyError:
58 die_with_usage("Unknown server, please configure")
59
60 # Associate manager configuration
61 server["ssh"] = CONFIG["managers"][server["manager"]]
62
63 return server
64
65 def set_server_power(state, server):
66 conf = server["power"]
67
68 # Set invert to write LOW for power on and HIGH for off
69 if conf["invert"]:
70 state = 1 - state
71
72 # Export pin, configure, write value, unexport
73 open_ssh(server, "echo " + str(conf["pin"]) + " > /sys/class/gpio/export")
74 open_ssh(server, "echo out > /sys/class/gpio/gpio" + str(conf["pin"]) + "/direction")
75 open_ssh(server, "echo " + str(state) + " > /sys/class/gpio/gpio" + str(conf["pin"]) + "/value")
76 open_ssh(server, "echo " + str(conf["pin"]) + " > /sys/class/gpio/unexport")
77
78 COMMANDS = {
79 # Power managemment
80
81 "shutdown": functools.partial(set_server_power, 0),
82 "poweron": functools.partial(set_server_power, 1),
83 "reboot": lambda s: (set_server_power(0, s), set_server_power(1, s)),
84
85 # TTY access (or keyboard if wired as such)
86
87 "tty": lambda s: open_ssh(s, "screen " + s["tty"]["file"] + " " + str(s["tty"]["baud"]), force_tty=True),
88
89 # SSH sanity tests
90
91 "sanity": lambda s: open_ssh(s, "whoami"),
92 "sanity-sh": lambda s: open_ssh(s, ""),
93 }
94
95 def issue_command(server_name, command):
96 server = get_server_handle(server_name)
97
98 try:
99 callback = COMMANDS[command]
100 except KeyError:
101 die_with_usage("Invalid command supplied")
102
103 callback(server)
104
105 # Load configuration, get command, and go!
106
107 with open("config.json") as f:
108 CONFIG = json.load(f)
109
110 if len(sys.argv) != 3:
111 die_with_usage("Incorrect number of arguments")
112
113 issue_command(sys.argv[1], sys.argv[2])