Speed matchchecker

This commit is contained in:
Magnus Root 2026-04-24 09:05:22 +03:00
parent a658bf8c05
commit 8a37c62e63

View file

@ -9,6 +9,7 @@ import (
"regexp"
"sort"
"strings"
"sync"
)
// ── Типы ─────────────────────────────────────────────────────────────────────
@ -173,7 +174,7 @@ func mcCheckClient(client, hostname string) mcClientCheck {
func mcCheckContainer(client string, num int, angiePorts []int, hostname string) mcContainerCheck {
result := mcContainerCheck{Number: num}
fullName := fmt.Sprintf("ptaf_%s_%03d", client, num)
fullName := mcBuildContainerName(client, hostname, num)
label := fmt.Sprintf("контейнер %03d", num)
fmt.Printf(" ┌─ Контейнер %03d\n", num)
@ -365,31 +366,99 @@ func mcIsValidJSON(filePath string) bool {
return json.Unmarshal(data, &js) == nil
}
func mcIsContainerRunning(name string) bool {
cmd := exec.Command("docker", "ps", "--format", "{{.Names}}")
output, err := cmd.Output()
if err != nil {
return false
// mcBuildContainerName строит имя Docker-контейнера по новому формату:
// {ClientTitle}_{AZN}_{prefix}{nodeN}_a{agentN}
//
// Пример:
//
// hostname: PTAF-TB-docker-AZ3-HW-node01, client: PSB_CFA, agent: 1
// → PSB_CFA_AZ3_hn01_a001
//
// hostname: PTAF-TB-docker-AZ1-node04, client: ITAR-TASS, agent: 1
// → ITAR-TASS_AZ1_n04_a001
func mcBuildContainerName(client, hostname string, agentNum int) string {
// Извлекаем AZN: ищем AZ + цифры в hostname
azRe := regexp.MustCompile(`AZ(\d+)`)
azMatch := azRe.FindStringSubmatch(hostname)
azPart := "AZ0"
if len(azMatch) > 1 {
azPart = "AZ" + azMatch[1]
}
for _, line := range strings.Split(string(output), "\n") {
if strings.TrimSpace(line) == name {
return true
// Определяем префикс ноды: HW → hn, иначе n
nodePrefix := "n"
if strings.Contains(hostname, "-HW-") {
nodePrefix = "hn"
}
// Извлекаем номер ноды: цифры после "node"
nodeRe := regexp.MustCompile(`node(\d+)`)
nodeMatch := nodeRe.FindStringSubmatch(hostname)
nodePart := "00"
if len(nodeMatch) > 1 {
// Берём последние 2 цифры с ведущим нулём
num := nodeMatch[1]
if len(num) >= 2 {
nodePart = num[len(num)-2:]
} else {
nodePart = fmt.Sprintf("%02s", num)
}
}
return false
return fmt.Sprintf("%s_%s_%s%s_a%03d", client, azPart, nodePrefix, nodePart, agentNum)
}
// ── Кэш системных вызовов ────────────────────────────────────────────────────
// docker ps и ss вызываются один раз на всё выполнение и кэшируются.
// Это критично при большом количестве контейнеров и портов (51+ портов на контейнер).
var (
mcRunningContainers map[string]bool // имя контейнера → запущен
mcListeningPorts map[int]bool // порт → слушается
mcCacheOnce sync.Once
)
func mcInitCache() {
mcCacheOnce.Do(func() {
mcRunningContainers = make(map[string]bool)
mcListeningPorts = make(map[int]bool)
// Один вызов docker ps — все запущенные контейнеры
if out, err := exec.Command("docker", "ps", "--format", "{{.Names}}").Output(); err == nil {
for _, line := range strings.Split(string(out), "\n") {
name := strings.TrimSpace(line)
if name != "" {
mcRunningContainers[name] = true
}
}
}
// Один вызов ss — все слушающие порты
out, err := exec.Command("ss", "-tlnp").Output()
if err != nil {
out, err = exec.Command("netstat", "-tlnp").Output()
}
if err == nil {
re := regexp.MustCompile(`127\.0\.0\.1:(\d+)`)
for _, m := range re.FindAllStringSubmatch(string(out), -1) {
if len(m) > 1 {
var p int
fmt.Sscanf(m[1], "%d", &p)
mcListeningPorts[p] = true
}
}
}
})
}
func mcIsContainerRunning(name string) bool {
mcInitCache()
return mcRunningContainers[name]
}
func mcIsPortListening(port int) bool {
cmd := exec.Command("ss", "-tlnp")
output, err := cmd.Output()
if err != nil {
cmd = exec.Command("netstat", "-tlnp")
output, err = cmd.Output()
if err != nil {
return false
}
}
return strings.Contains(string(output), fmt.Sprintf("127.0.0.1:%d", port))
mcInitCache()
return mcListeningPorts[port]
}
func mcContainsInt(slice []int, val int) bool {