Commit | Line | Data |
---|---|---|
bbc21163 AR |
1 | #!/usr/bin/python3 |
2 | ||
3f7ab73a AR |
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 | """ | |
93f19e92 | 21 | |
b266c229 | 22 | USAGE = """ |
93f19e92 AR |
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 | |
9ed4bf16 | 36 | - tty: Open TTY in GNU Screen |
93f19e92 AR |
37 | """ |
38 | ||
22f864bb AR |
39 | import sys |
40 | import json | |
41 | import functools | |
42 | import subprocess | |
e14c36b3 | 43 | |
28c204f9 AR |
44 | def open_ssh(server, command): |
45 | config = server["ssh"] | |
e51c7dbc | 46 | subprocess.run(["ssh", "-t", config["username"] + "@" + config["host"], "-p", str(config["port"]), command]) |
e14c36b3 | 47 | |
40a688c0 AR |
48 | def die_with_usage(message): |
49 | print(message) | |
b266c229 AR |
50 | print(USAGE) |
51 | sys.exit(1) | |
52 | ||
d8ddde1a | 53 | def get_server_handle(name): |
38e0ee81 AR |
54 | try: |
55 | server = CONFIG["servers"][name] | |
56 | except KeyError: | |
57 | die_with_usage("Unknown server, please configure") | |
58 | ||
59 | # Associate manager configuration | |
82ee0c31 | 60 | server["ssh"] = CONFIG["managers"][server["manager"]] |
38e0ee81 | 61 | |
82ee0c31 | 62 | return server |
d8ddde1a AR |
63 | |
64 | def set_server_power(state, server): | |
20249149 | 65 | print("Setting to power state " + str(state)) |
e799dcd1 AR |
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 | print(conf["pin"]) | |
282c96f8 | 73 | |
c10fa78c | 74 | # Export pin, configure, write value, unexport |
282c96f8 | 75 | open_ssh(server, "echo " + str(conf["pin"]) + " > /sys/class/gpio/export") |
c2e97249 | 76 | open_ssh(server, "echo out > /sys/class/gpio/gpio" + str(conf["pin"]) + "/direction") |
c10fa78c | 77 | open_ssh(server, "echo " + str(state) + " > /sys/class/gpio/gpio" + str(conf["pin"]) + "/value") |
282c96f8 | 78 | open_ssh(server, "echo " + str(conf["pin"]) + " > /sys/class/gpio/unexport") |
d8ddde1a AR |
79 | |
80 | COMMANDS = { | |
e14d7650 AR |
81 | # Power managemment |
82 | ||
da2eb513 AR |
83 | "shutdown": functools.partial(set_server_power, 0), |
84 | "poweron": functools.partial(set_server_power, 1), | |
85 | "reboot": lambda s: (set_server_power(0, s), set_server_power(1, s)), | |
e14d7650 AR |
86 | |
87 | # TTY access (or keyboard if wired as such) | |
88 | ||
e51c7dbc | 89 | "tty": lambda s: open_ssh(s, "screen " + s["tty"]["file"] + " " + str(s["tty"]["baud"])), |
e14c36b3 | 90 | |
da2eb513 | 91 | # SSH sanity tests |
82ee0c31 | 92 | |
28c204f9 AR |
93 | "sanity": lambda s: open_ssh(s, "whoami"), |
94 | "console": lambda s: open_ssh(s, ""), | |
d8ddde1a AR |
95 | } |
96 | ||
d24528e4 | 97 | def issue_command(server_name, command): |
d8ddde1a | 98 | server = get_server_handle(server_name) |
d24528e4 | 99 | print(server_name, command) |
40a688c0 AR |
100 | |
101 | try: | |
da2eb513 | 102 | callback = COMMANDS[command] |
40a688c0 AR |
103 | except KeyError: |
104 | die_with_usage("Invalid command supplied") | |
d24528e4 | 105 | |
82ee0c31 | 106 | callback(server) |
e14d7650 | 107 | |
22f864bb AR |
108 | # Load configuration, get command, and go! |
109 | ||
110 | with open("config.json") as f: | |
111 | CONFIG = json.load(f) | |
112 | ||
113 | if len(sys.argv) != 3: | |
114 | die_with_usage("Incorrect number of arguments") | |
115 | ||
d24528e4 | 116 | issue_command(sys.argv[1], sys.argv[2]) |