install.sh 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #!/usr/bin/env bash
  2. # ------------------------------------------------------------------
  3. # WeComCenter / ROMAN 定制中心 — one-step installer
  4. #
  5. # Usage:
  6. # ./install.sh # auto-detect the running zabbix web container
  7. # ./install.sh /path/to/modules # copy into a plain (non-docker) modules dir
  8. #
  9. # It will:
  10. # 1) copy the WecomCenter module next to the Zabbix frontend,
  11. # 2) fix permissions (module readable, assets/l2topology writable for the config mirror),
  12. # 3) create the two L2 tables if a MySQL container is reachable (idempotent),
  13. # 4) install the L2 collector into /opt/zabbix/l2collector and (optionally) crontab.
  14. #
  15. # Env knobs:
  16. # MYSQL_CONTAINER (default: auto / zabbix-mysql)
  17. # MYSQL_USER / MYSQL_PASSWORD (default: read from the Zabbix MySQL container)
  18. # MYSQL_ROOT_PASSWORD (optional fallback only; leave unset to use MYSQL_USER)
  19. # MYSQL_DATABASE (default: zabbix)
  20. # COLLECTOR_DIR (default: /opt/zabbix/l2collector)
  21. # INSTALL_CRON=0 to skip installing crontab entries.
  22. # ------------------------------------------------------------------
  23. set -euo pipefail
  24. HERE="$(cd "$(dirname "$0")" && pwd)"
  25. SRC="$HERE/WecomCenter"
  26. MYSQL_CONTAINER="${MYSQL_CONTAINER:-}"
  27. MYSQL_USER="${MYSQL_USER:-}"
  28. MYSQL_PASSWORD="${MYSQL_PASSWORD:-}"
  29. MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-}"
  30. MYSQL_DATABASE="${MYSQL_DATABASE:-}"
  31. COLLECTOR_DIR="${COLLECTOR_DIR:-/opt/zabbix/l2collector}"
  32. INSTALL_CRON="${INSTALL_CRON:-1}"
  33. if [ ! -d "$SRC" ]; then
  34. echo "ERROR: WecomCenter folder not found next to install.sh" >&2
  35. exit 1
  36. fi
  37. # Guard against double-encoded UTF-8 in the package. A good source contains
  38. # none of the classic GBK-mojibake fragments below; if it does, stop before
  39. # copying a template that would render garbage button labels.
  40. if grep -rEl '鈼|鉂|锛|鈱|鈥|�' "$SRC" --include='*.php' --include='*.js' >/dev/null 2>&1; then
  41. echo "ERROR: mojibake found in $SRC (repair the UTF-8 source before installing)." >&2
  42. grep -rEl '鈼|鉂|锛|鈱|鈥|�' "$SRC" --include='*.php' --include='*.js' >&2 || true
  43. exit 1
  44. fi
  45. DEST=""
  46. CONTAINER=""
  47. if [ $# -ge 1 ]; then
  48. DEST="$1"
  49. mkdir -p "$DEST"
  50. rm -rf "$DEST/WecomCenter"
  51. cp -r "$SRC" "$DEST/WecomCenter"
  52. echo "OK: copied to $DEST/WecomCenter"
  53. else
  54. CONTAINER="$(docker ps --format '{{.Names}}' 2>/dev/null | grep -iE 'zabbix.*(web|nginx|apache|frontend)' | head -n1 || true)"
  55. if [ -z "$CONTAINER" ]; then
  56. echo "ERROR: no running Zabbix frontend container detected." >&2
  57. echo " Pass the modules directory explicitly, e.g.: ./install.sh /opt/zabbix/zabbix-server/web-modules" >&2
  58. exit 1
  59. fi
  60. docker cp "$SRC" "$CONTAINER:/usr/share/zabbix/modules/"
  61. echo "OK: copied into container '$CONTAINER' -> /usr/share/zabbix/modules/WecomCenter"
  62. fi
  63. # ---- permissions: module readable everywhere, config dir writable by php ----
  64. if [ -n "$DEST" ]; then
  65. chmod -R a+rX "$DEST/WecomCenter" 2>/dev/null || true
  66. chmod -R 777 "$DEST/WecomCenter/assets/l2topology" 2>/dev/null || true
  67. else
  68. docker exec "$CONTAINER" sh -c 'M=/usr/share/zabbix/modules/WecomCenter; chmod -R a+rX "$M"; chmod -R 777 "$M/assets/l2topology"' 2>/dev/null || true
  69. fi
  70. # ---- create L2 tables (idempotent) ----
  71. if [ -z "$MYSQL_CONTAINER" ]; then
  72. MYSQL_CONTAINER="$(docker ps --format '{{.Names}}' 2>/dev/null | grep -iE 'zabbix.*(mysql|db|mariadb)' | head -n1 || true)"
  73. fi
  74. if [ -n "$MYSQL_CONTAINER" ]; then
  75. # Reuse the credentials Zabbix already injected into its MySQL container;
  76. # never keep a second, hard-coded database login in this installer.
  77. if [ -z "$MYSQL_USER" ] || [ -z "$MYSQL_PASSWORD" ]; then
  78. MYSQL_ENV="$(docker inspect "$MYSQL_CONTAINER" --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null || true)"
  79. MYSQL_USER="${MYSQL_USER:-$(printf '%s\n' "$MYSQL_ENV" | sed -n 's/^MYSQL_USER=//p' | head -n1)}"
  80. MYSQL_PASSWORD="${MYSQL_PASSWORD:-$(printf '%s\n' "$MYSQL_ENV" | sed -n 's/^MYSQL_PASSWORD=//p' | head -n1)}"
  81. MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-$(printf '%s\n' "$MYSQL_ENV" | sed -n 's/^MYSQL_ROOT_PASSWORD=//p' | head -n1)}"
  82. MYSQL_DATABASE="${MYSQL_DATABASE:-$(printf '%s\n' "$MYSQL_ENV" | sed -n 's/^MYSQL_DATABASE=//p' | head -n1)}"
  83. fi
  84. MYSQL_DATABASE="${MYSQL_DATABASE:-zabbix}"
  85. if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
  86. MYSQL_AUTH=(-u"$MYSQL_USER" -p"$MYSQL_PASSWORD")
  87. elif [ -n "$MYSQL_ROOT_PASSWORD" ]; then
  88. MYSQL_AUTH=(-uroot -p"$MYSQL_ROOT_PASSWORD")
  89. else
  90. MYSQL_AUTH=()
  91. fi
  92. if [ ${#MYSQL_AUTH[@]} -gt 0 ]; then
  93. echo "Ensuring L2 tables in container '$MYSQL_CONTAINER' ..."
  94. docker exec -i "$MYSQL_CONTAINER" mysql "${MYSQL_AUTH[@]}" "$MYSQL_DATABASE" <<'SQL' || echo "WARN: could not create tables automatically (page will retry on first load)."
  95. CREATE TABLE IF NOT EXISTS l2_topology_history (
  96. id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ts BIGINT UNSIGNED NOT NULL,
  97. captured_at DATETIME NOT NULL, stats TEXT NOT NULL, snapshot MEDIUMBLOB NOT NULL,
  98. PRIMARY KEY (id), KEY idx_l2_ts (ts)
  99. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  100. CREATE TABLE IF NOT EXISTS l2_topology_config (
  101. id TINYINT UNSIGNED NOT NULL, config LONGTEXT NOT NULL, updated DATETIME NOT NULL,
  102. PRIMARY KEY (id)
  103. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  104. SQL
  105. # Register/enable the module as well. This is exactly what the compose
  106. # sidecar does; doing it here keeps a manual install from requiring a
  107. # manual "Scan directory + Enable" step. Never touch an existing row.
  108. if docker exec "$MYSQL_CONTAINER" mysql "${MYSQL_AUTH[@]}" "$MYSQL_DATABASE" -N -e "SHOW TABLES LIKE 'module'" 2>/dev/null | grep -qx 'module'; then
  109. docker exec -i "$MYSQL_CONTAINER" mysql "${MYSQL_AUTH[@]}" "$MYSQL_DATABASE" <<'SQL' || echo "WARN: could not register module automatically; enable it in Administration -> Modules."
  110. SET @nid := (SELECT COALESCE(MAX(moduleid), 0) + 1 FROM module);
  111. INSERT INTO module (moduleid, id, relative_path, status, config)
  112. SELECT @nid, 'wecom_center', 'modules/WecomCenter', 1, '[]'
  113. FROM DUAL
  114. WHERE NOT EXISTS (SELECT 1 FROM module WHERE id = 'wecom_center');
  115. SQL
  116. echo "OK: module 'wecom_center' registered/enabled (idempotent)."
  117. else
  118. echo "WARN: Zabbix 'module' table not found yet; enable WecomCenter in Administration -> Modules after Zabbix finishes its first start."
  119. fi
  120. else
  121. echo "WARN: no MYSQL_USER/MYSQL_PASSWORD found in '$MYSQL_CONTAINER'; page auto-creates tables on load."
  122. fi
  123. else
  124. echo "WARN: no MySQL container detected; skip table creation (page auto-creates on load)."
  125. fi
  126. # ---- install collector on the host (needs SNMP access to switches) ----
  127. COLLECTOR_SRC="$HERE/../collector"
  128. if [ -d "$COLLECTOR_SRC" ] && [ -d /opt/zabbix ]; then
  129. echo "Installing L2 collector to $COLLECTOR_DIR ..."
  130. mkdir -p "$COLLECTOR_DIR"
  131. cp -f "$COLLECTOR_SRC"/*.py "$COLLECTOR_DIR"/ 2>/dev/null || true
  132. cp -f "$COLLECTOR_SRC"/*.sh "$COLLECTOR_DIR"/ 2>/dev/null || true
  133. chmod +x "$COLLECTOR_DIR"/*.sh 2>/dev/null || true
  134. # mirror config next to the module so collectors can read it
  135. CFG="$(dirname "$SRC")/WecomCenter/assets/l2topology/config.json"
  136. if [ -f "$CFG" ]; then cp -f "$CFG" "$COLLECTOR_DIR/config.json"; fi
  137. if [ "$INSTALL_CRON" = "1" ] && command -v crontab >/dev/null 2>&1; then
  138. { crontab -l 2>/dev/null | grep -v l2collector || true
  139. echo "*/5 * * * * /bin/bash $COLLECTOR_DIR/run_full.sh >/dev/null 2>&1"
  140. echo "* * * * * /bin/bash $COLLECTOR_DIR/refresh_alarms.sh >/dev/null 2>&1"
  141. } | crontab - || echo "WARN: crontab install failed"
  142. echo "OK: crontab entries installed (full every 5 min, alarm refresh every 1 min)."
  143. fi
  144. else
  145. echo "INFO: collector/ not found next to install.sh or /opt/zabbix missing; skip collector install."
  146. fi
  147. echo
  148. echo "Done. Next steps:"
  149. echo " 1) Zabbix -> Administration -> ROMAN 定制中心 (module should already be enabled by compose)"
  150. echo " If not listed: Administration -> Modules -> 'Scan directory' -> Enable 'WecomCenter'"
  151. echo " 2) Administration -> ROMAN 定制中心 -> 'L2 拓扑' tab: choose templates / colours / intervals -> Save"
  152. echo " 3) Monitoring -> L2 Topology (or: /zabbix.php?action=l2.topology.view)"
  153. echo " 4) Run once: sudo bash $COLLECTOR_DIR/run_full.sh (log: /var/log/l2collector.log)"