-
Notifications
You must be signed in to change notification settings - Fork 4
/
lib.go
349 lines (301 loc) · 9.09 KB
/
lib.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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
package lib
import (
"strings"
export "github.com/redhat-best-practices-for-k8s/graphsolver-exports"
l2lib "github.com/redhat-best-practices-for-k8s/l2discovery-exports"
"github.com/sirupsen/logrus"
)
var GlobalConfig = configObject{}
type configObject struct {
// tag for problem variables
variableTagToInt map[string][]int
// problem definition
problems map[string][][][]int
// map storing solutions
solutions map[string]*[][]int
l2Config export.L2Info
}
func (config *configObject) SetL2Config(param export.L2Info) {
config.l2Config = param
}
func (config *configObject) GetSolutions() map[string]*[][]int {
return config.solutions
}
func (config *configObject) InitProblem(name string, problemStatement [][][]int, problemVariablesMapping []int) {
if GlobalConfig.problems == nil {
GlobalConfig.problems = make(map[string][][][]int)
}
GlobalConfig.problems[name] = problemStatement
if GlobalConfig.variableTagToInt == nil {
GlobalConfig.variableTagToInt = make(map[string][]int)
}
GlobalConfig.variableTagToInt[name] = problemVariablesMapping
if GlobalConfig.solutions == nil {
GlobalConfig.solutions = make(map[string]*[][]int)
}
GlobalConfig.solutions[name] = &[][]int{}
}
func (config *configObject) Run(problemName string) {
// get all the vertices in the graph
L := GetAllGraphVertices(len(config.l2Config.GetPtpIfList()))
// Running solver
PermutationsWithConstraints(config.l2Config, GlobalConfig.problems[problemName], L, 0, len(GlobalConfig.problems[problemName]), len(L), true, GlobalConfig.solutions[problemName])
}
// Prints the all solutions for each scenario, if found
func (config configObject) PrintSolutions(all bool) {
for index, solutions := range config.solutions {
if len(*solutions) == 0 {
logrus.Infof("Solution for %s problem does not exists", index)
continue
}
logrus.Infof("Solutions for %s problem", index)
for _, solution := range *solutions {
PrintSolution(config.l2Config, solution)
logrus.Infof("---")
if !all {
break
}
}
}
}
// Prints the first solution for each scenario, if found
func (config configObject) PrintFirstSolution() {
config.PrintSolutions(false)
}
// Prints the all solutions for each scenario, if found
func (config configObject) PrintAllSolutions() {
config.PrintSolutions(true)
}
// list of Algorithm functions with zero params
type AlgoFunction0 int
// See applyStep
const (
// same node
StepNil AlgoFunction0 = iota
)
// list of Algorithm function with 1 params
type AlgoFunction1 int
// See applyStep
const (
// same node
StepIsPTP AlgoFunction1 = iota
StepIsWPCNic
)
// list of Algorithm function with 2 params
type AlgoFunction2 int
// See applyStep
const (
StepSameLan2 AlgoFunction2 = iota
StepSameNic
StepSameNode
StepDifferentNode
StepDifferentNic
)
const WPCNICSubsystemID = "E810-XXV-4T"
// list of Algorithm function with 3 params
type AlgoFunction3 int
// See applyStep
const (
StepSameLan3 AlgoFunction3 = iota
)
// Signature for algorithm functions with 0 params
type ConfigFunc0 func() bool
// Signature for algorithm functions with 1 params
type ConfigFunc1 func(export.L2Info, int) bool
// Signature for algorithm functions with 2 params
type ConfigFunc2 func(export.L2Info, int, int) bool
// Signature for algorithm functions with 3 params
type ConfigFunc3 func(export.L2Info, int, int, int) bool
type ConfigFunc func(export.L2Info, []int) bool
type Algorithm struct {
// number of interfaces to solve
IfCount int
// Function to run algo
TestSolution ConfigFunc
}
// Print a single solution
func PrintSolution(config export.L2Info, p []int) {
i := 0
for _, aIf := range p {
logrus.Infof("p%d= %s", i, config.GetPtpIfList()[aIf])
i++
}
}
func GetAllGraphVertices(count int) (l []int) {
for i := 0; i < count; i++ {
l = append(l, i)
}
return l
}
// Recursive solver function. Creates a set of permutations and applies contraints at each step to
// reduce the solution graph and speed up execution
func PermutationsWithConstraints(config export.L2Info, algo [][][]int, l []int, s, e, n int, result bool, solutions *[][]int) {
if !result || len(l) < e {
return
}
if s == e {
temp := make([]int, 0)
temp = append(temp, l...)
temp = temp[0:e]
logrus.Tracef("Permutations %v -- %v", temp, result)
*solutions = append(*solutions, temp)
} else {
// Backtracking loop
for i := s; i < n; i++ {
l[i], l[s] = l[s], l[i]
result = applyStep(config, algo[s], l[0:e])
PermutationsWithConstraints(config, algo, l, s+1, e, n, result, solutions)
l[i], l[s] = l[s], l[i]
}
}
}
// check if an interface is receiving GM
func IsPTP(config export.L2Info, aInterface *l2lib.PtpIf) bool {
for _, aIf := range config.GetPortsGettingPTP() {
if aInterface.IfClusterIndex == aIf.IfClusterIndex {
return true
}
}
return false
}
// Checks that an if an interface receives ptp frames
func IsPTPWrapper(config export.L2Info, if1 int) bool {
return IsPTP(config, config.GetPtpIfList()[if1])
}
// Checks if 2 interfaces are on the same node
func SameNode(if1, if2 *l2lib.PtpIf) bool {
return if1.NodeName == if2.NodeName
}
// algo Wrapper for SameNode
func SameNodeWrapper(config export.L2Info, if1, if2 int) bool {
return SameNode(config.GetPtpIfList()[if1], config.GetPtpIfList()[if2])
}
// algo wrapper for !SameNode
func DifferentNodeWrapper(config export.L2Info, if1, if2 int) bool {
return !SameNode(config.GetPtpIfList()[if1], config.GetPtpIfList()[if2])
}
// Algo wrapper for !SameNic
func DifferentNicWrapper(config export.L2Info, if1, if2 int) bool {
return !SameNic(config.GetPtpIfList()[if1], config.GetPtpIfList()[if2])
}
// Checks if 3 interfaces are connected to the same LAN
func SameLan3(config export.L2Info, if1, if2, if3 int, lans *[][]int) bool {
if SameNode(config.GetPtpIfList()[if1], config.GetPtpIfList()[if2]) ||
SameNode(config.GetPtpIfList()[if1], config.GetPtpIfList()[if3]) {
return false
}
for _, Lan := range *lans {
if1Present := false
if2Present := false
if3Present := false
for _, aIf := range Lan {
if aIf == if1 {
if1Present = true
}
if aIf == if2 {
if2Present = true
}
if aIf == if3 {
if3Present = true
}
}
if if1Present && if2Present && if3Present {
return true
}
}
return false
}
// algo wrapper for SameLan3
func SameLan3Wrapper(config export.L2Info, if1, if2, if3 int) bool {
return SameLan3(config, if1, if2, if3, config.GetLANs())
}
// Checks if 2 interfaces are connected to the same LAN
func SameLan2(config export.L2Info, if1, if2 int, lans *[][]int) bool {
if SameNode(config.GetPtpIfList()[if1], config.GetPtpIfList()[if2]) {
return false
}
for _, Lan := range *lans {
if1Present := false
if2Present := false
for _, aIf := range Lan {
if aIf == if1 {
if1Present = true
}
if aIf == if2 {
if2Present = true
}
}
if if1Present && if2Present {
return true
}
}
return false
}
// wrapper for SameLan2
func SameLan2Wrapper(config export.L2Info, if1, if2 int) bool {
return SameLan2(config, if1, if2, config.GetLANs())
}
// Determines if 2 interfaces (ports) belong to the same NIC
func SameNic(ifaceName1, ifaceName2 *l2lib.PtpIf) bool {
if ifaceName1.IfClusterIndex.NodeName != ifaceName2.IfClusterIndex.NodeName {
return false
}
return ifaceName1.IfPci.Device != "" && ifaceName1.IfPci.Device == ifaceName2.IfPci.Device
}
// wrapper for SameNic
func SameNicWrapper(config export.L2Info, if1, if2 int) bool {
return SameNic(config.GetPtpIfList()[if1], config.GetPtpIfList()[if2])
}
// Determines if the NIC is intel WPC NIC
func isWpcNic(ifaceName1 *l2lib.PtpIf) bool {
return strings.Contains(ifaceName1.IfPci.Subsystem, WPCNICSubsystemID)
}
// wrapper for isWpcNic
func isWPCNicWrapper(config export.L2Info, if1 int) bool {
return isWpcNic(config.GetPtpIfList()[if1])
}
// wrapper for nil algo function
func NilWrapper() bool {
return true
}
// Applies a single step (constraint) in the backtracking algorithm
func applyStep(config export.L2Info, step [][]int, combinations []int) bool {
type paramNum int
const (
NoParam paramNum = iota
OneParam
TwoParams
ThreeParams
FourParams
)
// mapping table between :
// AlgoFunction0, AlgoFunction1, AlgoFunction2, AlgoFunction3 and
// function wrappers
var AlgoCode0 [1]ConfigFunc0
AlgoCode0[StepNil] = NilWrapper
var AlgoCode1 [2]ConfigFunc1
AlgoCode1[StepIsPTP] = IsPTPWrapper
AlgoCode1[StepIsWPCNic] = isWPCNicWrapper
var AlgoCode2 [5]ConfigFunc2
AlgoCode2[StepSameLan2] = SameLan2Wrapper
AlgoCode2[StepSameNic] = SameNicWrapper
AlgoCode2[StepSameNode] = SameNodeWrapper
AlgoCode2[StepDifferentNode] = DifferentNodeWrapper
AlgoCode2[StepDifferentNic] = DifferentNicWrapper
var AlgoCode3 [1]ConfigFunc3
AlgoCode3[StepSameLan3] = SameLan3Wrapper
result := true
for _, test := range step {
switch test[1] {
case int(NoParam):
result = result && AlgoCode0[test[0]]()
case int(OneParam):
result = result && AlgoCode1[test[0]](config, combinations[test[2]])
case int(TwoParams):
result = result && AlgoCode2[test[0]](config, combinations[test[2]], combinations[test[3]])
case int(ThreeParams):
result = result && AlgoCode3[test[0]](config, combinations[test[2]], combinations[test[3]], combinations[test[4]])
}
}
return result
}