Skip to content

Commit 5599847

Browse files
committed
WIP: properly stop scenarios
1 parent 4a0821e commit 5599847

File tree

3 files changed

+41
-19
lines changed

3 files changed

+41
-19
lines changed

src/warnet/cli/scenarios.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def list():
2626

2727
@scenarios.command(context_settings={"ignore_unknown_options": True})
2828
@click.argument("scenario", type=str)
29-
@click.argument("additional_args", nargs=-1, type=click.UNPROCESSED)
29+
@click.argument("additional_args", nargs=-1, type=click.UNPROCESSED)
3030
@click.option("--network", default="warnet", show_default=True)
3131
def run(scenario, network, additional_args):
3232
"""
@@ -54,15 +54,14 @@ def active(network: str = "warnet"):
5454

5555

5656
@scenarios.command()
57-
@click.argument("scenario", type=str)
58-
@click.argument("name", type=str)
57+
@click.argument("pid", type=int)
5958
@click.option("--network", default="warnet", show_default=True)
60-
def stop(scenario, network):
59+
def stop(pid: int, network: str = "warnet"):
6160
"""
62-
Stop <scenario> from running on <--network>
61+
Stop scenario with <pid> from running on <--network>
6362
"""
6463
try:
65-
params = {"scenario": scenario, "network": network}
64+
params = {"pid": pid, "network": network}
6665
res = rpc_call("stop_scenario", params)
6766
print(res)
6867
except Exception as e:

src/warnet/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,9 @@ def wrapper(*args, **kwargs):
350350
return wrapper
351351

352352

353-
def save_running_scenario(scenario: str, process, config_dir: Path):
353+
def save_running_scenario(scenario: str, pid: int, config_dir: Path):
354354
with open(config_dir / RUNNING_PROC_FILE, "a") as file:
355-
file.write(f"{scenario}\t{process.pid}\n")
355+
file.write(f"{scenario}\t{pid}\n")
356356

357357

358358
def load_running_scenarios(config_dir: Path) -> Dict[str, int]:

src/warnet/warnetd.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import signal
77
import subprocess
88
import sys
9+
import time
910
import threading
1011
from collections import defaultdict
1112
from datetime import datetime
@@ -23,6 +24,7 @@
2324
compose_down,
2425
)
2526
from warnet.utils import (
27+
exponential_backoff,
2628
gen_config_dir,
2729
save_running_scenario,
2830
load_running_scenarios,
@@ -158,7 +160,7 @@ def run(scenario: str, additional_args: List[str], network: str = "warnet") -> s
158160
[sys.executable, scenario_path] + additional_args + [f"--network={network}"]
159161
)
160162
logger.debug(f"Running {run_cmd}")
161-
process = subprocess.Popen(run_cmd, shell=False)
163+
process = subprocess.Popen(run_cmd, shell=False, preexec_fn=os.setsid)
162164

163165
save_running_scenario(scenario, process, config_dir)
164166

@@ -169,24 +171,45 @@ def run(scenario: str, additional_args: List[str], network: str = "warnet") -> s
169171

170172

171173
@jsonrpc.method("stop_scenario")
172-
def stop_scenario(scenario: str, network: str = "warnet") -> str:
174+
def stop_scenario(pid: int, network: str = "warnet") -> str:
175+
176+
def is_running(pid):
177+
try:
178+
os.kill(pid, 0)
179+
except ProcessLookupError:
180+
return False
181+
return True
182+
183+
@exponential_backoff()
184+
def kill_process(pid):
185+
os.kill(pid, signal.SIGKILL)
186+
173187
config_dir = gen_config_dir(network)
174188
running_scenarios = load_running_scenarios(config_dir)
175189

176-
if scenario not in running_scenarios:
177-
return f"Scenario {scenario} is not running."
190+
scenario = None
191+
for scenario_name, scenario_pid in running_scenarios.items():
192+
if scenario_pid == pid:
193+
scenario = scenario_name
194+
break
195+
if not scenario:
196+
return f"No active scenario found for PID {pid}."
178197

179-
pid = running_scenarios[scenario]
180-
try:
181-
os.kill(pid, 0)
182-
except ProcessLookupError:
183-
return f"Scenario {scenario} with PID {pid} is not running."
198+
if not is_running(pid):
199+
return f"Scenario {scenario} with PID {pid} was found in file but is not running."
184200

201+
# First try with SIGTERM
185202
os.kill(pid, signal.SIGTERM)
203+
time.sleep(5)
204+
# Then try SIGKILL with exponential backoff
205+
if is_running(pid):
206+
kill_process(pid)
186207

187-
remove_stopped_scenario(scenario, config_dir)
208+
if is_running(pid):
209+
return f"Could not kill scenario {scenario} with pid {pid} using SIGKILL"
188210

189-
return f"Stopped scenario {scenario}."
211+
remove_stopped_scenario(scenario, config_dir)
212+
return f"Stopped scenario {scenario} with PID {pid}."
190213

191214

192215
@jsonrpc.method("list_running_scenarios")

0 commit comments

Comments
 (0)