22
33from pydantic import BaseModel , field_validator , model_validator
44
5+ from asyncflow .config .constants import EventDescription
56from asyncflow .schemas .event .injection import EventInjection
67from asyncflow .schemas .settings .simulation import SimulationSettings
78from asyncflow .schemas .topology .graph import TopologyGraph
89from asyncflow .schemas .workload .rqs_generator import RqsGenerator
9- from asyncflow .config .constants import EventDescription
1010
1111
1212class SimulationPayload (BaseModel ):
@@ -60,7 +60,7 @@ def ensure_components_ids_is_compatible(
6060 raise ValueError (msg )
6161
6262 return model
63-
63+
6464 @model_validator (mode = "after" ) # type: ignore[arg-type]
6565 def ensure_event_time_inside_simulation_horizon (
6666 cls , # noqa: N805
@@ -102,7 +102,7 @@ def ensure_event_time_inside_simulation_horizon(
102102 raise ValueError (msg )
103103
104104 return model
105-
105+
106106 @model_validator (mode = "after" ) # type: ignore[arg-type]
107107 def ensure_compatibility_event_kind_target_id (
108108 cls , # noqa: N805
@@ -115,88 +115,88 @@ def ensure_compatibility_event_kind_target_id(
115115 """
116116 if model .events is None :
117117 return model
118-
118+
119119 servers_list = model .topology_graph .nodes .servers
120120 edges_list = model .topology_graph .edges
121-
121+
122122 # We need just the Start or End kind because
123123 # we have a validation for the coherence between
124124 # the starting event kind and the finishing event kind
125125 server_kind = {EventDescription .SERVER_DOWN }
126126 edge_kind = {EventDescription .NETWORK_SPIKE_START }
127-
127+
128128 servers_ids = {server .id for server in servers_list }
129129 edges_ids = {edge .id for edge in edges_list }
130-
130+
131131 for event in model .events :
132132 if event .start .kind in server_kind and event .target_id not in servers_ids :
133- msg = (f"The event { event .event_id } regarding a server does not have "
133+ msg = (f"The event { event .event_id } regarding a server does not have "
134134 "a compatible target id" )
135135 raise ValueError (msg )
136- elif event .start .kind in edge_kind and event .target_id not in edges_ids :
136+ if event .start .kind in edge_kind and event .target_id not in edges_ids :
137137 msg = (f"The event { event .event_id } regarding an edge does not have "
138138 "a compatible target id" )
139139 raise ValueError (msg )
140-
141-
140+
141+
142142 return model
143-
144-
143+
144+
145145 @model_validator (mode = "after" ) # type: ignore[arg-type]
146146 def ensure_not_all_servers_are_down_simultaneously (
147147 cls , # noqa: N805
148148 model : "SimulationPayload" ,
149149 ) -> "SimulationPayload" :
150150 """
151151 We will not accept the condition to have all server down
152- at the same moment, always at least one server must be up
152+ at the same moment, always at least one server must be up
153153 and running
154154 """
155-
156155 if model .events is None :
157156 return model
158-
157+
159158 # First let us build a list of events related to the servers
160159 servers_list = model .topology_graph .nodes .servers
161160 servers_ids = {server .id for server in servers_list }
162161 server_events = [
163- event for event in model .events
162+ event for event in model .events
164163 if event .target_id in servers_ids
165164 ]
166-
165+
167166 # Helpers needed in the algorithm to define a specific ordering
168167 # procedure
169- START = "start"
170- END = "end"
171-
168+ start = "start"
169+ end = "end"
170+
172171 # Let us define a list of tuple as a timeline, this approach ensure
173172 # the possibility to have different servers going up or down at the
174173 # same time, a more elegant approach through an hashmap has been
175174 # considered however it would require an extra assumption that all
176175 # the times had to be different, we thought that this would be too
177176 # strict
178- timeline = []
177+ timeline : list [ tuple [ float , str , str ]] = []
179178 for event in server_events :
180- timeline .append ((event .start .t_start , START , event .target_id ))
181- timeline .append ((event .end .t_end , END , event .target_id ))
179+ timeline .append ((event .start .t_start , start , event .target_id ))
180+ timeline .append ((event .end .t_end , end , event .target_id ))
182181
183182 # Let us order the timeline by time if there are multiple events at the
184183 # same time process first the end type events
185- timeline .sort (key = lambda x : (x [0 ], x [1 ] == START ))
184+ timeline .sort (key = lambda x : (x [0 ], x [1 ] == start ))
186185
187- # Definition of a set to verify the condition that at least one server must
186+ # Definition of a set to verify the condition that at least one server must
188187 # be up
189188 server_down = set ()
190189 for time , kind , server_id in timeline :
191- if kind == START :
190+ if kind == end :
192191 server_down .discard (server_id )
193192 else : # "start"
194193 server_down .add (server_id )
195194 if len (server_down ) == len (servers_ids ):
196- raise ValueError (
197- f"At time { time :.6f} all servers are down; keep at least one up. "
195+ msg = (
196+ f"At time { time :.6f} all servers are down; keep at least one up"
198197 )
199-
198+ raise ValueError (msg )
199+
200200 return model
201-
201+
202202
0 commit comments