Skip to content

Commit ca462e2

Browse files
authored
Merge pull request #599 from pinheadmz/sc-active
status: only count "running"/"pending" scenarios as "active"
2 parents 023421e + 42a739b commit ca462e2

8 files changed

+106
-88
lines changed

src/warnet/control.py

+1-25
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,11 @@
2828
console = Console()
2929

3030

31-
def get_active_scenarios():
32-
"""Get list of active scenarios"""
33-
commanders = get_mission("commander")
34-
return [c.metadata.name for c in commanders]
35-
36-
3731
@click.command()
3832
@click.argument("scenario_name", required=False)
3933
def stop(scenario_name):
4034
"""Stop a running scenario or all scenarios"""
41-
active_scenarios = get_active_scenarios()
35+
active_scenarios = [sc.metadata.name for sc in get_mission("commander")]
4236

4337
if not active_scenarios:
4438
console.print("[bold red]No active scenarios found.[/bold red]")
@@ -108,24 +102,6 @@ def stop_all_scenarios(scenarios):
108102
console.print("[bold green]All scenarios have been stopped.[/bold green]")
109103

110104

111-
def list_active_scenarios():
112-
"""List all active scenarios"""
113-
active_scenarios = get_active_scenarios()
114-
if not active_scenarios:
115-
print("No active scenarios found.")
116-
return
117-
118-
console = Console()
119-
table = Table(title="Active Scenarios", show_header=True, header_style="bold magenta")
120-
table.add_column("Name", style="cyan")
121-
table.add_column("Status", style="green")
122-
123-
for scenario in active_scenarios:
124-
table.add_row(scenario, "deployed")
125-
126-
console.print(table)
127-
128-
129105
@click.command()
130106
def down():
131107
"""Bring down a running warnet quickly"""

src/warnet/status.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def status():
1414
console = Console()
1515

1616
tanks = _get_tank_status()
17-
scenarios = _get_active_scenarios()
17+
scenarios = _get_deployed_scenarios()
1818

1919
# Create a unified table
2020
table = Table(title="Warnet Status", show_header=True, header_style="bold magenta")
@@ -31,9 +31,12 @@ def status():
3131
table.add_row("", "", "")
3232

3333
# Add scenarios to the table
34+
active = 0
3435
if scenarios:
3536
for scenario in scenarios:
3637
table.add_row("Scenario", scenario["name"], scenario["status"])
38+
if scenario["status"] == "running" or scenario["status"] == "pending":
39+
active += 1
3740
else:
3841
table.add_row("Scenario", "No active scenarios", "")
3942

@@ -52,7 +55,7 @@ def status():
5255
# Print summary
5356
summary = Text()
5457
summary.append(f"\nTotal Tanks: {len(tanks)}", style="bold cyan")
55-
summary.append(f" | Active Scenarios: {len(scenarios)}", style="bold green")
58+
summary.append(f" | Active Scenarios: {active}", style="bold green")
5659
console.print(summary)
5760
_connected(end="\r")
5861

@@ -62,6 +65,6 @@ def _get_tank_status():
6265
return [{"name": tank.metadata.name, "status": tank.status.phase.lower()} for tank in tanks]
6366

6467

65-
def _get_active_scenarios():
68+
def _get_deployed_scenarios():
6669
commanders = get_mission("commander")
6770
return [{"name": c.metadata.name, "status": c.status.phase.lower()} for c in commanders]

test/data/scenario_buggy_failure.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python3
2+
3+
4+
# The base class exists inside the commander container
5+
try:
6+
from commander import Commander
7+
except Exception:
8+
from resources.scenarios.commander import Commander
9+
10+
11+
class Failure(Commander):
12+
def set_test_params(self):
13+
self.num_nodes = 1
14+
15+
def add_options(self, parser):
16+
parser.description = "This test will fail and exit with code 222"
17+
parser.usage = "warnet run /path/to/scenario_buggy_failure.py"
18+
19+
def run_test(self):
20+
raise Exception("Failed execution!")
21+
22+
23+
if __name__ == "__main__":
24+
Failure().main()

test/data/scenario_connect_dag.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
from commander import Commander
88

99

10-
def cli_help():
11-
return "Connect a complete DAG from a set of unconnected nodes"
12-
13-
1410
@unique
1511
class ConnectionType(Enum):
1612
IP = auto()
@@ -22,6 +18,10 @@ def set_test_params(self):
2218
# This is just a minimum
2319
self.num_nodes = 10
2420

21+
def add_options(self, parser):
22+
parser.description = "Connect a complete DAG from a set of unconnected nodes"
23+
parser.usage = "warnet run /path/to/scenario_connect_dag.py"
24+
2525
def run_test(self):
2626
# All permutations of a directed acyclic graph with zero, one, or two inputs/outputs
2727
#

test/data/scenario_p2p_interface.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212
from test_framework.p2p import P2PInterface
1313

1414

15-
def cli_help():
16-
return "Run P2P GETDATA test"
17-
18-
1915
class P2PStoreBlock(P2PInterface):
2016
def __init__(self):
2117
super().__init__()
@@ -30,6 +26,10 @@ class GetdataTest(Commander):
3026
def set_test_params(self):
3127
self.num_nodes = 1
3228

29+
def add_options(self, parser):
30+
parser.description = "Run P2P GETDATA test"
31+
parser.usage = "warnet run /path/to/scenario_p2p_interface.py"
32+
3333
def run_test(self):
3434
self.log.info("Adding the p2p connection")
3535

test/scenarios_test.py

+60-45
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from warnet.control import stop_scenario
99
from warnet.process import run_command
10-
from warnet.status import _get_active_scenarios as scenarios_active
10+
from warnet.status import _get_deployed_scenarios as scenarios_deployed
1111

1212

1313
class ScenariosTest(TestBase):
@@ -18,7 +18,10 @@ def __init__(self):
1818
def run_test(self):
1919
try:
2020
self.setup_network()
21-
self.test_scenarios()
21+
self.run_and_check_miner_scenario_from_file()
22+
self.run_and_check_scenario_from_file()
23+
self.check_regtest_recon()
24+
self.check_active_count()
2225
finally:
2326
self.cleanup()
2427

@@ -28,49 +31,36 @@ def setup_network(self):
2831
self.wait_for_all_tanks_status(target="running")
2932
self.wait_for_all_edges()
3033

31-
def test_scenarios(self):
32-
self.run_and_check_miner_scenario_from_file()
33-
self.run_and_check_scenario_from_file()
34-
self.check_regtest_recon()
35-
3634
def scenario_running(self, scenario_name: str):
3735
"""Check that we are only running a single scenario of the correct name"""
38-
active = scenarios_active()
39-
assert len(active) == 1
40-
return scenario_name in active[0]["name"]
41-
42-
def run_and_check_scenario_from_file(self):
43-
scenario_file = "test/data/scenario_p2p_interface.py"
44-
self.log.info(f"Running scenario from: {scenario_file}")
45-
self.warnet(f"run {scenario_file}")
46-
self.wait_for_predicate(self.check_scenario_clean_exit)
47-
48-
def run_and_check_miner_scenario_from_file(self):
49-
scenario_file = "resources/scenarios/miner_std.py"
50-
self.log.info(f"Running scenario from file: {scenario_file}")
51-
self.warnet(f"run {scenario_file} --allnodes --interval=1")
52-
start = int(self.warnet("bitcoin rpc tank-0000 getblockcount"))
53-
self.wait_for_predicate(lambda: self.scenario_running("commander-minerstd"))
54-
self.wait_for_predicate(lambda: self.check_blocks(2, start=start))
55-
self.stop_scenario()
36+
deployed = scenarios_deployed()
37+
assert len(deployed) == 1
38+
return scenario_name in deployed[0]["name"]
5639

57-
def check_regtest_recon(self):
58-
scenario_file = "resources/scenarios/reconnaissance.py"
59-
self.log.info(f"Running scenario from file: {scenario_file}")
60-
self.warnet(f"run {scenario_file}")
61-
self.wait_for_predicate(self.check_scenario_clean_exit)
40+
def check_scenario_stopped(self):
41+
running = scenarios_deployed()
42+
self.log.debug(f"Checking if scenario stopped. Running scenarios: {len(running)}")
43+
return len(running) == 0
6244

6345
def check_scenario_clean_exit(self):
64-
active = scenarios_active()
65-
return all(scenario["status"] == "succeeded" for scenario in active)
46+
deployed = scenarios_deployed()
47+
return all(scenario["status"] == "succeeded" for scenario in deployed)
48+
49+
def stop_scenario(self):
50+
self.log.info("Stopping running scenario")
51+
running = scenarios_deployed()
52+
assert len(running) == 1, f"Expected one running scenario, got {len(running)}"
53+
assert running[0]["status"] == "running", "Scenario should be running"
54+
stop_scenario(running[0]["name"])
55+
self.wait_for_predicate(self.check_scenario_stopped)
6656

6757
def check_blocks(self, target_blocks, start: int = 0):
6858
count = int(self.warnet("bitcoin rpc tank-0000 getblockcount"))
6959
self.log.debug(f"Current block count: {count}, target: {start + target_blocks}")
7060

7161
try:
72-
active = scenarios_active()
73-
commander = active[0]["commander"]
62+
deployed = scenarios_deployed()
63+
commander = deployed[0]["commander"]
7464
command = f"kubectl logs {commander}"
7565
print("\ncommander output:")
7666
print(run_command(command))
@@ -80,18 +70,43 @@ def check_blocks(self, target_blocks, start: int = 0):
8070

8171
return count >= start + target_blocks
8272

83-
def stop_scenario(self):
84-
self.log.info("Stopping running scenario")
85-
running = scenarios_active()
86-
assert len(running) == 1, f"Expected one running scenario, got {len(running)}"
87-
assert running[0]["status"] == "running", "Scenario should be running"
88-
stop_scenario(running[0]["name"])
89-
self.wait_for_predicate(self.check_scenario_stopped)
73+
def run_and_check_miner_scenario_from_file(self):
74+
scenario_file = "resources/scenarios/miner_std.py"
75+
self.log.info(f"Running scenario from file: {scenario_file}")
76+
self.warnet(f"run {scenario_file} --allnodes --interval=1")
77+
start = int(self.warnet("bitcoin rpc tank-0000 getblockcount"))
78+
self.wait_for_predicate(lambda: self.scenario_running("commander-minerstd"))
79+
self.wait_for_predicate(lambda: self.check_blocks(2, start=start))
80+
table = self.warnet("status")
81+
assert "Active Scenarios: 1" in table
82+
self.stop_scenario()
9083

91-
def check_scenario_stopped(self):
92-
running = scenarios_active()
93-
self.log.debug(f"Checking if scenario stopped. Running scenarios: {len(running)}")
94-
return len(running) == 0
84+
def run_and_check_scenario_from_file(self):
85+
scenario_file = "test/data/scenario_p2p_interface.py"
86+
self.log.info(f"Running scenario from: {scenario_file}")
87+
self.warnet(f"run {scenario_file}")
88+
self.wait_for_predicate(self.check_scenario_clean_exit)
89+
90+
def check_regtest_recon(self):
91+
scenario_file = "resources/scenarios/reconnaissance.py"
92+
self.log.info(f"Running scenario from file: {scenario_file}")
93+
self.warnet(f"run {scenario_file}")
94+
self.wait_for_predicate(self.check_scenario_clean_exit)
95+
96+
def check_active_count(self):
97+
scenario_file = "test/data/scenario_buggy_failure.py"
98+
self.log.info(f"Running scenario from: {scenario_file}")
99+
self.warnet(f"run {scenario_file}")
100+
101+
def two_pass_one_fail():
102+
deployed = scenarios_deployed()
103+
if len([s for s in deployed if s["status"] == "succeeded"]) != 2:
104+
return False
105+
return len([s for s in deployed if s["status"] == "failed"]) == 1
106+
107+
self.wait_for_predicate(two_pass_one_fail)
108+
table = self.warnet("status")
109+
assert "Active Scenarios: 0" in table
95110

96111

97112
if __name__ == "__main__":

test/signet_test.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from test_base import TestBase
88

9-
from warnet.status import _get_active_scenarios as scenarios_active
9+
from warnet.status import _get_deployed_scenarios as scenarios_deployed
1010

1111

1212
class SignetTest(TestBase):
@@ -55,8 +55,8 @@ def check_signet_recon(self):
5555
self.warnet(f"run {scenario_file}")
5656

5757
def check_scenario_clean_exit():
58-
active = scenarios_active()
59-
return all(scenario["status"] == "succeeded" for scenario in active)
58+
deployed = scenarios_deployed()
59+
return all(scenario["status"] == "succeeded" for scenario in deployed)
6060

6161
self.wait_for_predicate(check_scenario_clean_exit)
6262

test/test_base.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
from time import sleep
1111

1212
from warnet import SRC_DIR
13-
from warnet.control import get_active_scenarios
1413
from warnet.k8s import get_pod_exit_status
1514
from warnet.network import _connected as network_connected
15+
from warnet.status import _get_deployed_scenarios as scenarios_deployed
1616
from warnet.status import _get_tank_status as network_status
1717

1818

@@ -126,12 +126,12 @@ def wait_for_all_edges(self, timeout=20 * 60, interval=5):
126126

127127
def wait_for_all_scenarios(self):
128128
def check_scenarios():
129-
scns = get_active_scenarios()
129+
scns = scenarios_deployed()
130130
if len(scns) == 0:
131131
return True
132132
for s in scns:
133-
exit_status = get_pod_exit_status(s)
134-
self.log.debug(f"Scenario {s} exited with code {exit_status}")
133+
exit_status = get_pod_exit_status(s["name"])
134+
self.log.debug(f"Scenario {s['name']} exited with code {exit_status}")
135135
if exit_status != 0:
136136
return False
137137
return True

0 commit comments

Comments
 (0)