summaryrefslogtreecommitdiff
path: root/host-control
diff options
context:
space:
mode:
authorPaul Kocialkowski2023-01-19 21:22:36 +0100
committerPaul Kocialkowski2023-01-24 10:42:18 +0100
commitbc73d147c73efa1c0eb2594bb3435935a6d70a7f (patch)
tree110dbc244b2a336e2426f1dd28d4e6c91b380c6a /host-control
parentf704292c65cd1bcc958db5c2c1162f88a52d9c62 (diff)
Add host-control util
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
Diffstat (limited to 'host-control')
-rwxr-xr-xhost-control430
1 files changed, 430 insertions, 0 deletions
diff --git a/host-control b/host-control
new file mode 100755
index 0000000..f16225b
--- /dev/null
+++ b/host-control
@@ -0,0 +1,430 @@
+#!/usr/bin/python
+
+import os
+import sys
+import getopt
+import socket
+import subprocess
+import yaml
+
+data_path = os.path.expanduser("~") + "/.config/host-control.yaml"
+
+class host_control():
+ verbose = False
+ host = None
+
+ usage = "Usage: host-control [options] [action]\n" \
+ "\n" \
+ "Options:\n" \
+ " -h [host]: specify host\n" \
+ " -v: tell more details\n" \
+ "\n" \
+ "Actions:\n" \
+ " system-info show kernel info\n" \
+ " system-shell access system shell\n" \
+ " system-ping ping host system\n" \
+ " system-update update system\n" \
+ " system-restart restart system\n" \
+ " system-daemon-status show systemd status\n" \
+ " system-daemon-log show systemd log\n" \
+ " system-daemon-reload reload systemd units\n" \
+ " system-docker-status show docker status\n" \
+ " config-list list host config items\n" \
+ " config-update [config] update host config\n" \
+ " unit-list list host units\n" \
+ " unit-status [unit] show unit status\n" \
+ " unit-start [unit] start unit\n" \
+ " unit-stop [unit] stop unit\n" \
+ " unit-restart [unit] restart unit\n" \
+ " unit-log [unit] show unit log\n" \
+ " unit-update [unit] update unit"
+
+ # trace
+
+ def trace_command(self, command):
+ if self.verbose:
+ print("Running: " + " ".join(command))
+
+ # ssh
+
+ def ssh_address(self, host):
+ address = ""
+
+ if "user" in host:
+ address += host["user"]+"@"
+
+ address += self.host_address(host)
+
+ return address
+
+ def ssh_command(self, address, arguments):
+ return [ "ssh", address ] + arguments
+
+ # host
+
+ def host_address(self, host):
+ if "address" in host:
+ return host["address"]
+ else:
+ return host["name"]
+
+ def host_lookup(self, name):
+ for h in self.hosts:
+ if not "name" in h:
+ continue
+
+ if h["name"] == name:
+ return h
+
+ return None
+
+ # config
+
+ def config_path(self):
+ return self.config["path"]
+
+ def config_lookup(self, host, name):
+ if not "config" in host:
+ print("No config in host: " + self.host)
+ return 1
+
+ for c in host["config"]:
+ if not "name" in c:
+ continue
+ elif not "files" in c:
+ continue
+
+ if c["name"] == name:
+ return c
+
+ return None
+
+ def config_copy(self, host, source, sink):
+ source_path = os.path.join(self.config_path(), source)
+
+ if self.host_address(host) == self.hostname:
+ print("Copy " + source + " to " + sink)
+
+ command = [ "cp", source_path, sink ]
+ else:
+ sink_address = self.ssh_address(host) + ":" + sink
+ print("Copy " + source + " to " + sink_address)
+
+ command = [ "scp", source_path, sink_address ]
+
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def config_update(self, host, name):
+ config = self.config_lookup(host, name)
+ if config is None:
+ print("No config for " + name + " in host: " + self.host)
+ return 1
+
+ print("Updating host " + self.host + " config: " + name)
+
+ for f in config["files"]:
+ source = f.split(":")[0]
+ sink = f.split(":")[1]
+
+ self.config_copy(host, source, sink)
+
+ return 0
+
+ def config_list(self, host):
+ if not "config" in host:
+ print("No config in host: " + self.host)
+ return 1
+
+ print("Host " + self.host + " config:")
+
+ for c in host["config"]:
+ if not "name" in c:
+ continue
+ elif not "files" in c:
+ continue
+
+ print("- " + c["name"])
+
+ return 0
+
+ # unit
+
+ def unit_lookup(self, host, name):
+ if not "units" in host:
+ print("No units in host: " + self.host)
+ return 1
+
+ for u in host["units"]:
+ if not "name" in u:
+ continue
+
+ if u["name"] == name:
+ return u
+
+ return None
+
+ def unit_systemctl(self, host, name, option):
+ address = self.ssh_address(host)
+
+ unit = self.unit_lookup(host, name)
+ if unit is None:
+ print("No " + name + " unit in host: " + self.host)
+ return 1
+
+ if not "systemd-unit" in unit:
+ return 1
+
+ command = self.ssh_command(address, [ "SYSTEMD_COLORS=1", "systemctl", option, unit["systemd-unit"] ])
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ return 0
+
+ def unit_status(self, host, name):
+ return self.unit_systemctl(host, name, "status")
+
+ def unit_start(self, host, name):
+ print("Starting unit: " + name)
+ return self.unit_systemctl(host, name, "start")
+
+ def unit_stop(self, host, name):
+ print("Stopping unit: " + name)
+ return self.unit_systemctl(host, name, "stop")
+
+ def unit_restart(self, host, name):
+ print("Restarting unit: " + name)
+ return self.unit_systemctl(host, name, "restart")
+
+ def unit_log(self, host, name):
+ address = self.ssh_address(host)
+
+ unit = self.unit_lookup(host, name)
+ if unit is None:
+ print("No " + name + " unit in host: " + self.host)
+ return 1
+
+ if not "systemd-unit" in unit:
+ return 1
+
+ if "log-path" in unit:
+ arguments = [ "tail", "-n", "50", "-f", unit["log-path"] ]
+ else:
+ arguments = [ "SYSTEMD_COLORS=1", "journalctl", "-n", "50", "-fu", unit["systemd-unit"] ]
+
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def unit_update(self, host, name):
+ daemon_reload = False
+
+ unit = self.unit_lookup(host, name)
+ if unit is None:
+ print("No " + name + " unit in host: " + self.host)
+ return 1
+
+ if not "config" in unit:
+ return 1
+
+ print("Updating unit: " + name)
+
+ if isinstance(unit["config"], list):
+ for c in unit["config"]:
+ self.config_update(host, c)
+
+ if c.startswith("systemd"):
+ daemon_reload = True
+ else:
+ self.config_update(host, unit["config"])
+ if unit["config"].startswith("systemd"):
+ daemon_reload = True
+
+ if daemon_reload:
+ self.system_daemon_reload(host)
+
+ self.unit_restart(host, name)
+
+ return 0
+
+ def unit_list(self, host):
+ if not "units" in host:
+ print("No units in host: " + self.host)
+ return 1
+
+ print("Host " + self.host + " units:")
+
+ for u in host["units"]:
+ if not "name" in u:
+ continue
+
+ print("- " + u["name"])
+
+ # system
+
+ def system_info(self, host):
+ address = self.ssh_address(host)
+ arguments = [ "uname", "-a" ]
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_shell(self, host):
+ address = self.ssh_address(host)
+ command = self.ssh_command(address, [])
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_ping(self, host):
+ address = self.host_address(host)
+ command = [ "ping", address ]
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_update(self, host):
+ address = self.ssh_address(host)
+ # TODO: Move update command in config.
+ arguments = [ "apt", "update", "&&", "apt", "upgrade" ]
+
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_restart(self, host):
+ address = self.ssh_address(host)
+ arguments = [ "reboot" ]
+
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_daemon_status(self, host):
+ address = self.ssh_address(host)
+ arguments = [ "SYSTEMD_COLORS=1", "systemctl", "status" ]
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_daemon_log(self, host):
+ address = self.ssh_address(host)
+ arguments = [ "SYSTEMD_COLORS=1", "journalctl", "-n", "50", "-fb" ]
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_daemon_reload(self, host):
+ address = self.ssh_address(host)
+ arguments = [ "SYSTEMD_COLORS=1", "systemctl", "daemon-reload" ]
+
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ def system_docker_status(self, host):
+ address = self.ssh_address(host)
+ arguments = [ "docker", "ps" ]
+
+ command = self.ssh_command(address, arguments)
+
+ self.trace_command(command)
+ subprocess.call(command)
+
+ # main
+
+ def data_load(self):
+ s = open(data_path, "r")
+ y = yaml.load(s, Loader = yaml.SafeLoader)
+ s.close()
+
+ self.hostname = socket.gethostname()
+ self.config = y["config"]
+ self.hosts = y["hosts"]
+
+ def main(self):
+ action = None
+ self.data_load()
+
+ options, arguments = getopt.getopt(sys.argv[1:], "h:v")
+
+ for key, value in options:
+ if key == "-h":
+ self.host = value
+ elif key == "-v":
+ self.verbose = True
+
+ if len(arguments) < 1:
+ print(self.usage)
+ return 1
+
+ if self.host is None:
+ self.host = self.hostname
+
+ host = self.host_lookup(self.host)
+ if host is None:
+ print("Unknown host: " + self.host)
+ return 1
+
+ print("Target host: " + self.host)
+
+ action = arguments[0]
+
+ if action == "system-info":
+ return self.system_info(host)
+ elif action == "system-shell":
+ return self.system_shell(host)
+ elif action == "system-ping":
+ return self.system_ping(host)
+ elif action == "system-update":
+ return self.system_update(host)
+ elif action == "system-restart":
+ return self.system_restart(host)
+ elif action == "system-daemon-status":
+ return self.system_daemon_status(host)
+ elif action == "system-daemon-log":
+ return self.system_daemon_log(host)
+ elif action == "system-daemon-reload":
+ return self.system_daemon_reload(host)
+ elif action == "system-docker-status":
+ return self.system_docker_status(host)
+ elif action == "config-list":
+ return self.config_list(host)
+ elif action == "unit-list":
+ return self.unit_list(host)
+
+ if len(arguments) < 2:
+ print(self.usage)
+ return 1
+
+ if action == "config-update":
+ return self.config_update(host, arguments[1])
+ elif action == "unit-status":
+ return self.unit_status(host, arguments[1])
+ elif action == "unit-start":
+ return self.unit_start(host, arguments[1])
+ elif action == "unit-stop":
+ return self.unit_stop(host, arguments[1])
+ elif action == "unit-restart":
+ return self.unit_restart(host, arguments[1])
+ elif action == "unit-log":
+ return self.unit_log(host, arguments[1])
+ elif action == "unit-update":
+ return self.unit_update(host, arguments[1])
+ else:
+ print("Unknown action: " + action)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ host_control().main()