/
rankgroups.go
153 lines (126 loc) · 3.7 KB
/
rankgroups.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
//
// (C) Copyright 2020-2022 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
package system
import (
"bytes"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"github.com/daos-stack/daos/src/control/lib/ranklist"
)
// RankGroups maps a set of ranks to string value (group).
type RankGroups map[string]*ranklist.RankSet
const replaceSep = " "
// Keys returns sorted group names.
//
// Sort first by number of ranks in grouping then by alphabetical order of
// group name.
func (rgs RankGroups) Keys() []string {
keys := make([]string, 0, len(rgs))
for key := range rgs {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
ci := rgs[keys[i]].Count()
cj := rgs[keys[j]].Count()
if ci == cj {
return keys[i] < keys[j]
}
return ci > cj
})
return keys
}
func (rgs RankGroups) String() string {
var buf bytes.Buffer
padding := 0
keys := rgs.Keys()
for _, key := range keys {
valStr := rgs[key].String()
if len(valStr) > padding {
padding = len(valStr)
}
}
for _, key := range rgs.Keys() {
fmt.Fprintf(&buf, "%*s: %s\n", padding, rgs[key], key)
}
return buf.String()
}
// FromMembers initializes groupings of ranks that are at a particular state
// from a slice of system members.
func (rgs RankGroups) FromMembers(members Members) error {
if rgs == nil || len(rgs) > 0 {
return errors.New("expecting non-nil empty rank groups")
}
ranksInState := make(map[MemberState]*bytes.Buffer)
ranksSeen := make(map[ranklist.Rank]struct{})
for _, m := range members {
if _, exists := ranksSeen[m.Rank]; exists {
return &ErrMemberExists{Rank: &m.Rank}
}
ranksSeen[m.Rank] = struct{}{}
if _, exists := ranksInState[m.State]; !exists {
ranksInState[m.State] = new(bytes.Buffer)
}
fmt.Fprintf(ranksInState[m.State], "%d,", m.Rank)
}
for state, ranksStrBuf := range ranksInState {
rankSet, err := ranklist.CreateRankSet(
strings.TrimSuffix(ranksStrBuf.String(), ","))
if err != nil {
return errors.WithMessage(err,
"generating groups of ranks at state")
}
rgs[state.String()] = rankSet
}
return nil
}
// FromMemberResults initializes groupings of ranks that had a particular result
// from a requested action, populated from a slice of system member results.
//
// Supplied fieldsep parameter is used to separate row field elements in the string that is used
// as the key for the rank groups.
func (rgs RankGroups) FromMemberResults(results MemberResults, fieldSep string) error {
if rgs == nil || len(rgs) > 0 {
return errors.New("expecting non-nil empty rank groups")
}
// Refuse to use separator pattern if it matches the replacement.
if fieldSep == replaceSep {
return errors.Errorf("illegal field separator %q", fieldSep)
}
ranksWithResult := make(map[string]*bytes.Buffer)
ranksSeen := make(map[ranklist.Rank]struct{})
for _, r := range results {
if _, exists := ranksSeen[r.Rank]; exists {
return errors.Wrap(&ErrMemberExists{Rank: &r.Rank},
"duplicate result for rank")
}
ranksSeen[r.Rank] = struct{}{}
msg := "OK"
if r.Errored {
msg = strings.Replace(r.Msg, fieldSep, replaceSep, -1)
}
if r.Action == "" {
return errors.Errorf(
"action field empty for rank %d result", r.Rank)
}
resStr := fmt.Sprintf("%s%s%s", r.Action, fieldSep, msg)
if _, exists := ranksWithResult[resStr]; !exists {
ranksWithResult[resStr] = new(bytes.Buffer)
}
fmt.Fprintf(ranksWithResult[resStr], "%d,", r.Rank)
}
for strResult, ranksStrBuf := range ranksWithResult {
rankSet, err := ranklist.CreateRankSet(
strings.TrimSuffix(ranksStrBuf.String(), ","))
if err != nil {
return errors.WithMessage(err,
"generating groups of ranks with same result")
}
rgs[strResult] = rankSet
}
return nil
}