11from abc import abstractmethod
2- from typing import Protocol
2+ from typing import Any , Final , Protocol
33
44from models_library .projects_nodes_io import NodeID
5- from pydantic import BaseModel , ByteSize , Field
5+ from pydantic import (
6+ BaseModel ,
7+ ByteSize ,
8+ Field ,
9+ NonNegativeFloat ,
10+ root_validator ,
11+ validator ,
12+ )
13+
14+ _EPSILON : Final [NonNegativeFloat ] = 1e-16
615
716
817class SDiskUsageProtocol (Protocol ):
@@ -27,28 +36,70 @@ def percent(self) -> float:
2736 ...
2837
2938
39+ def _get_percent (used : float , total : float ) -> float :
40+ return round (used * 100 / (total + _EPSILON ), 2 )
41+
42+
3043class DiskUsage (BaseModel ):
3144 used : ByteSize = Field (description = "used space" )
3245 free : ByteSize = Field (description = "remaining space" )
3346
3447 total : ByteSize = Field (description = "total space = free + used" )
35- used_percent : float = Field (
48+ used_percent : NonNegativeFloat = Field (
3649 gte = 0.00 ,
3750 lte = 100.00 ,
3851 description = "Percent of used space relative to the total space" ,
3952 )
4053
54+ @validator ("free" )
55+ @classmethod
56+ def _free_positive (cls , v : float ) -> float :
57+ if v < 0 :
58+ msg = f"free={ v } cannot be a negative value"
59+ raise ValueError (msg )
60+ return v
61+
62+ @validator ("used" )
63+ @classmethod
64+ def _used_positive (cls , v : float ) -> float :
65+ if v < 0 :
66+ msg = f"used={ v } cannot be a negative value"
67+ raise ValueError (msg )
68+ return v
69+
70+ @root_validator (pre = True )
71+ @classmethod
72+ def _check_total (cls , values : dict [str , Any ]) -> dict [str , Any ]:
73+ total = values ["total" ]
74+ free = values ["free" ]
75+ used = values ["used" ]
76+ if total != free + used :
77+ msg = f"{ total = } is different than the sum of { free = } +{ used = } => sum={ free + used } "
78+ raise ValueError (msg )
79+ return values
80+
4181 @classmethod
4282 def from_ps_util_disk_usage (
4383 cls , ps_util_disk_usage : SDiskUsageProtocol
4484 ) -> "DiskUsage" :
4585 total = ps_util_disk_usage .free + ps_util_disk_usage .used
46- used_percent = round (ps_util_disk_usage .used * 100 / total , 2 )
4786 return cls (
4887 used = ByteSize (ps_util_disk_usage .used ),
4988 free = ByteSize (ps_util_disk_usage .free ),
5089 total = ByteSize (total ),
51- used_percent = used_percent ,
90+ used_percent = _get_percent (ps_util_disk_usage .used , total ),
91+ )
92+
93+ @classmethod
94+ def from_efs_guardian (
95+ cls , used : NonNegativeFloat , total : NonNegativeFloat
96+ ) -> "DiskUsage" :
97+ free = total - used
98+ return cls (
99+ used = ByteSize (used ),
100+ free = ByteSize (free ),
101+ total = ByteSize (total ),
102+ used_percent = _get_percent (used , total ),
52103 )
53104
54105 def __hash__ (self ):
0 commit comments