git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/netmap/filter.go (about) 1 package netmap 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" 9 ) 10 11 // mainFilterName is a name of the filter 12 // which points to the whole netmap. 13 const mainFilterName = "*" 14 15 const likeWildcard = "*" 16 17 // processFilters processes filters and returns error is any of them is invalid. 18 func (c *context) processFilters(p PlacementPolicy) error { 19 for i := range p.filters { 20 if err := c.processFilter(p.filters[i], true); err != nil { 21 return fmt.Errorf("process filter #%d (%s): %w", i, p.filters[i].GetName(), err) 22 } 23 } 24 25 return nil 26 } 27 28 func (c *context) processFilter(f netmap.Filter, top bool) error { 29 fName := f.GetName() 30 if fName == mainFilterName { 31 return fmt.Errorf("%w: '%s' is reserved", errInvalidFilterName, mainFilterName) 32 } 33 34 if top && fName == "" { 35 return errUnnamedTopFilter 36 } 37 38 if !top && fName != "" && c.processedFilters[fName] == nil { 39 return errFilterNotFound 40 } 41 42 inner := f.GetFilters() 43 44 switch op := f.GetOp(); op { 45 case netmap.AND, netmap.OR, netmap.NOT: 46 for i := range inner { 47 if err := c.processFilter(inner[i], false); err != nil { 48 return fmt.Errorf("process inner filter #%d: %w", i, err) 49 } 50 } 51 default: 52 if len(inner) != 0 { 53 return errNonEmptyFilters 54 } else if !top && fName != "" { // named reference 55 return nil 56 } 57 58 switch op { 59 case netmap.EQ, netmap.NE, netmap.LIKE: 60 case netmap.GT, netmap.GE, netmap.LT, netmap.LE: 61 val := f.GetValue() 62 n, err := strconv.ParseUint(val, 10, 64) 63 if err != nil { 64 return fmt.Errorf("%w: '%s'", errInvalidNumber, f.GetValue()) 65 } 66 67 c.numCache[val] = n 68 default: 69 return fmt.Errorf("%w: %s", errInvalidFilterOp, op) 70 } 71 } 72 73 if top { 74 c.processedFilters[fName] = &f 75 } 76 77 return nil 78 } 79 80 // match matches f against b. It returns no errors because 81 // filter should have been parsed during context creation 82 // and missing node properties are considered as a regular fail. 83 func (c *context) match(f *netmap.Filter, b NodeInfo) bool { 84 switch f.GetOp() { 85 case netmap.NOT: 86 inner := f.GetFilters() 87 fSub := &inner[0] 88 if name := inner[0].GetName(); name != "" { 89 fSub = c.processedFilters[name] 90 } 91 return !c.match(fSub, b) 92 case netmap.AND, netmap.OR: 93 inner := f.GetFilters() 94 for i := range inner { 95 fSub := &inner[i] 96 if name := inner[i].GetName(); name != "" { 97 fSub = c.processedFilters[name] 98 } 99 100 ok := c.match(fSub, b) 101 if ok == (f.GetOp() == netmap.OR) { 102 return ok 103 } 104 } 105 106 return f.GetOp() == netmap.AND 107 default: 108 return c.matchKeyValue(f, b) 109 } 110 } 111 112 func (c *context) matchKeyValue(f *netmap.Filter, b NodeInfo) bool { 113 switch op := f.GetOp(); op { 114 case netmap.EQ: 115 return b.Attribute(f.GetKey()) == f.GetValue() 116 case netmap.LIKE: 117 str, prefix := strings.CutPrefix(f.GetValue(), likeWildcard) 118 str, suffix := strings.CutSuffix(str, likeWildcard) 119 if prefix && suffix { 120 return strings.Contains(b.Attribute(f.GetKey()), str) 121 } 122 if prefix && !suffix { 123 return strings.HasSuffix(b.Attribute(f.GetKey()), str) 124 } 125 if !prefix && suffix { 126 return strings.HasPrefix(b.Attribute(f.GetKey()), str) 127 } 128 return b.Attribute(f.GetKey()) == f.GetValue() 129 case netmap.NE: 130 return b.Attribute(f.GetKey()) != f.GetValue() 131 default: 132 var attr uint64 133 134 switch f.GetKey() { 135 case attrPrice: 136 attr = b.Price() 137 case attrCapacity: 138 attr = b.capacity() 139 default: 140 var err error 141 142 attr, err = strconv.ParseUint(b.Attribute(f.GetKey()), 10, 64) 143 if err != nil { 144 // Note: because filters are somewhat independent from nodes attributes, 145 // We don't report an error here, and fail filter instead. 146 return false 147 } 148 } 149 150 switch op { 151 case netmap.GT: 152 return attr > c.numCache[f.GetValue()] 153 case netmap.GE: 154 return attr >= c.numCache[f.GetValue()] 155 case netmap.LT: 156 return attr < c.numCache[f.GetValue()] 157 case netmap.LE: 158 return attr <= c.numCache[f.GetValue()] 159 default: 160 // do nothing and return false 161 } 162 } 163 // will not happen if context was created from f (maybe panic?) 164 return false 165 }