Skip to content

Commit 358d2b5

Browse files
GhostofGoesactiveshadow
authored andcommitted
feat(cc): add reset command
- Add 'reset' to cc scorch component - README updates and formatting - Use self.mm property instead of passing around a mm object - Handle cases where no cmd.type is specified - Code cleanup
1 parent d35ce79 commit 358d2b5

File tree

4 files changed

+128
-81
lines changed

4 files changed

+128
-81
lines changed

src/python/phenix_apps/apps/scorch/cc/README.md

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,47 @@ type: cc
55
exe: phenix-scorch-component-cc
66
```
77

8+
> NOTE: miniccc must be installed and running on the target VM(s).
9+
810
## Metadata Options
911

10-
```
12+
```yaml
1113
metadata:
14+
# Commands (types) to run for a particular stage (configure, start, stop, cleanup)
15+
# NOTE: The stage-level types differ from the VM-level types
16+
configure:
17+
- type: reset # delete all miniccc commands and responses
18+
start: [] # same array of keys as above
19+
stop: [] # same array of keys as above
20+
cleanup: [] # same array of keys as above
21+
22+
# Commands to run for specific VMs, for a particular stage
1223
vms:
13-
- hostname:
14-
configure:
15-
- type: exec # can be exec, background, send, or recv
16-
args: whoami # a simple string of args (not an array)
17-
once: <bool> # only execute a command once (applicable for exec and background) (default: true)
18-
wait: <bool> # wait for cmd to be executed by VM (default: false)
19-
validator: <bash script to validate exec results>
20-
start: [] # same array of keys as above
21-
stop: [] # same array of keys as above
22-
cleanup: [] # same array of keys as above
24+
- hostname:
25+
configure:
26+
- type: exec # can be exec, background, send, recv
27+
args: whoami # a simple string of args (not an array)
28+
once: <bool> # only execute a command once (applicable for exec and background) (default: true)
29+
wait: <bool> # wait for cmd to be executed by VM (default: false)
30+
validator: <bash script to validate exec results>
31+
start: [] # same array of keys as above
32+
stop: [] # same array of keys as above
33+
cleanup: [] # same array of keys as above
2334
```
2435
2536
> The validator is only used when `type = exec` and forces `wait = true`. The
2637
> validator script should be written to process STDIN. Anything the validator
2738
> script writes to STDERR will be available to the user if the validation fails.
2839

40+
## Types
41+
- VM-specific command types
42+
- `exec`: execute a command (`cc exec`)
43+
- `background`: execute a command in the background (component will continue)
44+
- `send`: send a file (Host -> VM)
45+
- `recv`: receive a file (VM -> Host)
46+
- Stage-level command types
47+
- `reset`: reset miniccc state, by clearing filter and deleting all commands and responses. **WARNING**: THIS WILL INTERFERE WITH OPERATION OF MANY COMPONENTS! Reset should only be used at the start or end of a run or a loop (use with special care in loops!).
48+
2949
## Notes on the `exec` and `background` Type
3050

3151
When a `cc` command is configured, minimega will send the command to a `miniccc`
@@ -68,6 +88,13 @@ host for the current scorch run, loop, and count.
6888

6989
```yaml
7090
components:
91+
- name: reset_miniccc
92+
type: cc
93+
metadata:
94+
configure:
95+
- reset
96+
cleanup:
97+
- reset
7198
- name: disable-eth0
7299
type: cc
73100
metadata:

src/python/phenix_apps/apps/scorch/cc/cc.py

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import os, subprocess, sys, uuid
1+
import os
2+
import subprocess
3+
import sys
4+
import uuid
25

36
from phenix_apps.apps.scorch import ComponentBase
47
from phenix_apps.common import utils
@@ -10,28 +13,33 @@ def __init__(self):
1013

1114
self.execute_stage()
1215

13-
1416
def configure(self):
1517
self.__run('configure')
1618

17-
1819
def start(self):
1920
self.__run('start')
2021

21-
2222
def stop(self):
2323
self.__run('stop')
2424

25-
2625
def cleanup(self):
2726
self.__run('cleanup')
2827

29-
30-
def __run(self, stage):
28+
def __run(self, stage: str) -> None:
3129
nodes = self.extract_node_names()
32-
vms = self.metadata.get('vms', [])
33-
34-
mm = self.mm_init()
30+
vms = self.metadata.get('vms', [])
31+
commands = self.metadata.get(stage, [])
32+
33+
for cmd in commands:
34+
if cmd.type == 'reset':
35+
self.print("deleting miniccc commands and responses")
36+
self.mm.clear_cc_filter()
37+
self.mm.cc_delete_command("all")
38+
self.mm.cc_delete_response("all")
39+
self.mm.clear_cc_commands()
40+
self.mm.clear_cc_responses()
41+
else:
42+
self.eprint(f"Unknown command type '{cmd.type}' for stage '{stage}'")
3543

3644
for vm in vms:
3745
if vm.hostname not in nodes:
@@ -55,10 +63,10 @@ def __run(self, stage):
5563

5664
self.print(f"executing command '{cmd.args}' in VM {vm.hostname}")
5765

58-
script = self.__send_cmd_as_file(mm, vm.hostname, cmd.args)
66+
script = self.__send_cmd_as_file(vm.hostname, cmd.args)
5967

6068
if wait:
61-
results = utils.mm_exec_wait(mm, vm.hostname, script, once=once)
69+
results = utils.mm_exec_wait(self.mm, vm.hostname, script, once=once)
6270

6371
self.print(f"command '{cmd.args}' executed in VM {vm.hostname} using cc")
6472

@@ -108,28 +116,29 @@ def __run(self, stage):
108116
else:
109117
self.print('results are valid')
110118
else:
111-
mm.cc_filter(f'name={vm.hostname}')
119+
self.mm.cc_filter(f'name={vm.hostname}')
112120

113121
if once:
114-
mm.cc_exec_once(script)
122+
self.mm.cc_exec_once(script)
115123
else:
116-
mm.cc_exec(script)
124+
self.mm.cc_exec(script)
117125

118126
self.print(f"command '{cmd.args}' executed in VM {vm.hostname} using cc")
119127
elif cmd.type == 'background':
120128
once = cmd.get('once', True)
121129

122130
self.print(f"backgrounding command '{cmd.args}' in VM {vm.hostname} using cc")
123131

124-
script = self.__send_cmd_as_file(mm, vm.hostname, cmd.args)
132+
script = self.__send_cmd_as_file(vm.hostname, cmd.args)
125133

126-
mm.cc_filter(f'name={vm.hostname}')
134+
self.mm.cc_filter(f'name={vm.hostname}')
127135

128136
if once:
129-
mm.cc_background_once(script)
137+
self.mm.cc_background_once(script)
130138
else:
131-
mm.cc_background(script)
139+
self.mm.cc_background(script)
132140

141+
self.mm.clear_cc_filter()
133142
self.print(f"command '{cmd.args}' backgrounded in VM {vm.hostname}")
134143
elif cmd.type == 'send':
135144
args = cmd.args.split(':')
@@ -154,7 +163,7 @@ def __run(self, stage):
154163
self.print(f"sending file '{src}' to VM {vm.hostname} at '{dst}' using cc")
155164

156165
try:
157-
utils.mm_send(mm, vm.hostname, src, dst)
166+
utils.mm_send(self.mm, vm.hostname, src, dst)
158167
self.print(f"file '{src}' sent to VM {vm.hostname} at '{dst}'")
159168
except Exception as ex:
160169
self.eprint(f"error sending '{src}' to VM {vm.hostname}: {ex}")
@@ -177,14 +186,22 @@ def __run(self, stage):
177186
self.print(f"receiving file '{src}' from VM {vm.hostname} to `{dst}` using cc")
178187

179188
try:
180-
utils.mm_recv(mm, vm.hostname, src, dst)
189+
utils.mm_recv(self.mm, vm.hostname, src, dst)
181190
self.print(f"file '{src}' received from VM {vm.hostname} to `{dst}`")
182191
except Exception as ex:
183192
self.eprint(f"error receiving '{src}' from VM {vm.hostname}: {ex}")
184193
sys.exit(1)
185-
186-
187-
def __send_cmd_as_file(self, mm, hostname, cmd):
194+
elif cmd.type == 'reset':
195+
self.print("deleting miniccc commands and responses")
196+
self.mm.clear_cc_filter()
197+
self.mm.cc_delete_command("all")
198+
self.mm.cc_delete_response("all")
199+
self.mm.clear_cc_commands()
200+
self.mm.clear_cc_responses()
201+
else:
202+
self.eprint(f"Unknown command type '{cmd.type}' for VM '{vm.hostname}' and stage '{stage}'")
203+
204+
def __send_cmd_as_file(self, hostname, cmd):
188205
cmd_file = f'run-{self.extract_run_name()}_{str(uuid.uuid4())}'
189206
node = self.extract_node(hostname)
190207

@@ -199,12 +216,13 @@ def __send_cmd_as_file(self, mm, hostname, cmd):
199216
with open(cmd_src, 'w') as f:
200217
f.write(cmd)
201218

202-
mm.cc_filter(f'name={hostname}')
203-
mm.cc_send(cmd_src)
219+
self.mm.cc_filter(f'name={hostname}')
220+
self.mm.cc_send(cmd_src)
204221

205222
# wait for file to be sent via cc
206-
last_cmd = utils.mm_last_command(mm)
207-
utils.mm_wait_for_cmd(mm, last_cmd['id'])
223+
last_cmd = utils.mm_last_command(self.mm)
224+
utils.mm_wait_for_cmd(self.mm, last_cmd['id'])
225+
self.mm.clear_cc_filter()
208226

209227
os.remove(cmd_src)
210228

src/python/phenix_apps/apps/scorch/mm/README.md

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,48 @@ exe: phenix-scorch-component-mm
77

88
## Metadata Options
99

10-
```
10+
```yaml
1111
metadata:
12+
# Commands (types) to run for a particular stage (configure, start, stop, cleanup)
1213
configure:
13-
- type: start_capture
14-
capture:
15-
bridge: phenix
16-
filename: capture.pcap # will be auto generated as <bridge>-<ts>.pcap if not provided
17-
filter: port 80 # typical BPF filter expression
18-
snaplen: null
19-
- type: stop_capture
20-
capture:
21-
bridge: phenix
22-
convert: true # convert to JSON (for parsing into Elastic, for example) (default is false)
23-
start: [] # same array of keys as above
24-
stop: [] # same array of keys as above
25-
cleanup: [] # same array of keys as above
26-
vms:
27-
- hostname: <string>
28-
configure:
29-
- type: start # can be start, stop, connect, disconnect, start_capture, stop_capture
30-
- type: stop
31-
- type: connect # can also be used to move an already connected interface
32-
connect:
33-
interface: 0 # index of interface
34-
vlan: FOO
35-
bridge: phenix # will use the same bridge previously connected to (if moving) if not provided
36-
- type: disconnect
37-
disconnect:
38-
interface: 0 # index of interface
3914
- type: start_capture
4015
capture:
41-
interface: 0
42-
filename: capture.pcap # will be auto generated as <host>-<iface>-<ts>.pcap if not provided
16+
bridge: phenix
17+
filename: capture.pcap # will be auto generated as <bridge>-<ts>.pcap if not provided
4318
filter: port 80 # typical BPF filter expression
4419
snaplen: null
45-
- type: stop_capture # will stop captures on all interfaces (minimega limitation)
20+
- type: stop_capture
4621
capture:
22+
bridge: phenix
4723
convert: true # convert to JSON (for parsing into Elastic, for example) (default is false)
48-
start: [] # same array of keys as above
49-
stop: [] # same array of keys as above
50-
cleanup: [] # same array of keys as above
51-
```
24+
start: [] # same array of keys as above
25+
stop: [] # same array of keys as above
26+
cleanup: [] # same array of keys as above
27+
28+
# Commands (types) to run for specific VMs, for a particular stage
29+
vms:
30+
- hostname: <string>
31+
configure:
32+
- type: start # can be start, stop, connect, disconnect, start_capture, stop_capture
33+
- type: stop
34+
- type: connect # can also be used to move an already connected interface
35+
connect:
36+
interface: 0 # index of interface
37+
vlan: FOO
38+
bridge: phenix # will use the same bridge previously connected to (if moving) if not provided
39+
- type: disconnect
40+
disconnect:
41+
interface: 0 # index of interface
42+
- type: start_capture
43+
capture:
44+
interface: 0
45+
filename: capture.pcap # will be auto generated as <host>-<iface>-<ts>.pcap if not provided
46+
filter: port 80 # typical BPF filter expression
47+
snaplen: null
48+
- type: stop_capture # will stop captures on all interfaces (minimega limitation)
49+
capture:
50+
convert: true # convert to JSON (for parsing into Elastic, for example) (default is false)
51+
start: [] # same array of keys as above
52+
stop: [] # same array of keys as above
53+
cleanup: [] # same array of keys as above
54+
```

src/python/phenix_apps/apps/scorch/mm/mm.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,19 @@ def __init__(self):
1414

1515
self.execute_stage()
1616

17-
1817
def configure(self):
1918
self.__run('configure')
2019

21-
2220
def start(self):
2321
self.__run('start')
2422

25-
2623
def stop(self):
2724
self.__run('stop')
2825

29-
3026
def cleanup(self):
3127
self.__run('cleanup')
3228

33-
34-
def __run(self, stage):
29+
def __run(self, stage: str) -> None:
3530
mm = self.mm_init()
3631

3732
commands = self.metadata.get(stage, [])
@@ -83,13 +78,13 @@ def __run(self, stage):
8378
cap = cmd.get('capture', None)
8479

8580
if not cap:
86-
self.eprint(f'no bridge capture details provided')
81+
self.eprint('no bridge capture details provided')
8782
sys.exit(1)
8883

8984
bridge = cap.get('bridge', None)
9085

9186
if not bridge:
92-
self.eprint(f'bridge to stop capture traffic on not provided')
87+
self.eprint('bridge to stop capture traffic on not provided')
9388
sys.exit(1)
9489
try:
9590
self.print(f'stopping pcap capture on bridge {bridge}')
@@ -132,6 +127,8 @@ def __run(self, stage):
132127
except Exception as ex:
133128
self.eprint(f'unable to stop pcap capture on bridge {bridge}: {ex}')
134129
sys.exit(1)
130+
else:
131+
self.eprint(f"Unknown command type '{cmd.type}'")
135132

136133
vms = self.metadata.get('vms', [])
137134

@@ -285,6 +282,8 @@ def __run(self, stage):
285282
except Exception as ex:
286283
self.eprint(f'unable to stop pcap capture(s) on vm {vm.hostname}: {ex}')
287284
sys.exit(1)
285+
else:
286+
self.eprint(f"Unknown command type for VM '{vm.hostname}': {cmd.type}")
288287

289288

290289
def main():

0 commit comments

Comments
 (0)