Files
techsuppgetinfo/DCIManager6/dci6-support/server_info.go
2026-04-04 00:09:02 +08:00

601 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
// "bufio"
// "fmt"
// "io/ioutil"
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"syscall"
)
// ОС серверов Платформа и Локации
// (источник ОС)
// - Серверная платформа
// dmidecode -t system
func dmidecodeTSystem() (biosInfo, error) {
var biosInfo biosInfo
// нужно получить информацию из bios
// cat /sys/firmware/dmi/entries/1-0/raw
bi, err := os.ReadFile("/sys/firmware/dmi/entries/1-0/raw")
if err != nil {
err = fmt.Errorf("Ошибка получения dmi данных: %s", err)
}
biosInfo.Name = string(bi)
return biosInfo, nil
}
// - Операционная система
// cat /etc/os-release
// cat /etc/astra_version
// cat /etc/astra_license
func getOs() (operatingSystem, []error) {
var opsys operatingSystem
var errors []error
fOsInfo, err := os.ReadFile("/etc/os-release")
if err != nil {
err = fmt.Errorf("Ошибка чтения os-release: %s", err)
errors = append(errors, err)
} else {
osInfoSpl := strings.Split(string(fOsInfo), "\n")
for _, r := range osInfoSpl {
capt, _ := regexp.MatchString("PRETTY_NAME*", r)
if capt {
opsys.OsRelease = strings.Split(r, "=")[1]
break
}
}
}
_, err = os.Stat("/etc/astra_version")
if err != nil {
err = fmt.Errorf("ошибка чтения asrta_version")
errors = append(errors, err)
} else {
}
return opsys, errors
}
// - Пакеты
// apt list --installed
func getDebPackages() ([]installedPackage, error) {
// читаем /var/lib/dpkg/status
var debPackages []installedPackage
b_dpkgStatus, err := os.ReadFile("/var/lib/dpkg/status")
if err != nil {
err = fmt.Errorf("Ошибка чтения файла /var/lib/dpkg/status: %s", err)
return nil, err
}
dpkgStatus := strings.Split(string(b_dpkgStatus), "\n\n")
for _, ds := range dpkgStatus {
var debPackage installedPackage
if len(ds) == 0 {
continue
}
ds_s := strings.Split(ds, "\n")
for _, d := range ds_s {
if strings.Contains(d, "Package: ") {
debPackage.Name = strings.Split(d, ": ")[1]
}
if strings.Contains(d, "Version: ") {
debPackage.Version = strings.Split(d, ": ")[1]
}
}
debPackages = append(debPackages, debPackage)
}
return debPackages, err
}
func getRpmPackages() ([]installedPackage, error) {
var rpmPackages []installedPackage
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME};%{VERSION}\n")
output, err := cmd.Output()
if err != nil {
err = fmt.Errorf("Ошибка выпонения команды rpm -qa: %s", err)
return nil, err
}
scanner := bufio.NewScanner(strings.NewReader(string(output)))
for scanner.Scan() {
var instpack installedPackage
line := scanner.Text()
parts := strings.Split(line, ";")
instpack.Name = parts[0]
instpack.Version = parts[1]
rpmPackages = append(rpmPackages, instpack)
}
return rpmPackages, err
}
// - Размер диска и объем свободного места на диске
// lsblk
// df
// df -i
func getFsInfo() ([]fsInfo, error) {
var fsInfoList []fsInfo
fProcMounts, err := os.ReadFile("/proc/mounts")
if err != nil {
err = fmt.Errorf("Ошибка чтения файла /proc/mounts : %s", err)
return fsInfoList, err
}
for _, mp := range strings.Split(string(fProcMounts), "\n") {
var fi fsInfo
var sysStatfs syscall.Statfs_t
if len(mp) == 0 {
continue
}
mp_s := strings.Split(mp, " ")
fi.MountPointDevice = mp_s[0]
fi.MountPointOsPath = mp_s[1]
err = syscall.Statfs(fi.MountPointOsPath, &sysStatfs)
if err != nil {
log.Println(err)
} else {
fi.MountPointSize = sysStatfs.Blocks * uint64(sysStatfs.Bsize)
fi.MountPointFree = sysStatfs.Blocks * uint64(sysStatfs.Bfree)
}
fsInfoList = append(fsInfoList, fi)
// fmt.Printf("Dev: %s Path: %s Size: %d Free: %d \n", fi.MountPointDevice, fi.MountPointOsPath, fi.MountPointSize, fi.MountPointFree)
}
return fsInfoList, err
}
// - LA
// top -bn1 | head -n 1
// cat /proc/loadavg
func getLa() (loadAverage, error) {
var la loadAverage
laFile, err := os.ReadFile("/proc/loadavg")
if err != nil {
err = fmt.Errorf("файл proc loadavg недоступен %s", err)
return la, err
}
laFileSplit := strings.Split(string(laFile), " ")
la.OneMin = laFileSplit[0]
la.FiveMin = laFileSplit[1]
la.FifteenMin = laFileSplit[2]
la.Processes = laFileSplit[3]
return la, nil
}
// - Процессор
// cat /proc/cpuinfo
// lscpu
// func getCpu() (cpu, error) {
// var cpu cpu
// cpuFile, err := os.ReadFile("/proc/cpuinfo")
// if err != nil {
// err = fmt.Errorf("Проблема с доступом к файлу proc cpuinfo %s", err)
// return cpu, err
// }
// prevCpu := " "
// for _, line := range strings.Split(string(cpuFile), "\n") {
// matched, err := regexp.Match("model name", []byte(line))
// if err != nil {
// err = fmt.Errorf("Ошибка при оценке регулярного выражения %s", err)
// return cpu, err
// }
// if matched {
// cpuFound := strings.Split(line, ":")[1]
// if cpuFound != prevCpu {
// cpu.Model = append(cpu.Model, cpuFound)
// prevCpu = cpuFound
// }
// }
// }
// return cpu, nil
// }
func getCpuinfo() ([]cpu, error) {
var cpus []cpu
var err error
rawCpuinfo, err := os.ReadFile("/proc/cpuinfo")
var c cpu
var physId []string
physId = append(physId, "a")
for _, cpuLine := range strings.Split(string(rawCpuinfo), "\n") {
if len(strings.TrimSpace(cpuLine)) == 0 {
controlPoint := 0
for _, arrayIndex := range physId {
if c.PhysicalId == "" {
controlPoint = 1
}
if arrayIndex == c.PhysicalId {
controlPoint = 1
}
}
if controlPoint == 0 {
cpus = append(cpus, c)
physId = append(physId, c.PhysicalId)
c = cpu{}
} else {
c = cpu{}
}
} else {
if strings.Contains(cpuLine, "vendor_id") {
c.VendorId = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "cpu family") {
c.CpuFamily = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "model") && !strings.Contains(cpuLine, "name") {
c.Model = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "model name") {
c.ModelName = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "stepping") {
c.Stepping = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "microcode") {
c.Microcode = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "cpu MHz") {
c.CpuMhz = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "cache size") {
c.CacheSize = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "physical id") {
c.PhysicalId = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "cpu cores") {
c.CpuCores = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "flags") {
c.Flags = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "bugs") {
c.Bugs = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "bogomips") {
c.Bogomips = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "TLB size") {
c.TlbSize = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "clflush size") {
c.ClflushSize = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "cache_alignment") {
c.CacheAlignment = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "address size") {
c.AddressSize = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
if strings.Contains(cpuLine, "power management") {
c.PowerManagement = strings.TrimSpace(strings.Split(cpuLine, ":")[1])
}
}
}
return cpus, err
}
// - Объём и тип ОЗУ, объём свободной памяти в моменте
// vmstat -s
// free
// /proc/meminfo
func getRam() (ram, error) {
var ram ram
ramFile, err := os.ReadFile("/proc/meminfo")
if err != nil {
err = fmt.Errorf("Проблема с доступом к файлу proc meminfo %s", err)
return ram, err
}
for _, line := range strings.Split(string(ramFile), "\n") {
matched, _ := regexp.Match("MemTotal", []byte(line))
if matched {
ram.MemTotal = strings.Trim(strings.Split(line, ":")[1], " ")
}
matched, _ = regexp.Match("MemFree", []byte(line))
if matched {
ram.MemFree = strings.Trim(strings.Split(line, ":")[1], " ")
}
matched, _ = regexp.Match("Available", []byte(line))
if matched {
ram.MemAvailable = strings.Trim(strings.Split(line, ":")[1], " ")
}
}
return ram, nil
}
// - Uptime сервера
// uptime
func getUptime() (uptime, error) {
var uptime uptime
uptimeFile, err := os.ReadFile("/proc/uptime")
if err != nil {
err = fmt.Errorf("Ошибка досутпа к файлу proc uptime: %s", err)
return uptime, err
}
uptime.WorkSeconds = strings.Split(string(uptimeFile), " ")[0]
return uptime, nil
}
// - NTP (nrp ntpsec chrony)
// Запущен ли сервис, выполнялась ли синхронизация
// date -R на всех серверах инфраструктуры
func getTimeService() (timeService, []error) {
var timeService timeService
var errors []error
cmd := exec.Command("timedatectl", "show", "--property=NTPSynchronized")
output, err := cmd.Output()
if err != nil {
err = fmt.Errorf("Ошибка получения данных timedatectl: %s", err)
errors = append(errors, err)
err = nil
}
timeService.TimeSync = strings.Trim(strings.Split(string(output), "=")[1], "\n")
dir, err := os.ReadDir("/run/systemd/units")
if err != nil {
err = fmt.Errorf("Ошибка при получении содержимого run systemd units : %s", err)
errors = append(errors, err)
err = nil
} else {
for _, entry := range dir {
matchedChrony, _ := regexp.Match("chrony", []byte(entry.Name()))
if matchedChrony {
timeService.TimeService = strings.Split(entry.Name(), ":")[1]
break
}
matchedNtp, _ := regexp.Match("ntp", []byte(entry.Name()))
if matchedNtp {
timeService.TimeService = strings.Split(entry.Name(), ":")[1]
break
}
timeService.TimeService = "nothing"
}
}
return timeService, errors
}
// - Стандартный сбор ошибок
// journalctl -xe -p4 -o json-pretty
// dmesg -T --level=err,warn
// dmesg \| grep -i -E 'error\|failed\|critical\|bug\|panic' или dmesg --level=warn,err
// journalctl \| grep -i -E 'error\|failed\|critical\|bug\|panic' или journalctl -xe -p 4 можно since "1 day ago" -o json-pretty
// journalctl -xe -p 4 --since "1 day ago" -o json-pretty
func getJournalctl() ([]journalctlEntry, error) {
var jList []journalctlEntry
cmd := exec.Command("journalctl", "-xe", "-p 4", "--since", "1 day ago", "-o", "json-pretty")
output, err := cmd.Output()
if err != nil {
err = fmt.Errorf("Ошибка выполнения команды journalctl -xe -p 4 --since '5 minutes ago' -o json-pretty : %s", err)
return jList, err
}
outputSplited := strings.Split(string(output), "}\n{")
for _, event := range outputSplited {
var je journalctlEntry
if len(event) == 0 {
continue
}
event = strings.Trim(event, "{}")
eventFull := "{" + event + "}"
// err = json.Unmarshal([]byte(eventFull), &je)
buf := bytes.NewBuffer([]byte(eventFull))
err = json.NewDecoder(buf).Decode(&je)
if err != nil {
err = fmt.Errorf("Ошибка десериализации данных: %s в итерации %s", err, eventFull)
return jList, err
}
jList = append(jList, je)
}
return jList, nil
}
// - Порты
// nmap от Платформы к Локациям и наоборот
// оставим пока, двигаем в самый низ, как реализовать пока не представляю. Нужны проверки портов с учетом фаерволов
// возможно это будет выполнение в лоб команды docker exec -it dci_back dcissh -a "nmap"
// пока вместо открытых портов проверим запущен ли фаервол
func getFirewalls() (firewalls, error) {
var fwl firewalls
units, err := os.ReadDir("/run/systemd/units")
if err != nil {
err = fmt.Errorf("Ошибка при получении содержимого run systemd units : %s", err)
return fwl, err
} else {
for _, unit := range units {
ufw, _ := regexp.Match("ufw", []byte(unit.Name()))
if ufw {
fwl.Ufw = "running"
}
nftables, _ := regexp.Match("nftables", []byte(unit.Name()))
if nftables {
fwl.Nftables = "running"
}
iptables, _ := regexp.Match("iptables", []byte(unit.Name()))
if iptables {
fwl.Iptables = "running"
}
firewalld, _ := regexp.Match("firewalld", []byte(unit.Name()))
if firewalld {
fwl.Firewalld = "running"
}
}
}
return fwl, nil
}
// - Настройки сети
// NetworkManager
// настройки интерфейсов
// etc/hosts
// etc/resolv.conf
func getNetworkConf() (networkConfig, []error) {
var networkConfig networkConfig
var errors []error
interfaces, err := net.Interfaces()
if err != nil {
err = fmt.Errorf("Ошибка при получении сетевых интерфейсов: %s", err)
errors = append(errors, err)
err = nil
} else {
for _, interf := range interfaces {
var netInterface netInterface
netInterface.Device = interf.Name
netInterface.Flags = interf.Flags.String()
netInterface.HwAddr = interf.HardwareAddr.String()
adrs, _ := interf.Addrs()
for _, adr := range adrs {
netInterface.IpAddr = append(netInterface.IpAddr, adr.String())
}
netInterface.Index = strconv.Itoa(interf.Index)
netInterface.MTU = strconv.Itoa(interf.MTU)
networkConfig.NetInterfaces = append(networkConfig.NetInterfaces, netInterface)
// fmt.Printf("interf: %v\n", interf)
}
}
units, err := os.ReadDir("/run/systemd/units")
if err != nil {
err = fmt.Errorf("Ошибка при получении содержимого run systemd units : %s", err)
errors = append(errors, err)
err = nil
} else {
for _, unit := range units {
// Полчить статус NetworkManager
matched, _ := regexp.Match("NetworkManager.services", []byte(unit.Name()))
if matched {
networkConfig.NetworkManager = "running"
}
// Полчить статус networking
matchedNetworking, _ := regexp.Match("networking", []byte(unit.Name()))
if matchedNetworking {
networkConfig.Networking = "running"
}
}
}
nmConns, err := os.ReadDir("/run/NetworkManager/system-connections")
if err != nil {
err = fmt.Errorf("Ошибка при получении содержимого run NetworkManager")
errors = append(errors, err)
err = nil
} else {
for _, nmConn := range nmConns {
networkConfig.NetworkManagerConn = append(networkConfig.NetworkManagerConn, nmConn.Name())
}
}
// Содержимое /etc/hosts
linesByte, err := os.ReadFile("/etc/hosts")
if err != nil {
err = fmt.Errorf("Ошибка при получении содержимого etc hosts : %s", err)
errors = append(errors, err)
err = nil
} else {
lines := strings.Split(string(linesByte), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "#") {
continue
} else {
networkConfig.EtcHosts = append(networkConfig.EtcHosts, line)
}
}
}
return networkConfig, errors
}
// Возможность доступа к основным ресурсам в internet
// docker-registry.ispsystem.com, download.docker.com — для доступа к Docker;
// download.ispsystem.com — для обновления и установки платформы;
// license6.ispsystem.com — для проверки лицензий;
// metricreport.ispsystem.net — для работы сервера метрик.
func getInternetResource(ispUrl string) (internetResource, error) {
var ir internetResource
var outErr error
ipList, err := net.LookupIP(ispUrl)
if err != nil {
return ir, err
}
ir.Name = ispUrl
var i []string
for _, ip := range ipList {
i = append(i, ip.String())
}
ir.IpList = strings.Join(i, ",")
httpUrl := "http://" + ispUrl
httpsUrl := "https://" + ispUrl
resp, err := http.Get(httpUrl)
if err != nil {
outErr = fmt.Errorf("Ошибка HTTP %s :: %s. ", httpUrl, err)
ir.Http = err.Error()
} else {
ir.Http = strconv.Itoa(resp.StatusCode)
}
resp, err = http.Get(httpsUrl)
if err != nil {
outErr = fmt.Errorf("%s Ошибка HTTPS %s :: %s", outErr, httpUrl, err)
ir.Https = err.Error()
} else {
ir.Https = strconv.Itoa(resp.StatusCode)
}
return ir, outErr
}
// 2026-02.5 получить историю. просто прочитаем файл с историей bash у root
func getRootHistory() ([]rootHistoryCommand, error) {
var rootHistoryCommands []rootHistoryCommand
rawHistory, err := os.ReadFile("/root/.bash_history")
if err != nil {
err = fmt.Errorf("Ошибка при открытии файла /root/.bash_history : %s", err.Error())
return nil, err
}
i := 0
execTime := ""
for _, line := range strings.Split(string(rawHistory), "\n") {
var c rootHistoryCommand
if strings.HasPrefix(line, "#") {
execTime = strings.TrimLeft(line, "#")
} else {
c.Id = i
i += 1
c.ExecutionTime = execTime
c.Command = line
execTime = ""
rootHistoryCommands = append(rootHistoryCommands, c)
}
}
return rootHistoryCommands, err
}