Skip to content

Commit 78a44bc

Browse files
authored
🐛: ensure negative values are set to 0 (ITISFoundation#2985)
1 parent d7ea6e8 commit 78a44bc

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

services/director-v2/src/simcore_service_director_v2/models/schemas/clusters.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,40 @@
2323
validator,
2424
)
2525
from pydantic.networks import AnyUrl
26-
from pydantic.types import ByteSize, PositiveFloat
26+
from pydantic.types import ByteSize, NonNegativeInt, PositiveFloat
2727

2828

2929
class WorkerMetrics(BaseModel):
30-
cpu: float = Field(..., description="consumed # of cpus")
30+
cpu: float = Field(..., description="consumed % of cpus")
3131
memory: ByteSize = Field(..., description="consumed memory")
3232
num_fds: int = Field(..., description="consumed file descriptors")
33-
ready: int = Field(..., description="# tasks ready to run")
34-
executing: int = Field(..., description="# tasks currently executing")
35-
in_flight: int = Field(..., description="# tasks currenntly waiting for data")
36-
in_memory: ByteSize = Field(..., description="result data still in memory")
33+
ready: NonNegativeInt = Field(..., description="# tasks ready to run")
34+
executing: NonNegativeInt = Field(..., description="# tasks currently executing")
35+
in_flight: NonNegativeInt = Field(..., description="# tasks waiting for data")
36+
in_memory: NonNegativeInt = Field(..., description="# tasks in worker memory")
37+
38+
39+
AvailableResources = DictModel[str, PositiveFloat]
40+
41+
42+
class UsedResources(DictModel[str, NonNegativeFloat]):
43+
@root_validator(pre=True)
44+
@classmethod
45+
def ensure_negative_value_is_zero(cls, values):
46+
# dasks adds/remove resource values and sometimes
47+
# they end up being negative instead of 0
48+
if v := values.get("__root__", {}):
49+
for res_key, res_value in v.items():
50+
if res_value < 0:
51+
v[res_key] = 0
52+
return values
3753

3854

3955
class Worker(BaseModel):
4056
id: str
4157
name: str
42-
resources: DictModel[str, PositiveFloat]
43-
used_resources: DictModel[str, NonNegativeFloat]
58+
resources: AvailableResources
59+
used_resources: UsedResources
4460
memory_limit: ByteSize
4561
metrics: WorkerMetrics
4662

services/director-v2/tests/unit/test_models_clusters.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
import pytest
55
from faker import Faker
6-
from pydantic import BaseModel
6+
from pydantic import BaseModel, parse_obj_as
77
from simcore_service_director_v2.models.schemas.clusters import (
8+
AvailableResources,
89
ClusterCreate,
910
ClusterPatch,
1011
Scheduler,
12+
UsedResources,
13+
Worker,
14+
WorkerMetrics,
1115
WorkersDict,
1216
)
1317

@@ -52,3 +56,27 @@ def test_scheduler_constructor_with_no_workers_has_correct_dict(faker: Faker):
5256
scheduler = Scheduler(status=faker.text(), workers=None)
5357
assert isinstance(scheduler.workers, WorkersDict)
5458
assert len(scheduler.workers) == 0
59+
60+
61+
def test_worker_constructor_corrects_negative_used_resources(faker: Faker):
62+
worker = Worker(
63+
id=faker.pyint(min_value=1),
64+
name=faker.name(),
65+
resources=parse_obj_as(AvailableResources, {}),
66+
used_resources=parse_obj_as(UsedResources, {"CPU": -0.0000234}),
67+
memory_limit=faker.pyint(min_value=1),
68+
metrics=parse_obj_as(
69+
WorkerMetrics,
70+
{
71+
"cpu": faker.pyfloat(min_value=0),
72+
"memory": faker.pyint(min_value=0),
73+
"num_fds": faker.pyint(),
74+
"ready": faker.pyint(min_value=0),
75+
"executing": faker.pyint(min_value=0),
76+
"in_flight": faker.pyint(min_value=0),
77+
"in_memory": faker.pyint(min_value=0),
78+
},
79+
),
80+
)
81+
assert worker
82+
assert worker.used_resources["CPU"] == 0

0 commit comments

Comments
 (0)