Skip to content

Commit 70c5a34

Browse files
authored
Log parsing on full data (#1276)
* measurement_flow_process_duration is set in any case. Supplying a timeout of None will lead to no timeout * Log parsing is now done on full data * Introduced simpler handling of post processing steps; Improved cleaning routine of runner private attributes; Small Bugfixes * Test fix * Fixed SCI parsing and added multi test * Added test file * Changed all function names to _XXX to indicate that they should not be used externally * Adapted test_functions to use new runner function names and _post_process mechanism * Test-fix
1 parent 4dd7b67 commit 70c5a34

File tree

8 files changed

+325
-322
lines changed

8 files changed

+325
-322
lines changed

lib/notes.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from re import fullmatch
1+
import re
22

33
from lib.db import DB
44

@@ -22,26 +22,21 @@ def save_to_db(self, run_id):
2222
""",
2323
params=(run_id, note['detail_name'], note['note'], int(note['timestamp']))
2424
)
25+
def parse_and_add_notes(self, detail_name, data):
26+
for match in re.findall(r'^(\d{16}) (.+)$', data, re.MULTILINE):
27+
self.__notes.append({'note': match[1], 'detail_name': detail_name, 'timestamp': match[0]})
2528

26-
def parse_note(self, line):
27-
if match := fullmatch(r'^(\d{16}) (.+)', line):
28-
return int(match[1]), match[2]
29-
return None
30-
31-
def add_note(self, note):
32-
self.__notes.append(note)
29+
def add_note(self, note, detail_name, timestamp):
30+
self.__notes.append({'note': note , 'detail_name': detail_name, 'timestamp': timestamp})
3331

3432
if __name__ == '__main__':
3533
import argparse
36-
import time
3734

3835
parser = argparse.ArgumentParser()
3936
parser.add_argument('run_id', help='Please supply a run_id to attribute the measurements to')
4037

4138
args = parser.parse_args() # script will exit if arguments not present
4239

4340
notes = Notes()
44-
notes.add_note({'note': 'This is my note',
45-
'timestamp': int(time.time_ns() / 1000),
46-
'detail_name': 'Arnes_ Container'})
41+
notes.parse_and_add_notes('my container', '1234567890123456 My note')
4742
notes.save_to_db(args.run_id)

lib/scenario_runner.py

Lines changed: 211 additions & 237 deletions
Large diffs are not rendered by default.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
name: Test Stress
3+
author: Dan Mateas
4+
description: test
5+
6+
sci:
7+
R_d: Cool run
8+
9+
services:
10+
test-container:
11+
type: container
12+
image: gcb_stress
13+
build:
14+
context: ../stress-application
15+
16+
flow:
17+
- name: Stress
18+
container: test-container
19+
commands:
20+
- type: console
21+
shell: sh
22+
command: stress-ng -c 1 -t 1 -q; echo GMT_SCI_R=500
23+
log-stdout: True
24+
read-sci-stdout: True
25+
- type: console
26+
shell: sh
27+
command: stress-ng -c 1 -t 1 -q; echo GMT_SCI_R=600
28+
log-stdout: True
29+
read-sci-stdout: True

tests/lib/test_phase_stats.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,17 @@ def test_sci_run():
410410
assert len(data) == 1
411411
assert 50 < data[0]['value'] < 150
412412
assert data[0]['unit'] == 'ugCO2e/Cool run'
413+
414+
def test_sci_multi_steps_run():
415+
runner = ScenarioRunner(uri=GMT_ROOT_DIR, uri_type='folder', filename='tests/data/usage_scenarios/stress_sci_multi.yml', skip_system_checks=True, dev_cache_build=True, dev_no_sleeps=True, dev_no_metrics=False, dev_no_phase_stats=False)
416+
417+
out = io.StringIO()
418+
err = io.StringIO()
419+
with redirect_stdout(out), redirect_stderr(err):
420+
run_id = runner.run()
421+
422+
data = DB().fetch_all("SELECT value, unit FROM phase_stats WHERE phase = %s AND run_id = %s AND metric = 'software_carbon_intensity_global' ", params=('004_[RUNTIME]', run_id), fetch_mode='dict')
423+
424+
assert len(data) == 1
425+
assert 8 < data[0]['value'] < 20
426+
assert data[0]['unit'] == 'ugCO2e/Cool run'

tests/lib/test_save_notes.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,23 @@
1313
]
1414

1515

16-
@pytest.mark.parametrize("run_id,note,detail,timestamp", invalid_test_data)
17-
def test_invalid_timestamp(run_id, note, detail, timestamp):
16+
@pytest.mark.parametrize("run_id,note,detail_name,timestamp", invalid_test_data)
17+
def test_invalid_timestamp(run_id, note, detail_name, timestamp):
1818
with pytest.raises(ValueError) as err:
1919
notes = Notes()
20-
notes.add_note({"note": note,"detail_name": detail,"timestamp": timestamp,})
20+
notes.add_note(note, detail_name, timestamp)
2121
notes.save_to_db(run_id)
2222
expected_exception = "invalid literal for int"
2323
assert expected_exception in str(err.value), \
2424
Tests.assertion_info(f"Exception: {expected_exception}", str(err.value))
2525

26-
@pytest.mark.parametrize("run_id,note,detail,timestamp", valid_test_data)
26+
@pytest.mark.parametrize("run_id,note,detail_name,timestamp", valid_test_data)
2727
@patch('lib.db.DB.query')
28-
def test_valid_timestamp(mock_query, run_id, note, detail, timestamp):
28+
def test_valid_timestamp(mock_query, run_id, note, detail_name, timestamp):
2929
mock_query.return_value = None # Replace with the desired return value
3030

3131
notes = Notes()
32-
notes.add_note({"note": note, "detail_name": detail, "timestamp": timestamp})
32+
notes.add_note(note, detail_name, timestamp)
3333
notes.save_to_db(run_id)
3434

3535
mock_query.assert_called_once()

tests/test_functions.py

Lines changed: 51 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -277,79 +277,70 @@ def run_until(self, step):
277277
raise RuntimeError("run_until must be used within the context")
278278

279279
try:
280-
self.__runner.start_measurement()
281-
self.__runner.clear_caches()
282-
self.__runner.check_system('start')
283-
self.__runner.initialize_folder(self.__runner._tmp_folder)
284-
self.__runner.checkout_repository()
285-
self.__runner.load_yml_file()
286-
self.__runner.initial_parse()
287-
self.__runner.register_machine_id()
288-
self.__runner.import_metric_providers()
280+
self.__runner._start_measurement()
281+
self.__runner._clear_caches()
282+
self.__runner._check_system('start')
283+
self.__runner._initialize_folder(self.__runner._tmp_folder)
284+
self.__runner._checkout_repository()
285+
self.__runner._load_yml_file()
286+
self.__runner._initial_parse()
287+
self.__runner._register_machine_id()
288+
self.__runner._import_metric_providers()
289289
if step == 'import_metric_providers':
290290
return
291-
self.__runner.populate_image_names()
292-
self.__runner.prepare_docker()
293-
self.__runner.check_running_containers()
294-
self.__runner.remove_docker_images()
295-
self.__runner.download_dependencies()
296-
self.__runner.initialize_run()
291+
self.__runner._populate_image_names()
292+
self.__runner._prepare_docker()
293+
self.__runner._check_running_containers()
294+
self.__runner._remove_docker_images()
295+
self.__runner._download_dependencies()
296+
self.__runner._initialize_run()
297297

298-
self.__runner.start_metric_providers(allow_other=True, allow_container=False)
299-
self.__runner.custom_sleep(self.__runner._measurement_pre_test_sleep)
298+
self.__runner._start_metric_providers(allow_other=True, allow_container=False)
299+
self.__runner._custom_sleep(self.__runner._measurement_pre_test_sleep)
300300

301-
self.__runner.start_phase('[BASELINE]')
302-
self.__runner.custom_sleep(self.__runner._measurement_baseline_duration)
303-
self.__runner.end_phase('[BASELINE]')
301+
self.__runner._start_phase('[BASELINE]')
302+
self.__runner._custom_sleep(self.__runner._measurement_baseline_duration)
303+
self.__runner._end_phase('[BASELINE]')
304304

305-
self.__runner.start_phase('[INSTALLATION]')
306-
self.__runner.build_docker_images()
307-
self.__runner.end_phase('[INSTALLATION]')
305+
self.__runner._start_phase('[INSTALLATION]')
306+
self.__runner._build_docker_images()
307+
self.__runner._end_phase('[INSTALLATION]')
308308

309-
self.__runner.save_image_and_volume_sizes()
309+
self.__runner._save_image_and_volume_sizes()
310310

311-
self.__runner.start_phase('[BOOT]')
312-
self.__runner.setup_networks()
311+
self.__runner._start_phase('[BOOT]')
312+
self.__runner._setup_networks()
313313
if step == 'setup_networks':
314314
return
315-
self.__runner.setup_services()
315+
self.__runner._setup_services()
316316
if step == 'setup_services':
317317
return
318-
self.__runner.end_phase('[BOOT]')
319-
320-
self.__runner.add_containers_to_metric_providers()
321-
self.__runner.start_metric_providers(allow_container=True, allow_other=False)
322-
323-
self.__runner.start_phase('[IDLE]')
324-
self.__runner.custom_sleep(self.__runner._measurement_idle_duration)
325-
self.__runner.end_phase('[IDLE]')
326-
327-
self.__runner.start_phase('[RUNTIME]')
328-
self.__runner.run_flows() # can trigger debug breakpoints;
329-
self.__runner.end_phase('[RUNTIME]')
330-
331-
self.__runner.start_phase('[REMOVE]')
332-
self.__runner.custom_sleep(1)
333-
self.__runner.end_phase('[REMOVE]')
334-
335-
self.__runner.end_measurement()
336-
self.__runner.check_process_returncodes()
337-
self.__runner.identify_invalid_run()
338-
self.__runner.custom_sleep(self.__runner._measurement_post_test_sleep)
339-
self.__runner.update_start_and_end_times()
340-
self.__runner.store_phases()
341-
self.__runner.read_container_logs()
342-
self.__runner.stop_metric_providers()
343-
self.__runner.read_and_cleanup_processes()
344-
self.__runner.save_notes_runner()
345-
self.__runner.save_stdout_logs()
346-
347-
if self.__runner._dev_no_phase_stats is False:
348-
from tools.phase_stats import build_and_store_phase_stats # pylint: disable=import-outside-toplevel
349-
build_and_store_phase_stats(self.__runner._run_id, self.__runner._sci)
318+
self.__runner._end_phase('[BOOT]')
319+
320+
self.__runner._add_containers_to_metric_providers()
321+
self.__runner._start_metric_providers(allow_container=True, allow_other=False)
322+
323+
self.__runner._start_phase('[IDLE]')
324+
self.__runner._custom_sleep(self.__runner._measurement_idle_duration)
325+
self.__runner._end_phase('[IDLE]')
326+
327+
self.__runner._start_phase('[RUNTIME]')
328+
self.__runner._run_flows() # can trigger debug breakpoints;
329+
self.__runner._end_phase('[RUNTIME]')
330+
331+
self.__runner._start_phase('[REMOVE]')
332+
self.__runner._custom_sleep(1)
333+
self.__runner._end_phase('[REMOVE]')
334+
335+
self.__runner._end_measurement()
336+
self.__runner._check_process_returncodes()
337+
self.__runner._check_system('end')
338+
self.__runner._custom_sleep(self.__runner._measurement_post_test_sleep)
339+
self.__runner._identify_invalid_run()
340+
self.__runner._post_process(0)
350341

351342
except BaseException as exc:
352-
self.__runner.add_to_log(exc.__class__.__name__, str(exc))
343+
self.__runner._add_to_log(exc.__class__.__name__, str(exc))
353344
raise exc
354345

355346
def __exit__(self, exc_type, exc_value, traceback):

tests/test_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def test_check_system(skip_system_checks, config_file, expectation):
329329

330330
try:
331331
with expectation:
332-
runner.check_system()
332+
runner._check_system('start')
333333
finally:
334334
GlobalConfig().override_config(config_location=f"{os.path.dirname(os.path.realpath(__file__))}/test-config.yml") # reset, just in case. although done by fixture
335335

tests/test_yml_parsing.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ def test_includes(self):
1313
name = 'test_' + utils.randomword(12)
1414

1515
runner = ScenarioRunner(name=name, uri=GMT_DIR, uri_type='folder', filename=test_root_file)
16-
runner.checkout_repository() # We need to do this to setup the file paths correctly
16+
runner._checkout_repository() # We need to do this to setup the file paths correctly
1717

18-
runner.load_yml_file()
18+
runner._load_yml_file()
1919
result_obj = {'name': 'Import Test',
2020
'services': {'test-container':
2121
{'type': 'container'}},
@@ -28,9 +28,9 @@ def test_(self):
2828
name = 'test_' + utils.randomword(12)
2929

3030
runner = ScenarioRunner(name=name, uri=GMT_DIR, uri_type='folder', filename=test_root_file)
31-
runner.checkout_repository() # We need to do this to setup the file paths correctly
31+
runner._checkout_repository() # We need to do this to setup the file paths correctly
3232

33-
runner.load_yml_file()
33+
runner._load_yml_file()
3434
result_obj = {'name': 'my sample flow',
3535
'author': 'Arne Tarara',
3636
'description': 'test',
@@ -50,5 +50,5 @@ def test_invalid_path(self):
5050
name = 'test_' + utils.randomword(12)
5151
test_root_file = 'tests/data/usage_scenarios/import_error.yml'
5252
runner = ScenarioRunner(name=name, uri=GMT_DIR, uri_type='folder', filename=test_root_file)
53-
runner.checkout_repository() # We need to do this to setup the file paths correctly
54-
self.assertRaises(ValueError, runner.load_yml_file)
53+
runner._checkout_repository() # We need to do this to setup the file paths correctly
54+
self.assertRaises(ValueError, runner._load_yml_file)

0 commit comments

Comments
 (0)