From 906555ab4b7d3748157cd624e73c6f299684673e Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 14 Nov 2025 06:18:58 +0000 Subject: [PATCH] Update 4 files - /roles/proxmox_base_setup/tasks/main.yml - /roles/proxmox_base_setup/templates/node_exporter.service.j2 - /roles/proxmox_base_setup/templates/storcli_metrics.sh.j2 - /roles/proxmox_base_setup/handlers/main.yml --- roles/proxmox_base_setup/handlers/main.yml | 19 ++ roles/proxmox_base_setup/tasks/main.yml | 302 ++++++++++++++++++ .../templates/node_exporter.service.j2 | 31 ++ .../templates/storcli_metrics.sh.j2 | 64 ++++ 4 files changed, 416 insertions(+) create mode 100644 roles/proxmox_base_setup/handlers/main.yml create mode 100644 roles/proxmox_base_setup/tasks/main.yml create mode 100644 roles/proxmox_base_setup/templates/node_exporter.service.j2 create mode 100644 roles/proxmox_base_setup/templates/storcli_metrics.sh.j2 diff --git a/roles/proxmox_base_setup/handlers/main.yml b/roles/proxmox_base_setup/handlers/main.yml new file mode 100644 index 0000000..b34de83 --- /dev/null +++ b/roles/proxmox_base_setup/handlers/main.yml @@ -0,0 +1,19 @@ +--- +- name: restart ssh + systemd: + name: ssh + state: restarted + +- name: restart node_exporter + systemd: + name: node_exporter + state: restarted + daemon_reload: yes + enabled: yes + +- name: restart storcli_metrics + systemd: + name: storcli_metrics.timer + state: restarted + daemon_reload: yes + when: storcli_available | default(false) \ No newline at end of file diff --git a/roles/proxmox_base_setup/tasks/main.yml b/roles/proxmox_base_setup/tasks/main.yml new file mode 100644 index 0000000..96dcb1c --- /dev/null +++ b/roles/proxmox_base_setup/tasks/main.yml @@ -0,0 +1,302 @@ +--- +- name: Update package cache + apt: + update_cache: yes + cache_valid_time: 86400 + +- name: Upgrade installed packages (safe — не трогает pve-kernel и pve-пакеты) + apt: + upgrade: safe + notify: reboot if kernel updated + +- name: Install essential base packages + apt: + name: + - curl + - wget + - gnupg + - apt-transport-https + - ca-certificates + - lsb-release + - net-tools + - iproute2 + - pciutils + - smartmontools + - htop + - vim + - bash-completion + state: present + +- name: Ensure 'locales' is installed (required for locale_gen) + apt: + name: locales + state: present + when: system_locale is defined and system_locale | length > 0 + +- name: Remove unused packages + apt: + autoremove: yes + autoclean: yes + +- name: Disable IPv6 via sysctl + sysctl: + name: "{{ item.name }}" + value: "{{ item.value }}" + sysctl_set: yes + state: present + reload: yes + loop: + - { name: 'net.ipv6.conf.all.disable_ipv6', value: '1' } + - { name: 'net.ipv6.conf.default.disable_ipv6', value: '1' } + +- name: Ensure /root/.bashrc exists + file: + path: /root/.bashrc + state: touch + mode: '0644' + +- name: Add custom aliases and env to ~/.bashrc + blockinfile: + path: /root/.bashrc + marker: "# {mark} ANSIBLE MANAGED BLOCK: PROXMOX CUSTOM ALIASES" + block: | + # ——— Общие ——— + alias rm='rm -i' # Защита от случайного удаления + alias cp='cp -i' + alias mv='mv -i' + alias ls='ls --color=auto' + alias ll='ls -lah' + alias l.='ls -d .* --color=auto' + alias mount='mount | column -t' + alias h='history' + alias c='clear' + alias now='date +%T' + alias nowdate='date +%Y-%m-%d' + alias ping5='ping -c 5' + alias ports='ss -tulnp' # современная замена netstat + alias meminfo='free -h' + alias psmem='ps aux --sort=-%mem | head -11' + + # ——— Proxmox ——— + alias lxcstat='pct list' + alias qmstat='qm list' + alias ha='pvecm status && pve-ha-manager status' + alias stor='pvesm status' + + # ——— RAID / Диски ——— + alias storcli='/opt/MegaRAID/storcli/storcli64' + + # ——— Обновление ——— + alias aptup='apt update && apt list --upgradable' + + owner: root + mode: '0644' + +- name: Configure timezone + timezone: + name: "{{ timezone }}" + +- name: Generate system locale + locale_gen: + name: "{{ system_locale }}" + state: present + when: system_locale is defined and system_locale | length > 0 + +- name: Set default LANG in /etc/default/locale + lineinfile: + path: /etc/default/locale + regexp: '^LANG=' + line: "LANG={{ system_locale }}" + state: present + create: yes + when: system_locale is defined and system_locale | length > 0 + +- name: Ensure common dirs exist + file: + path: "{{ item }}" + state: directory + mode: '0755' + loop: + - /opt/scripts + - /etc/apt/keyrings + +- name: Ensure python3-requests is available (for custom scripts) + apt: + name: python3-requests + state: present + +- name: Ensure SSH directory exists + file: + path: /root/.ssh + state: directory + mode: '0700' + +- name: Add authorized SSH keys for root + authorized_key: + user: root + state: present + key: "{{ item }}" + loop: "{{ ssh_public_keys | default([]) }}" + +- name: Harden SSH configuration + lineinfile: + path: /etc/ssh/sshd_config + regexp: "^{{ item.key }}\\s" + line: "{{ item.key }} {{ item.value }}" + state: present + loop: + - { key: 'PasswordAuthentication', value: 'no' } + - { key: 'PermitRootLogin', value: 'prohibit-password' } + - { key: 'PubkeyAuthentication', value: 'yes' } + - { key: 'X11Forwarding', value: 'no' } + notify: restart ssh + +# ========== Node Exporter ========== +- name: Create node_exporter user + user: + name: node_exporter + system: yes + shell: /usr/sbin/nologin + create_home: no + +- name: Download node_exporter + get_url: + url: "https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz" + dest: /tmp/node_exporter-1.8.2.linux-amd64.tar.gz + checksum: "sha256:https://github.com/prometheus/node_exporter/releases/download/v1.8.2/sha256sums.txt" + mode: '0644' + timeout: 60 + +- name: Extract node_exporter + unarchive: + src: /tmp/node_exporter-1.8.2.linux-amd64.tar.gz + dest: /tmp/ + remote_src: yes + creates: /tmp/node_exporter-1.8.2.linux-amd64/node_exporter + +- name: Install node_exporter binary + copy: + src: /tmp/node_exporter-1.8.2.linux-amd64/node_exporter + dest: /usr/local/bin/node_exporter + owner: root + group: root + mode: '0755' + remote_src: yes + notify: restart node_exporter + +- name: Create textfile_collector directory + file: + path: /var/lib/node_exporter/textfile_collector + state: directory + owner: node_exporter + group: node_exporter + mode: '0755' + +- name: Deploy node_exporter systemd service + template: + src: node_exporter.service.j2 + dest: /etc/systemd/system/node_exporter.service + owner: root + group: root + mode: '0644' + notify: restart node_exporter + +# ========== storcli — проверка и сбор метрик (без установки) ========== +- name: Detect MegaRAID controller (lspci check) + command: lspci -d 1000: | grep -q "MegaRAID" + register: lspci_megaraid + ignore_errors: yes + changed_when: false + +- name: Check storcli binary exists + stat: + path: /opt/MegaRAID/storcli/storcli64 + register: storcli_bin + when: lspci_megaraid.rc == 0 + +- name: Ensure storcli symlink in PATH + file: + src: /opt/MegaRAID/storcli/storcli64 + dest: /usr/local/bin/storcli + state: link + force: yes + when: + - lspci_megaraid.rc == 0 + - storcli_bin.stat.exists + +- name: Verify storcli functionality + command: storcli /call show + register: storcli_test + changed_when: false + failed_when: + - storcli_test.rc != 0 + - "'Controller' not in storcli_test.stdout" + when: + - lspci_megaraid.rc == 0 + - storcli_bin.stat.exists + +- name: Set fact — storcli is available + set_fact: + storcli_available: true + when: + - lspci_megaraid.rc == 0 + - storcli_bin.stat.exists + - storcli_test is success + +- name: Set fact — storcli NOT available + set_fact: + storcli_available: false + when: not (lspci_megaraid.rc == 0 and storcli_bin.stat.exists) + +# Сбор метрик ТОЛЬКО при наличии storcli +- name: Deploy storcli metrics script + template: + src: storcli_metrics.sh.j2 + dest: /opt/scripts/storcli_metrics.sh + owner: root + group: root + mode: '0755' + when: storcli_available | default(false) + +- name: Deploy storcli_metrics systemd units + block: + - name: Create storcli_metrics.service + copy: + content: | + [Unit] + Description=Collect RAID/disk metrics via storcli + After=network.target + [Service] + Type=oneshot + ExecStart=/opt/scripts/storcli_metrics.sh + User=root + StandardOutput=journal + StandardError=journal + dest: /etc/systemd/system/storcli_metrics.service + owner: root + mode: '0644' + + - name: Create storcli_metrics.timer (every 5 min) + copy: + content: | + [Unit] + Description=Run storcli metrics collector every 5 minutes + Requires=storcli_metrics.service + [Timer] + OnBootSec=60 + OnUnitActiveSec=5m + AccuracySec=1s + [Install] + WantedBy=timers.target + dest: /etc/systemd/system/storcli_metrics.timer + owner: root + mode: '0644' + + - name: Enable & start storcli_metrics.timer + systemd: + name: storcli_metrics.timer + state: started + enabled: yes + daemon_reload: yes + when: storcli_available | default(false) + notify: restart storcli_metrics \ No newline at end of file diff --git a/roles/proxmox_base_setup/templates/node_exporter.service.j2 b/roles/proxmox_base_setup/templates/node_exporter.service.j2 new file mode 100644 index 0000000..b7eeedc --- /dev/null +++ b/roles/proxmox_base_setup/templates/node_exporter.service.j2 @@ -0,0 +1,31 @@ +[Unit] +Description=Prometheus Node Exporter +Documentation=https://github.com/prometheus/node_exporter +After=network.target + +[Service] +Type=simple +User=node_exporter +Group=node_exporter +ExecStart=/usr/local/bin/node_exporter \ + --collector.systemd \ + --collector.processes \ + --collector.cpu \ + --collector.meminfo \ + --collector.diskstats \ + --collector.netdev \ + --collector.filesystem \ + --collector.loadavg \ + --collector.time \ + --collector.textfile.directory=/var/lib/node_exporter/textfile_collector \ + --web.listen-address=0.0.0.0:9100 +Restart=always +RestartSec=5 +# Защита от атак и случайных ошибок +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/proxmox_base_setup/templates/storcli_metrics.sh.j2 b/roles/proxmox_base_setup/templates/storcli_metrics.sh.j2 new file mode 100644 index 0000000..807632c --- /dev/null +++ b/roles/proxmox_base_setup/templates/storcli_metrics.sh.j2 @@ -0,0 +1,64 @@ +#!/bin/bash + if (enc != "" && slot != "") { + # Вывести предыдущий диск + gsub(/ /, "_", model); + printf "storcli_disk_temp{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, (temp ~ /^[0-9]+$/ ? temp : 0) + printf "storcli_disk_state{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, state_num + printf "storcli_disk_media_error_count{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, media_err + printf "storcli_disk_other_error_count{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, other_err + } + enc=""; slot=""; model=""; temp=""; media_err=0; other_err=0; state_num=0 + } + /Enclosure Device ID:/ { enc = $4 } + /Slot Number:/ { slot = $3 } + /Device Id:/ { if (enc == "") enc = $3 } # fallback + /Model Number:/ { model = $3 } + /Drive Temperature/ { + match($0, /([0-9]+)C/); + if (RSTART) temp = substr($0, RSTART, RLENGTH-1) + } + /Firmware state:/ { + state = $3 + if (state ~ /Online/) state_num = 2 + else if (state ~ /Unconfigured.*Good/) state_num = 1 + else if (state ~ /Failed/) state_num = 3 + else if (state ~ /Rebuild/) state_num = 4 + else if (state ~ /Offline/) state_num = 0 + else state_num = -1 + } + /Media Error Count:/ { media_err = $4 } + /Other Error Count:/ { other_err = $4 } + END { + if (enc != "" && slot != "") { + gsub(/ /, "_", model); + printf "storcli_disk_temp{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, (temp ~ /^[0-9]+$/ ? temp : 0) + printf "storcli_disk_state{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, state_num + printf "storcli_disk_media_error_count{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, media_err + printf "storcli_disk_other_error_count{controller=\"%s\",enclosure=\"%s\",slot=\"%s\",model=\"%s\"} %s\n", c, enc, slot, model, other_err + } + } + ' c="$c") + + echo "$disks_info" >> "$OUT_FILE" + + # Виртуальные диски (массивы) + vds=$(storcli /c$c/vall show | awk ' + NR>8 && $1 ~ /^[0-9]+$/ { + vd = $1; state = $2; size = $3; + gsub(/,/,"", size); + # state: Optl=Online(1), Dgrd=Degraded(2), Offln=Offline(0), Ft=Failed(3) + state_num = 1; + if (state == "Dgrd") state_num = 2; + else if (state ~ /Offln|Pdgd/) state_num = 0; + else if (state == "Ft") state_num = 3; + printf "storcli_array_state{controller=\"%s\",vd=\"%s\",size=\"%s\"} %s\n", c, vd, size, state_num + } + ') + echo "$vds" >> "$OUT_FILE" +done + +# Атомарная замена +mv "$OUT_FILE" "$FINAL_FILE" +chmod 644 "$FINAL_FILE" + +echo "$(date -Iseconds) storcli metrics updated" >&2 \ No newline at end of file