-
Notifications
You must be signed in to change notification settings - Fork 125
/
pd.go
147 lines (125 loc) · 3.45 KB
/
pd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2024 PingCAP, Inc. Licensed under Apache-2.0.
package topology
import (
"encoding/json"
"fmt"
"sort"
"strings"
"github.com/pingcap/log"
"go.uber.org/zap"
"github.com/pingcap/tidb-dashboard/pkg/pd"
"github.com/pingcap/tidb-dashboard/util/distro"
"github.com/pingcap/tidb-dashboard/util/netutil"
)
func FetchPDTopology(pdClient *pd.Client) ([]PDInfo, error) {
nodes := make([]PDInfo, 0)
healthMap, err := fetchPDHealth(pdClient)
if err != nil {
return nil, err
}
data, err := pdClient.SendGetRequest("/members")
if err != nil {
return nil, err
}
ds := struct {
Count int `json:"count"`
Members []struct {
GitHash string `json:"git_hash"`
ClientUrls []string `json:"client_urls"`
DeployPath string `json:"deploy_path"`
BinaryVersion string `json:"binary_version"`
MemberID uint64 `json:"member_id"`
} `json:"members"`
}{}
err = json.Unmarshal(data, &ds)
if err != nil {
return nil, ErrInvalidTopologyData.Wrap(err, "%s members API unmarshal failed", distro.R().PD)
}
for _, ds := range ds.Members {
u := ds.ClientUrls[0]
hostname, port, err := netutil.ParseHostAndPortFromAddressURL(u)
if err != nil {
continue
}
ts, err := fetchPDStartTimestamp(pdClient)
if err != nil {
log.Warn(fmt.Sprintf("Failed to fetch %s start timestamp", distro.R().PD), zap.String("targetPdNode", u), zap.Error(err))
ts = 0
}
var storeStatus ComponentStatus
if _, ok := healthMap[ds.MemberID]; ok {
storeStatus = ComponentStatusUp
} else {
storeStatus = ComponentStatusUnreachable
}
nodes = append(nodes, PDInfo{
GitHash: ds.GitHash,
Version: ds.BinaryVersion,
IP: hostname,
Port: port,
DeployPath: ds.DeployPath,
Status: storeStatus,
StartTimestamp: ts,
})
}
sort.Slice(nodes, func(i, j int) bool {
if nodes[i].IP < nodes[j].IP {
return true
}
if nodes[i].IP > nodes[j].IP {
return false
}
return nodes[i].Port < nodes[j].Port
})
return nodes, nil
}
func fetchPDStartTimestamp(pdClient *pd.Client) (int64, error) {
data, err := pdClient.SendGetRequest("/status")
if err != nil {
return 0, err
}
ds := struct {
StartTimestamp int64 `json:"start_timestamp"`
}{}
err = json.Unmarshal(data, &ds)
if err != nil {
return 0, ErrInvalidTopologyData.Wrap(err, "%s status API unmarshal failed", distro.R().PD)
}
return ds.StartTimestamp, nil
}
func fetchPDHealth(pdClient *pd.Client) (map[uint64]struct{}, error) {
data, err := pdClient.SendGetRequest("/health")
if err != nil {
return nil, err
}
var healths []struct {
MemberID uint64 `json:"member_id"`
Health bool `json:"health"`
}
err = json.Unmarshal(data, &healths)
if err != nil {
return nil, ErrInvalidTopologyData.Wrap(err, "%s health API unmarshal failed", distro.R().PD)
}
memberHealth := map[uint64]struct{}{}
for _, v := range healths {
if v.Health {
memberHealth[v.MemberID] = struct{}{}
}
}
return memberHealth, nil
}
func fetchLocationLabels(pdClient *pd.Client) ([]string, error) {
data, err := pdClient.SendGetRequest("/config/replicate")
if err != nil {
return nil, err
}
var replicateConfig struct {
LocationLabels string `json:"location-labels"`
}
err = json.Unmarshal(data, &replicateConfig)
if err != nil {
return nil, ErrInvalidTopologyData.Wrap(err, "%s config/replicate API unmarshal failed", distro.R().PD)
}
labels := strings.Split(replicateConfig.LocationLabels, ",")
return labels, nil
}