docs: Add 2 and update 2 files
This commit is contained in:
@@ -168,7 +168,7 @@ _check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_T
|
|||||||
_check_docker_installed || _die "Docker test failed"
|
_check_docker_installed || _die "Docker test failed"
|
||||||
|
|
||||||
# Pull the Docker image
|
# Pull the Docker image
|
||||||
docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || _die "Failed to pull image"
|
docker pull -q "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || _die "Failed to pull image"
|
||||||
|
|
||||||
# Stop any existing container
|
# Stop any existing container
|
||||||
bash ./stop.sh || _die "Failed to stop container"
|
bash ./stop.sh || _die "Failed to stop container"
|
||||||
|
|||||||
113
source/agent-remote/hostinfo.sh
Executable file
113
source/agent-remote/hostinfo.sh
Executable file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# hostinfo.sh - Gather hardware/system info from a remote host
|
||||||
|
#
|
||||||
|
# Output: JSON object with system information
|
||||||
|
|
||||||
|
# -- Helper to escape JSON strings --
|
||||||
|
json_escape() {
|
||||||
|
local str="$1"
|
||||||
|
str="${str//\\/\\\\}"
|
||||||
|
str="${str//\"/\\\"}"
|
||||||
|
str="${str//$'\n'/\\n}"
|
||||||
|
str="${str//$'\r'/\\r}"
|
||||||
|
str="${str//$'\t'/\\t}"
|
||||||
|
echo -n "$str"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -- Gather system info --
|
||||||
|
|
||||||
|
HOSTNAME=$(hostname 2>/dev/null || echo "Unknown")
|
||||||
|
|
||||||
|
CPU=$(grep -m1 'model name' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | xargs || echo "Unknown")
|
||||||
|
CPU_CORES=$(nproc 2>/dev/null || grep -c '^processor' /proc/cpuinfo 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
MOTHERBOARD=$(cat /sys/devices/virtual/dmi/id/board_name 2>/dev/null || echo "Unknown")
|
||||||
|
|
||||||
|
# RAM from free -m (used = total - available)
|
||||||
|
RAM_TOTAL_MB=$(free -m 2>/dev/null | awk 'NR==2{print $2}' || echo "0")
|
||||||
|
RAM_AVAIL_MB=$(free -m 2>/dev/null | awk 'NR==2{print $7}' || echo "0")
|
||||||
|
RAM_USED_MB=$(( RAM_TOTAL_MB - RAM_AVAIL_MB ))
|
||||||
|
|
||||||
|
# Disk root (strip trailing G from df -BG output)
|
||||||
|
DISK_ROOT_USED_GB=$(df -BG / 2>/dev/null | awk 'NR==2{gsub(/G/,"",$3); print $3}' || echo "0")
|
||||||
|
DISK_ROOT_TOTAL_GB=$(df -BG / 2>/dev/null | awk 'NR==2{gsub(/G/,"",$2); print $2}' || echo "0")
|
||||||
|
|
||||||
|
# Disk /tank (only if mounted)
|
||||||
|
DISK_TANK_USED_GB=""
|
||||||
|
DISK_TANK_TOTAL_GB=""
|
||||||
|
if [ -d /tank ] && mountpoint -q /tank 2>/dev/null; then
|
||||||
|
DISK_TANK_USED_GB=$(df -BG /tank 2>/dev/null | awk 'NR==2{gsub(/G/,"",$3); print $3}' || echo "")
|
||||||
|
DISK_TANK_TOTAL_GB=$(df -BG /tank 2>/dev/null | awk 'NR==2{gsub(/G/,"",$2); print $2}' || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# IP addresses
|
||||||
|
IP_LOCAL=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "Unknown")
|
||||||
|
|
||||||
|
IP_TAILSCALE=""
|
||||||
|
if command -v tailscale &>/dev/null; then
|
||||||
|
IP_TAILSCALE=$(tailscale ip -4 2>/dev/null || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
IP_PUBLIC=""
|
||||||
|
if command -v curl &>/dev/null; then
|
||||||
|
IP_PUBLIC=$(curl -s --max-time 3 ifconfig.me 2>/dev/null || echo "")
|
||||||
|
elif command -v wget &>/dev/null; then
|
||||||
|
IP_PUBLIC=$(wget -qO- --timeout=3 ifconfig.me 2>/dev/null || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS info
|
||||||
|
OS=$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"' || uname -o 2>/dev/null || echo "Unknown")
|
||||||
|
KERNEL=$(uname -r 2>/dev/null || echo "Unknown")
|
||||||
|
UPTIME=$(uptime -p 2>/dev/null || uptime 2>/dev/null || echo "Unknown")
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
DOCKER_VERSION=""
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
DOCKER_VERSION=$(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# GPU list (may have 0, 1, or multiple)
|
||||||
|
GPU_JSON="[]"
|
||||||
|
if command -v lspci &>/dev/null; then
|
||||||
|
GPU_LINES=$(lspci 2>/dev/null | grep -i 'vga\|3d\|display' | sed 's/^[^ ]* //' || true)
|
||||||
|
if [ -n "$GPU_LINES" ]; then
|
||||||
|
GPU_JSON="["
|
||||||
|
first=true
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
if [ "$first" = "true" ]; then
|
||||||
|
first=false
|
||||||
|
else
|
||||||
|
GPU_JSON+=","
|
||||||
|
fi
|
||||||
|
GPU_JSON+="\""
|
||||||
|
GPU_JSON+=$(json_escape "$line")
|
||||||
|
GPU_JSON+="\""
|
||||||
|
done <<< "$GPU_LINES"
|
||||||
|
GPU_JSON+="]"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -- Output JSON --
|
||||||
|
echo -n '{'
|
||||||
|
echo -n '"hostname":"'; json_escape "$HOSTNAME"; echo -n '"'
|
||||||
|
echo -n ',"cpu":"'; json_escape "$CPU"; echo -n '"'
|
||||||
|
echo -n ',"cpu_cores":"'; json_escape "$CPU_CORES"; echo -n '"'
|
||||||
|
echo -n ',"motherboard":"'; json_escape "$MOTHERBOARD"; echo -n '"'
|
||||||
|
echo -n ',"gpu":'"$GPU_JSON"
|
||||||
|
echo -n ',"ram_used_mb":"'; json_escape "$RAM_USED_MB"; echo -n '"'
|
||||||
|
echo -n ',"ram_total_mb":"'; json_escape "$RAM_TOTAL_MB"; echo -n '"'
|
||||||
|
echo -n ',"disk_root_used_gb":"'; json_escape "$DISK_ROOT_USED_GB"; echo -n '"'
|
||||||
|
echo -n ',"disk_root_total_gb":"'; json_escape "$DISK_ROOT_TOTAL_GB"; echo -n '"'
|
||||||
|
echo -n ',"disk_tank_used_gb":"'; json_escape "$DISK_TANK_USED_GB"; echo -n '"'
|
||||||
|
echo -n ',"disk_tank_total_gb":"'; json_escape "$DISK_TANK_TOTAL_GB"; echo -n '"'
|
||||||
|
echo -n ',"ip_local":"'; json_escape "$IP_LOCAL"; echo -n '"'
|
||||||
|
echo -n ',"ip_tailscale":"'; json_escape "$IP_TAILSCALE"; echo -n '"'
|
||||||
|
echo -n ',"ip_public":"'; json_escape "$IP_PUBLIC"; echo -n '"'
|
||||||
|
echo -n ',"os":"'; json_escape "$OS"; echo -n '"'
|
||||||
|
echo -n ',"kernel":"'; json_escape "$KERNEL"; echo -n '"'
|
||||||
|
echo -n ',"uptime":"'; json_escape "$UPTIME"; echo -n '"'
|
||||||
|
echo -n ',"docker_version":"'; json_escape "$DOCKER_VERSION"; echo -n '"'
|
||||||
|
echo '}'
|
||||||
171
source/src/commands/hostinfo.cpp
Normal file
171
source/src/commands/hostinfo.cpp
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include "command_registry.hpp"
|
||||||
|
#include "servers.hpp"
|
||||||
|
#include "utils/output.hpp"
|
||||||
|
#include "utils/execute.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
|
#include "shared_commands.hpp"
|
||||||
|
#include "tableprint.hpp"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <libassert/assert.hpp>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
int hostinfo_handler(const CommandContext &ctx);
|
||||||
|
|
||||||
|
static std::vector<std::string> hostinfo_name_list = {"hostinfo", "sysinfo"};
|
||||||
|
|
||||||
|
void hostinfo_autocomplete(const CommandContext &ctx) {
|
||||||
|
if (ctx.args.size() == 0) {
|
||||||
|
std::vector<ServerConfig> servers = get_configured_servers();
|
||||||
|
for (const auto &server : servers)
|
||||||
|
rawout << server.get_server_name() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HostinfoCommandRegister {
|
||||||
|
HostinfoCommandRegister() {
|
||||||
|
CommandRegistry::instance().register_command({
|
||||||
|
hostinfo_name_list,
|
||||||
|
hostinfo_handler,
|
||||||
|
hostinfo_autocomplete,
|
||||||
|
false, // hidden
|
||||||
|
true, // requires_config
|
||||||
|
true, // requires_install
|
||||||
|
1, // min_args
|
||||||
|
1, // max_args
|
||||||
|
"hostinfo SERVER",
|
||||||
|
"Display hardware and system information for a remote server.",
|
||||||
|
R"(
|
||||||
|
Display hardware and system information for a remote server.
|
||||||
|
hostinfo SERVER Show CPU, motherboard, GPU, RAM, disk usage,
|
||||||
|
IP addresses, OS, kernel, uptime, and Docker version.
|
||||||
|
)"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} hostinfo_command_register;
|
||||||
|
|
||||||
|
int hostinfo_handler(const CommandContext &ctx) {
|
||||||
|
std::string server_name = ctx.args[0];
|
||||||
|
|
||||||
|
ServerConfig server_env(server_name);
|
||||||
|
if (!server_env.is_valid()) {
|
||||||
|
error << "Server '" << server_name << "' is not valid." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(server_env.get_users().size() > 0, "No users found for server " + server_name);
|
||||||
|
std::string user = server_env.get_users()[0].user;
|
||||||
|
|
||||||
|
std::string agent_path = remotepath(server_name, user).agent();
|
||||||
|
std::string script_path = agent_path + "/hostinfo.sh";
|
||||||
|
|
||||||
|
std::string output;
|
||||||
|
sCommand cmd(agent_path, script_path, {});
|
||||||
|
bool success = execute_ssh_command(server_env.get_SSH_INFO(user), cmd, cMode::Silent, &output);
|
||||||
|
|
||||||
|
if (!success || output.empty()) {
|
||||||
|
error << "Failed to retrieve host information from " << server_name << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json j;
|
||||||
|
try {
|
||||||
|
j = nlohmann::json::parse(output);
|
||||||
|
} catch (const nlohmann::json::parse_error &e) {
|
||||||
|
error << "Failed to parse host info: " << e.what() << std::endl;
|
||||||
|
debug << "Output was: " << output << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_str = [&](const std::string &key) -> std::string {
|
||||||
|
if (j.contains(key) && j[key].is_string()) {
|
||||||
|
std::string val = j[key].get<std::string>();
|
||||||
|
return val.empty() ? "-" : val;
|
||||||
|
}
|
||||||
|
return "-";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format RAM as "X.X / Y.Y GB"
|
||||||
|
std::string ram_display = "-";
|
||||||
|
{
|
||||||
|
std::string used = get_str("ram_used_mb");
|
||||||
|
std::string total = get_str("ram_total_mb");
|
||||||
|
if (used != "-" && total != "-") {
|
||||||
|
try {
|
||||||
|
int used_mb = std::stoi(used);
|
||||||
|
int total_mb = std::stoi(total);
|
||||||
|
char buf[64];
|
||||||
|
snprintf(buf, sizeof(buf), "%.1f / %.1f GB",
|
||||||
|
used_mb / 1024.0, total_mb / 1024.0);
|
||||||
|
ram_display = buf;
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format disk root
|
||||||
|
std::string disk_root_display = "-";
|
||||||
|
{
|
||||||
|
std::string used = get_str("disk_root_used_gb");
|
||||||
|
std::string total = get_str("disk_root_total_gb");
|
||||||
|
if (used != "-" && total != "-")
|
||||||
|
disk_root_display = used + " / " + total + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format disk /tank (only if present)
|
||||||
|
std::string disk_tank_display;
|
||||||
|
{
|
||||||
|
std::string used = get_str("disk_tank_used_gb");
|
||||||
|
std::string total = get_str("disk_tank_total_gb");
|
||||||
|
if (used != "-" && total != "-")
|
||||||
|
disk_tank_display = used + " / " + total + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format GPU list
|
||||||
|
std::string gpu_display = "-";
|
||||||
|
if (j.contains("gpu") && j["gpu"].is_array() && !j["gpu"].empty()) {
|
||||||
|
gpu_display.clear();
|
||||||
|
for (size_t i = 0; i < j["gpu"].size(); ++i) {
|
||||||
|
if (i > 0) gpu_display += ", ";
|
||||||
|
gpu_display += j["gpu"][i].get<std::string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format CPU with cores
|
||||||
|
std::string cpu_display = get_str("cpu");
|
||||||
|
std::string cores = get_str("cpu_cores");
|
||||||
|
if (cores != "-")
|
||||||
|
cpu_display += " (" + cores + " cores)";
|
||||||
|
|
||||||
|
// Format IP addresses on one line
|
||||||
|
std::string ip_display = get_str("ip_local");
|
||||||
|
{
|
||||||
|
std::string ts = get_str("ip_tailscale");
|
||||||
|
std::string pub = get_str("ip_public");
|
||||||
|
if (ts != "-") ip_display += " ts:" + ts;
|
||||||
|
if (pub != "-") ip_display += " pub:" + pub;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display
|
||||||
|
tableprint tp("Host Info: " + server_name, true);
|
||||||
|
tp.add_row({"Property", "Value"});
|
||||||
|
tp.add_row({"Hostname", get_str("hostname")});
|
||||||
|
tp.add_row({"OS", get_str("os")});
|
||||||
|
tp.add_row({"Kernel", get_str("kernel")});
|
||||||
|
tp.add_row({"Uptime", get_str("uptime")});
|
||||||
|
tp.add_row({"CPU", cpu_display});
|
||||||
|
tp.add_row({"Motherboard", get_str("motherboard")});
|
||||||
|
tp.add_row({"GPU", gpu_display});
|
||||||
|
tp.add_row({"RAM", ram_display});
|
||||||
|
tp.add_row({"Disk /", disk_root_display});
|
||||||
|
if (!disk_tank_display.empty())
|
||||||
|
tp.add_row({"Disk /tank", disk_tank_display});
|
||||||
|
tp.add_row({"IP Addresses", ip_display});
|
||||||
|
tp.add_row({"Docker", get_str("docker_version")});
|
||||||
|
tp.print();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
@@ -561,7 +561,7 @@ _check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_T
|
|||||||
_check_docker_installed || _die "Docker test failed"
|
_check_docker_installed || _die "Docker test failed"
|
||||||
|
|
||||||
# Pull the Docker image
|
# Pull the Docker image
|
||||||
docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || _die "Failed to pull image"
|
docker pull -q "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || _die "Failed to pull image"
|
||||||
|
|
||||||
# Stop any existing container
|
# Stop any existing container
|
||||||
bash "$SCRIPT_DIR/stop.sh" 2>/dev/null || true
|
bash "$SCRIPT_DIR/stop.sh" 2>/dev/null || true
|
||||||
|
|||||||
Reference in New Issue
Block a user