-
Notifications
You must be signed in to change notification settings - Fork 297
/
finding.go
210 lines (189 loc) · 6.09 KB
/
finding.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//
// (C) Copyright 2022 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
package checker
import (
"fmt"
"strconv"
"strings"
chkpb "github.com/daos-stack/daos/src/control/common/proto/chk"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
type (
Finding struct {
chkpb.CheckReport
}
reportObject uint
)
const (
unkObj reportObject = iota
poolObj
contObj
engObj
otherObj
)
func (ro reportObject) String() string {
return map[reportObject]string{
unkObj: "unknown",
poolObj: "pool",
contObj: "container",
engObj: "target",
otherObj: "other",
}[ro]
}
func (f *Finding) HasChoice(action chkpb.CheckInconsistAction) bool {
for _, a := range f.ActChoices {
if a == action {
return true
}
}
return false
}
func (f *Finding) ValidChoicesString() string {
if len(f.ActChoices) == 0 {
return "no valid action choices (already repaired?)"
}
var actions []string
for _, a := range f.ActChoices {
actions = append(actions, strconv.Itoa(int(a)))
}
return strings.Join(actions, ",")
}
func NewFinding(report *chkpb.CheckReport) *Finding {
if report == nil {
return nil
}
f := new(Finding)
proto.Merge(&f.CheckReport, report)
return f
}
// descAction attempts to generate a human-readable description of the
// action that may be taken for the given finding.
func descAction(class chkpb.CheckInconsistClass, action chkpb.CheckInconsistAction, details ...string) string {
var ro reportObject
switch {
case class >= chkpb.CheckInconsistClass_CIC_POOL_LESS_SVC_WITH_QUORUM && class <= chkpb.CheckInconsistClass_CIC_POOL_BAD_LABEL:
ro = poolObj
case class >= chkpb.CheckInconsistClass_CIC_CONT_NONEXIST_ON_PS && class <= chkpb.CheckInconsistClass_CIC_CONT_BAD_LABEL:
ro = contObj
case class >= chkpb.CheckInconsistClass_CIC_ENGINE_NONEXIST_IN_MAP && class <= chkpb.CheckInconsistClass_CIC_ENGINE_HAS_NO_STORAGE:
ro = engObj
default:
ro = otherObj
}
// Create a map of details. If a detail is not found by
// the expected index, then the default is an empty string.
detMap := make(map[int]string)
for i, det := range details {
detMap[i] = det
}
switch action {
case chkpb.CheckInconsistAction_CIA_IGNORE:
return fmt.Sprintf("Ignore the %s finding", ro)
case chkpb.CheckInconsistAction_CIA_DISCARD:
return fmt.Sprintf("Discard the %s", ro)
case chkpb.CheckInconsistAction_CIA_READD:
return fmt.Sprintf("Re-add the %s", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_MS:
switch ro {
case poolObj:
switch class {
case chkpb.CheckInconsistClass_CIC_POOL_NONEXIST_ON_MS:
return fmt.Sprintf("Reclaim the orphaned pool storage for %s", detMap[0])
case chkpb.CheckInconsistClass_CIC_POOL_BAD_LABEL:
return fmt.Sprintf("Reset the pool property using the MS label for %s", detMap[0])
}
return fmt.Sprintf("Trust the MS pool entry for %s", detMap[0])
}
return fmt.Sprintf("Trust the MS %s entry", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_PS:
switch ro {
case poolObj:
switch class {
case chkpb.CheckInconsistClass_CIC_POOL_NONEXIST_ON_MS:
return fmt.Sprintf("Recreate the MS pool entry for %s", detMap[0])
case chkpb.CheckInconsistClass_CIC_POOL_NONEXIST_ON_ENGINE:
return fmt.Sprintf("Remove the MS pool entry for %s", detMap[0])
case chkpb.CheckInconsistClass_CIC_POOL_BAD_LABEL:
return fmt.Sprintf("Update the MS label to use the pool property value for %s", detMap[0])
}
case contObj:
switch class {
case chkpb.CheckInconsistClass_CIC_CONT_BAD_LABEL:
return fmt.Sprintf("Reset the container property using the PS label for %s", detMap[1])
}
}
return fmt.Sprintf("Trust the PS %s entry", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_TARGET:
switch ro {
case contObj:
switch class {
case chkpb.CheckInconsistClass_CIC_CONT_BAD_LABEL:
return fmt.Sprintf("Update the CS label to use the container property value for %s", detMap[1])
}
}
return fmt.Sprintf("Trust the %s result", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_MAJORITY:
return fmt.Sprintf("Trust the majority of the %s results", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_LATEST:
return fmt.Sprintf("Trust the latest %s result", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_OLDEST:
return fmt.Sprintf("Trust the oldest %s result", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_EC_PARITY:
return fmt.Sprintf("Trust the parity of the %s results", ro)
case chkpb.CheckInconsistAction_CIA_TRUST_EC_DATA:
return fmt.Sprintf("Trust the data of the %s results", ro)
default:
return fmt.Sprintf("%s: %s (details: %+v)", ro, action, details)
}
}
// Trim leading/trailing whitespace from all string fields in the
// checker report.
func trimProtoSpaces(pm proto.Message) {
pr := pm.ProtoReflect()
pr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
if fd.Kind() == protoreflect.StringKind {
if fd.IsList() {
for i := 0; i < v.List().Len(); i++ {
v.List().Set(i, protoreflect.ValueOf(strings.TrimSpace(v.List().Get(i).String())))
}
} else {
pr.Set(fd, protoreflect.ValueOf(strings.TrimSpace(v.String())))
}
}
return true
})
}
func AnnotateFinding(f *Finding) *Finding {
if f == nil {
return nil
}
trimProtoSpaces(f)
// Pad out the list of details as necessary to match
// the length of the action list.
if len(f.ActChoices) > 0 && len(f.ActDetails) != len(f.ActChoices) {
for i := len(f.ActDetails); i < len(f.ActChoices); i++ {
f.ActDetails = append(f.ActDetails, "")
}
}
// If the report does not specify a list of informative messages to
// accompany the list of actions, then create one.
if len(f.ActMsgs) == 0 {
if len(f.ActChoices) > 0 {
f.ActMsgs = make([]string, len(f.ActChoices))
for i, act := range f.ActChoices {
f.ActMsgs[i] = descAction(f.Class, act, append([]string{f.PoolUuid, f.ContUuid}, f.ActDetails...)...)
}
} else {
f.ActMsgs = make([]string, 1)
f.ActMsgs[0] = descAction(f.Class, f.Action, append([]string{f.PoolUuid, f.ContUuid}, f.ActDetails...)...)
}
}
if len(f.Msg) == 0 {
f.Msg = fmt.Sprintf("Inconsistency found: %s (details: %+v)", f.Class, f.ActDetails)
}
return f
}