Skip to content

Commit 9324076

Browse files
committed
feat: gpu traits filtering
Signed-off-by: Dario Tranchitella <[email protected]>
1 parent eb8ec79 commit 9324076

File tree

10 files changed

+453
-40
lines changed

10 files changed

+453
-40
lines changed

apis/nodecore/v1alpha1/k8slice_selector.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
package v1alpha1
1616

17-
import "k8s.io/klog/v2"
17+
import (
18+
"k8s.io/klog/v2"
19+
)
1820

1921
// K8SliceSelector is the selector for a K8Slice.
2022
type K8SliceSelector struct {
@@ -32,6 +34,9 @@ type K8SliceSelector struct {
3234

3335
// StorageFilter is the Storage filter of the K8SliceSelector.
3436
StorageFilter *ResourceQuantityFilter `json:"storageFilter,omitempty"`
37+
38+
//GPUFilters is the advanced GPU filter of the K8SliceSelector
39+
GPUFilters []GPUFieldSelector `json:"gpuFilters,omitempty"`
3540
}
3641

3742
// GetFlavorTypeSelector returns the type of the Flavor.

apis/nodecore/v1alpha1/selector.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,30 @@ const (
3434
// FilterType is the type of filter that can be applied to a resource quantity.
3535
type FilterType string
3636

37+
// BooleanFilter is a filter that can be applied to a boolean value.
38+
type BooleanFilter struct {
39+
Data runtime.RawExtension `json:"data"`
40+
}
41+
42+
type NumberFilter struct {
43+
// Name indicates the type of the filter
44+
Name FilterType `json:"name"`
45+
// Filter data
46+
Data runtime.RawExtension `json:"data"`
47+
}
48+
49+
type NumberMatchSelector struct {
50+
// Value is the value to match
51+
Value float64 `json:"value"`
52+
}
53+
54+
type NumberRangeSelector struct {
55+
// Min is the minimum value of the range
56+
Min *float64 `json:"min,omitempty"`
57+
// Max is the maximum value of the range
58+
Max *float64 `json:"max,omitempty"`
59+
}
60+
3761
// StringFilter is a filter that can be applied to a string.
3862
type StringFilter struct {
3963
// Name indicates the type of the filter
@@ -76,6 +100,28 @@ type ResourceRangeSelector struct {
76100
Max *resource.Quantity `json:"max,omitempty"`
77101
}
78102

103+
type SelectorName string
104+
105+
var (
106+
ResourceRangeSelectorName = SelectorName("ResourceRangeSelector")
107+
ResourceMatchSelectorName = SelectorName("ResourceMatchSelector")
108+
StringRangeSelectorName = SelectorName("StringRangeSelector")
109+
StringFilterSelectorName = SelectorName("StringFilter")
110+
BooleanFilterSelectorName = SelectorName("BooleanFilter")
111+
NumberRangeSelectorName = SelectorName("NumberRangeFilter")
112+
NumberMatchSelectorName = SelectorName("NumberMatchFilter")
113+
)
114+
115+
type GPUFieldSelector struct {
116+
// Field is the GPU field the filter should evaluate.
117+
Field string `json:"field"`
118+
// Selector is the name of the concrete Selector to enforce for the given data.
119+
//+kubebuilder:validation:Enum=ResourceRangeSelector;ResourceMatchSelector;StringRangeSelector;StringFilter;BooleanFilter;NumberRangeFilter;NumberFilter
120+
Selector SelectorName `json:"filter"`
121+
// Data contains the raw data for the given filter.
122+
Data runtime.RawExtension `json:"data"`
123+
}
124+
79125
// ParseResourceQuantityFilter parses a ResourceQuantityFilter into a FilterType and the corresponding filter data.
80126
// It also provides a set of validation rules for the filter data.
81127
// Particularly for the ResourceRangeSelector, it checks that at least one of min or max is set and that min is less than max if both are set.

apis/nodecore/v1alpha1/zz_generated.deepcopy.go

Lines changed: 95 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/gorilla/mux v1.8.1
77
github.com/liqotech/liqo v1.0.0
88
github.com/rabbitmq/amqp091-go v1.10.0
9+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
910
k8s.io/api v0.32.1
1011
k8s.io/apimachinery v0.32.1
1112
k8s.io/client-go v0.32.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
161161
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
162162
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
163163
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
164+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
165+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
164166
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
165167
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
166168
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=

pkg/rear-manager/allocation_controller.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,14 +1101,8 @@ func computeK8SliceCharacteristics(origin *nodecorev1alpha1.K8SliceCharacteristi
11011101
switch {
11021102
case part.Gpu != nil && origin != nil:
11031103
// Gpu is present in the origin and in the partition
1104-
newGpuCores := origin.Gpu.Cores.DeepCopy()
1105-
newGpuCores.Sub(part.Gpu.Cores)
1106-
newGpuMemory := origin.Gpu.Memory.DeepCopy()
1107-
newGpuMemory.Sub(part.Gpu.Memory)
1108-
11091104
newGpu := origin.DeepCopy()
1110-
newGpu.Gpu.Cores = newGpuCores
1111-
newGpu.Gpu.Memory = newGpuMemory
1105+
newGpu.Gpu.Count = 0
11121106

11131107
return newGpu.Gpu
11141108
case part.Gpu == nil && origin.Gpu != nil:
@@ -1207,7 +1201,6 @@ func reduceFlavorAvailability(ctx context.Context, flavor *nodecorev1alpha1.Flav
12071201
}
12081202

12091203
newFlavor := resourceforge.ForgeFlavorFromRef(flavor, newFlavorType)
1210-
12111204
// Create new Flavor
12121205
if err := r.Create(ctx, newFlavor); err != nil {
12131206
klog.Errorf("Error when creating Flavor %s: %v", newFlavor.Name, err)

pkg/utils/common/common.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ import (
1818
"encoding/json"
1919
"fmt"
2020
"regexp"
21+
"slices"
2122

23+
"golang.org/x/exp/maps"
2224
"k8s.io/apimachinery/pkg/api/resource"
25+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26+
"k8s.io/apimachinery/pkg/runtime"
2327
"k8s.io/klog/v2"
2428

2529
advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1"
@@ -144,6 +148,56 @@ func filterResourceQuantityFilter(selectorValue resource.Quantity, filter models
144148
return true
145149
}
146150

151+
func filterNumberFilter(selectorValue float64, filter models.NumberFilter) bool {
152+
switch filter.Name {
153+
case models.MatchFilter:
154+
// Parse the filter to a match filter
155+
var matchFilter models.NumberMatchFilter
156+
err := json.Unmarshal(filter.Data, &matchFilter)
157+
if err != nil {
158+
klog.Errorf("Error unmarshalling match filter: %v", err)
159+
return false
160+
}
161+
// Check if the selector value matches the filter value
162+
if selectorValue != matchFilter.Value {
163+
klog.Infof("Match Filter: %f - Selector Value: %s", matchFilter.Value, selectorValue)
164+
return false
165+
}
166+
case models.RangeFilter:
167+
// Parse the filter to a range filter
168+
var rangeFilter models.NumberRangeFilter
169+
err := json.Unmarshal(filter.Data, &rangeFilter)
170+
if err != nil {
171+
klog.Errorf("Error unmarshalling range filter: %v", err)
172+
return false
173+
}
174+
// Check if the selector value is within the range
175+
// If the rangeFilter.Min exists check if the selector value is greater or equal to it
176+
if rangeFilter.Min != nil {
177+
if selectorValue < *rangeFilter.Min {
178+
klog.Infof("Range Filter: %v-%v - Selector Value: %v", rangeFilter.Min, rangeFilter.Max, selectorValue)
179+
return false
180+
}
181+
}
182+
// If the rangeFilter.Max exists check if the selector value is less or equal to it
183+
if rangeFilter.Max != nil {
184+
if selectorValue > *rangeFilter.Max {
185+
klog.Infof("Range Filter: %v-%v - Selector Value: %v", rangeFilter.Min, rangeFilter.Max, selectorValue)
186+
return false
187+
}
188+
}
189+
default:
190+
klog.Errorf("Filter name %s not supported", filter.Name)
191+
return false
192+
}
193+
194+
return true
195+
}
196+
197+
func filterBooleanFilter(selectorValue bool, filter models.BooleanFilter) bool {
198+
return selectorValue != filter.Condition
199+
}
200+
147201
func filterStringFilter(selectorValue string, filter models.StringFilter) bool {
148202
switch filter.Name {
149203
case models.MatchFilter:
@@ -230,6 +284,66 @@ func filterFlavorK8Slice(k8SliceSelector *models.K8SliceSelector, flavorTypeK8Sl
230284
}
231285
}
232286

287+
if gpuTraits := flavorTypeK8SliceCR.Characteristics.Gpu; gpuTraits != nil {
288+
copied := gpuTraits.DeepCopy()
289+
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(copied)
290+
if err != nil {
291+
klog.Errorf("Error converting gpu traits to unstructured: %v", err)
292+
return false
293+
}
294+
295+
keys := maps.Keys(k8SliceSelector.GPUFields)
296+
slices.Sort(keys)
297+
298+
for _, key := range keys {
299+
filter := k8SliceSelector.GPUFields[key]
300+
301+
switch f := filter.(type) {
302+
case models.NumberFilter:
303+
v, found, _ := unstructured.NestedNumberAsFloat64(u, key)
304+
if !found {
305+
return false
306+
}
307+
308+
if !filterNumberFilter(v, f) {
309+
return false
310+
}
311+
case models.BooleanFilter:
312+
v, found, _ := unstructured.NestedBool(u, key)
313+
if !found {
314+
return false
315+
}
316+
317+
if !filterBooleanFilter(v, f) {
318+
return false
319+
}
320+
case models.StringFilter:
321+
v, found, _ := unstructured.NestedString(u, key)
322+
if !found {
323+
return false
324+
}
325+
326+
if !filterStringFilter(v, f) {
327+
return false
328+
}
329+
case models.ResourceQuantityFilter:
330+
v, found, _ := unstructured.NestedString(u, key)
331+
if !found {
332+
return false
333+
}
334+
335+
qty, parseErr := resource.ParseQuantity(v)
336+
if parseErr != nil {
337+
return false
338+
}
339+
340+
if !filterResourceQuantityFilter(qty, f) {
341+
return false
342+
}
343+
}
344+
}
345+
}
346+
233347
return true
234348
}
235349

0 commit comments

Comments
 (0)