steinsgateted commented on code in PR #691:
URL: https://github.com/apache/yunikorn-core/pull/691#discussion_r1382529593
##########
pkg/common/resources/resources_test.go:
##########
@@ -1701,3 +1701,29 @@ func TestIsEmpty(t *testing.T) {
})
}
}
+
+func TestResource_DominantResource(t *testing.T) {
+ tests := []struct {
+ name string
+ used *Resource
+ capacity *Resource
+ wantName string
+ }{
+ {"nil receiver", nil, Zero, ""},
+ {"nil cap", Zero, nil, ""},
+ {"zero cap", NewResourceFromMap(map[string]Quantity{"A": 10}),
Zero, ""},
+ {"over cap", NewResourceFromMap(map[string]Quantity{"A": 20}),
NewResourceFromMap(map[string]Quantity{"A": 10}), "A"},
+ {"zero usage exist",
NewResourceFromMap(map[string]Quantity{"A": 0}),
NewResourceFromMap(map[string]Quantity{"A": 10}), "A"},
+ {"usage not in cap",
NewResourceFromMap(map[string]Quantity{"B": 10}),
NewResourceFromMap(map[string]Quantity{"A": 10}), ""},
+ {"multiple usages", NewResourceFromMap(map[string]Quantity{"B":
10, "A": 10}), NewResourceFromMap(map[string]Quantity{"A": 10, "B": 20}), "A"},
+ {"0 usage with 0 cap",
NewResourceFromMap(map[string]Quantity{"A": 0}),
NewResourceFromMap(map[string]Quantity{"A": 0}), "A"},
Review Comment:
Change it to "" to pass the test:
`{"0 usage with 0 cap", NewResourceFromMap(map[string]Quantity{"A": 0}),
NewResourceFromMap(map[string]Quantity{"A": 0}), ""},`
Or we handle the 0/0 situation in `DominantResourceType`
##########
pkg/webservice/handlers_test.go:
##########
@@ -678,36 +681,126 @@ func TestGetNodesUtilJSON(t *testing.T) {
allocs = []*objects.Allocation{objects.NewAllocation("alloc-2-uuid",
node2ID, ask2)}
err = partition.AddNode(node2, allocs)
assert.NilError(t, err, "add node to partition should not have failed")
+ err = partition.AddNode(node3, nil)
+ assert.NilError(t, err, "add node to partition should not have failed")
+
+ // two nodes advertise memory: must show up in the list
+ result := getNodesUtilJSON(partition, siCommon.Memory)
+ subResult := result.NodesUtil
+ assert.Equal(t, result.ResourceType, siCommon.Memory)
+ assert.Equal(t, subResult[2].NumOfNodes, int64(1))
+ assert.Equal(t, subResult[4].NumOfNodes, int64(1))
+ assert.Equal(t, subResult[2].NodeNames[0], node2ID)
+ assert.Equal(t, subResult[4].NodeNames[0], node1ID)
+
+ // three nodes advertise cpu: must show up in the list
+ result = getNodesUtilJSON(partition, siCommon.CPU)
+ subResult = result.NodesUtil
+ assert.Equal(t, result.ResourceType, siCommon.CPU)
+ assert.Equal(t, subResult[0].NumOfNodes, int64(1))
+ assert.Equal(t, subResult[0].NodeNames[0], node3ID)
+ assert.Equal(t, subResult[2].NumOfNodes, int64(1))
+ assert.Equal(t, subResult[2].NodeNames[0], node1ID)
+ assert.Equal(t, subResult[4].NumOfNodes, int64(1))
+ assert.Equal(t, subResult[4].NodeNames[0], node2ID)
+
+ // one node advertise GPU: must show up in the list
+ result = getNodesUtilJSON(partition, "GPU")
+ subResult = result.NodesUtil
+ assert.Equal(t, result.ResourceType, "GPU")
+ assert.Equal(t, subResult[4].NumOfNodes, int64(1))
+ assert.Equal(t, subResult[4].NodeNames[0], node2ID)
+
+ result = getNodesUtilJSON(partition, "non-exist")
+ subResult = result.NodesUtil
+ assert.Equal(t, result.ResourceType, "non-exist")
+ assert.Equal(t, subResult[0].NumOfNodes, int64(0))
+ assert.Equal(t, len(subResult[0].NodeNames), 0)
+}
+
+func TestGetNodeUtilisation(t *testing.T) {
+ NewWebApp(&scheduler.ClusterContext{}, nil)
+
+ // var req *http.Request
+ req, err := http.NewRequest("GET", "/ws/v1/scheduler/node-utilization",
strings.NewReader(""))
+ assert.NilError(t, err, "Get node utilisation Handler request failed")
+ req = req.WithContext(context.TODO())
+ resp := &MockResponseWriter{}
+
+ getNodeUtilisation(resp, req)
+ var errInfo dao.YAPIError
+ err = json.Unmarshal(resp.outputBytes, &errInfo)
+ assert.NilError(t, err, "getNodeUtilisation should have returned and
error")
+
+ partition := setup(t, configDefault, 1)
+ utilisation := &dao.NodesUtilDAOInfo{}
+ err = json.Unmarshal(resp.outputBytes, utilisation)
+ assert.NilError(t, err, "getNodeUtilisation should have returned an
empty object")
+ assert.Equal(t, utilisation.ResourceType, "", "unexpected type
returned")
+ assert.Equal(t, len(utilisation.NodesUtil), 0, "no nodes should be
returned")
+ assert.Assert(t, confirmNodeCount(utilisation.NodesUtil, 0),
"unexpected number of nodes returned should be 0")
+
+ // create test nodes
+ nodeRes :=
resources.NewResourceFromMap(map[string]resources.Quantity{"first":
10}).ToProto()
+ nodeRes2 :=
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 10,
"second": 5}).ToProto()
+ node1ID := "node-1"
+ node1 := objects.NewNode(&si.NodeInfo{NodeID: node1ID,
SchedulableResource: nodeRes})
+ node2ID := "node-2"
+ node2 := objects.NewNode(&si.NodeInfo{NodeID: node2ID,
SchedulableResource: nodeRes2})
+
+ err = partition.AddNode(node1, nil)
+ assert.NilError(t, err, "add node to partition should not have failed")
+ err = partition.AddNode(node2, nil)
+ assert.NilError(t, err, "add node to partition should not have failed")
// get nodes utilization
- res1 := getNodesUtilJSON(partition, siCommon.Memory)
- res2 := getNodesUtilJSON(partition, siCommon.CPU)
- res3 := getNodesUtilJSON(partition, "GPU")
- resNon := getNodesUtilJSON(partition, "non-exist")
- subres1 := res1.NodesUtil
- subres2 := res2.NodesUtil
- subres3 := res3.NodesUtil
- subresNon := resNon.NodesUtil
-
- assert.Equal(t, res1.ResourceType, siCommon.Memory)
- assert.Equal(t, subres1[2].NumOfNodes, int64(1))
- assert.Equal(t, subres1[4].NumOfNodes, int64(1))
- assert.Equal(t, subres1[2].NodeNames[0], node2ID)
- assert.Equal(t, subres1[4].NodeNames[0], node1ID)
-
- assert.Equal(t, res2.ResourceType, siCommon.CPU)
- assert.Equal(t, subres2[2].NumOfNodes, int64(1))
- assert.Equal(t, subres2[4].NumOfNodes, int64(1))
- assert.Equal(t, subres2[2].NodeNames[0], node1ID)
- assert.Equal(t, subres2[4].NodeNames[0], node2ID)
-
- assert.Equal(t, res3.ResourceType, "GPU")
- assert.Equal(t, subres3[4].NumOfNodes, int64(1))
- assert.Equal(t, subres3[4].NodeNames[0], node2ID)
-
- assert.Equal(t, resNon.ResourceType, "non-exist")
- assert.Equal(t, subresNon[0].NumOfNodes, int64(0))
- assert.Equal(t, len(subresNon[0].NodeNames), 0)
+ getNodeUtilisation(resp, req)
+ utilisation = &dao.NodesUtilDAOInfo{}
+ err = json.Unmarshal(resp.outputBytes, utilisation)
+ assert.NilError(t, err, "getNodeUtilisation should have returned an
object")
+ assert.Equal(t, utilisation.ResourceType, "", "unexpected type
returned")
+ assert.Equal(t, len(utilisation.NodesUtil), 10, "empty usage:
unexpected bucket count returned")
+ assert.Assert(t, confirmNodeCount(utilisation.NodesUtil, 0),
"unexpected number of nodes returned should be 2")
Review Comment:
nit:"unexpected number of nodes returned should be 0"
##########
pkg/common/resources/resources_test.go:
##########
@@ -1701,3 +1701,29 @@ func TestIsEmpty(t *testing.T) {
})
}
}
+
+func TestResource_DominantResource(t *testing.T) {
+ tests := []struct {
+ name string
+ used *Resource
+ capacity *Resource
+ wantName string
+ }{
+ {"nil receiver", nil, Zero, ""},
+ {"nil cap", Zero, nil, ""},
+ {"zero cap", NewResourceFromMap(map[string]Quantity{"A": 10}),
Zero, ""},
+ {"over cap", NewResourceFromMap(map[string]Quantity{"A": 20}),
NewResourceFromMap(map[string]Quantity{"A": 10}), "A"},
+ {"zero usage exist",
NewResourceFromMap(map[string]Quantity{"A": 0}),
NewResourceFromMap(map[string]Quantity{"A": 10}), "A"},
+ {"usage not in cap",
NewResourceFromMap(map[string]Quantity{"B": 10}),
NewResourceFromMap(map[string]Quantity{"A": 10}), ""},
+ {"multiple usages", NewResourceFromMap(map[string]Quantity{"B":
10, "A": 10}), NewResourceFromMap(map[string]Quantity{"A": 10, "B": 20}), "A"},
Review Comment:
nit:`{"multiple usages", NewResourceFromMap(map[string]Quantity{"A": 10,
"B": 10}), NewResourceFromMap(map[string]Quantity{"A": 10, "B": 20}), "A"},`
##########
pkg/common/resources/resources.go:
##########
@@ -993,3 +993,39 @@ func CalculateAbsUsedCapacity(capacity, used *Resource)
*Resource {
}
return absResource
}
+
+// DominantResourceType calculates the most used resource type based on the
ratio of used compared to
+// the capacity. If a capacity type is set to 0 assume full usage.
+// Dominant type should be calculated with queue usage and capacity. Queue
capacities should never
+// contain 0 values when there is a usage also, however in the root queue this
could happen. If the
+// last node reporting that resource was removed but not everything has been
updated.
+// immediately
+// Ignores resources types that are used but not defined in the capacity.
+func (r *Resource) DominantResourceType(capacity *Resource) string {
+ if r == nil || capacity == nil {
+ return ""
+ }
+ var div float64
+ dominant := ""
+ for name, usedVal := range r.Resources {
+ capVal, ok := capacity.Resources[name]
+ if !ok {
+ log.Log(log.Resources).Debug("missing resource in
dominant calculation",
+ zap.String("missing resource", name))
+ continue
+ }
+ // calculate the ratio between usage and capacity
+ // filter out 0 just to be safe should never happen: make it
fully used
+ if capVal == 0 {
+ capVal = usedVal
+ }
+ // ratio should be somewhere between 0 and 1
+ // if we have exactly the same use the latest one
+ temp := float64(usedVal) / float64(capVal)
+ if temp >= div {
+ div = float64(usedVal) / float64(capVal)
Review Comment:
nit:`div = temp`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]