From 08c10345c9004e062a63fa0a87a2e3c260c05bd4 Mon Sep 17 00:00:00 2001 From: Vallari Agrawal Date: Wed, 24 Aug 2022 19:57:17 +0530 Subject: [PATCH 1/5] Add basic 'suite/dryrun' route and it's service Signed-off-by: Vallari Agrawal --- README.md | 3 +-- src/routes/suite.py | 11 ++++++++++- src/services/suite.py | 10 ++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff3af82..89a0e7b 100644 --- a/README.md +++ b/README.md @@ -59,5 +59,4 @@ curl --location --request POST 'http://localhost:8082/suite/' \ }' ``` - - +POST `/suite/dryrun`: dry runs a suite. diff --git a/src/routes/suite.py b/src/routes/suite.py index 74dfbb7..0a050de 100644 --- a/src/routes/suite.py +++ b/src/routes/suite.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, HTTPException -from services.suite import run +from services.suite import run, dry_run from schemas.suite import SuiteArgs import logging @@ -19,3 +19,12 @@ def create_run(args: SuiteArgs): return {"run": results} except Exception as exc: raise HTTPException(status_code=404, detail=repr(exc)) + +@router.post("/dryrun", status_code=200) +def create_dryrun(args: SuiteArgs): + try: + args = args.dict(by_alias=True) + result = dry_run(args) + return result + except Exception as exc: + raise HTTPException(status_code=404, detail=repr(exc)) diff --git a/src/services/suite.py b/src/services/suite.py index 22f1f01..01a2622 100644 --- a/src/services/suite.py +++ b/src/services/suite.py @@ -27,6 +27,16 @@ def run(args): log.error("teuthology.suite.main failed with the error: " + repr(exc)) raise +def dry_run(args): + try: + args['--dry-run'] = True + results = teuthology.suite.main(args) + log.debug(results) + return + except Exception as exc: + log.error("teuthology.suite.main failed with the error: " + repr(exc)) + raise + def get_run_details(run_name): """ Queries paddles to look if run is created. From e6caa30e34fca99bfc332755f9cdd0f8ce8898f1 Mon Sep 17 00:00:00 2001 From: Vallari Agrawal Date: Sun, 28 Aug 2022 04:08:33 +0530 Subject: [PATCH 2/5] Return logs with multiprocessing - Add two query params (dry_run and logs) to /suite - Use multiprocessing to isolate logs of two parallel runs Signed-off-by: Vallari Agrawal --- src/routes/suite.py | 17 +++---------- src/services/suite.py | 59 +++++++++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/routes/suite.py b/src/routes/suite.py index 0a050de..ec0ff78 100644 --- a/src/routes/suite.py +++ b/src/routes/suite.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, HTTPException -from services.suite import run, dry_run +from services.suite import run from schemas.suite import SuiteArgs import logging @@ -12,19 +12,10 @@ ) @router.post("/", status_code=200) -def create_run(args: SuiteArgs): +def create_run(args: SuiteArgs, dry_run: bool = False, logs: bool = False): try: args = args.dict(by_alias=True) - results = run(args) - return {"run": results} - except Exception as exc: - raise HTTPException(status_code=404, detail=repr(exc)) - -@router.post("/dryrun", status_code=200) -def create_dryrun(args: SuiteArgs): - try: - args = args.dict(by_alias=True) - result = dry_run(args) - return result + results = run(args, dry_run, logs) + return results except Exception as exc: raise HTTPException(status_code=404, detail=repr(exc)) diff --git a/src/services/suite.py b/src/services/suite.py index 01a2622..5c24591 100644 --- a/src/services/suite.py +++ b/src/services/suite.py @@ -1,5 +1,6 @@ import teuthology.suite -import logging, requests # Note: import requests after teuthology +from multiprocessing import Process +import logging, requests, uuid, os # Note: import requests after teuthology from datetime import datetime from config import settings @@ -8,10 +9,26 @@ log = logging.getLogger(__name__) -def run(args): + +def run(args, dry_run: bool, send_logs: bool): + """ + Schedule a suite. + :returns: Run details (dict) and logs (list). + """ try: args["--timestamp"] = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') - teuthology.suite.main(args) + if dry_run: + args['--dry-run'] = True + logs = logs_run(args) + return { "run": {}, "logs": logs } + + logs = [] + if send_logs: + logs = logs_run(args) + else: + teuthology.suite.main(args) + + # get run details from paddles run_name = make_run_name({ "machine_type": args["--machine-type"], "user": args["--user"], @@ -22,20 +39,36 @@ def run(args): "flavor": args["--flavor"] }) run_details = get_run_details(run_name) - return { "run": run_details } + return { "run": run_details, "logs": logs } except Exception as exc: log.error("teuthology.suite.main failed with the error: " + repr(exc)) raise -def dry_run(args): - try: - args['--dry-run'] = True - results = teuthology.suite.main(args) - log.debug(results) - return - except Exception as exc: - log.error("teuthology.suite.main failed with the error: " + repr(exc)) - raise +def logs_run(args): + """ + Schedule suite in a seperate process (to isolate logs). + """ + id = str(uuid.uuid4()) + log_file = f'/archive_dir/{id}.log' + + teuthology_process = Process(target=run_with_logs, args=(args, log_file,)) + teuthology_process.start() + teuthology_process.join() + + logs = "" + with open(log_file) as f: + logs = f.readlines() + if os.path.isfile(log_file): + os.remove(log_file) + return logs + +def run_with_logs(args, log_file): + """ + Set a new log file to store logs + and then schedule suite. + """ + teuthology.setup_log_file(log_file) + teuthology.suite.main(args) def get_run_details(run_name): """ From f9a2b5d97327d734fd08deb68b4a92423cf74823 Mon Sep 17 00:00:00 2001 From: Vallari Agrawal Date: Sun, 28 Aug 2022 04:28:41 +0530 Subject: [PATCH 3/5] README: Add doc for 'dry_run' and 'logs' Signed-off-by: Vallari Agrawal --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 89a0e7b..038ba8b 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,14 @@ Returns `{"root": "success"}`. ### Route `/suite` POST `/suite/`: schedules a run. + +Two query parameters: +- `dry_run` (boolean) - Do a dry run; do not schedule anything. +- `logs` (boolean) - Send scheduling logs in response. + Example: ``` -curl --location --request POST 'http://localhost:8082/suite/' \ +curl --location --request POST 'http://localhost:8082/suite?dry_run=false&logs=true' \ --header 'Content-Type: application/json' \ --data-raw '{ "--ceph": "wip-dis-testing-2", @@ -59,4 +64,3 @@ curl --location --request POST 'http://localhost:8082/suite/' \ }' ``` -POST `/suite/dryrun`: dry runs a suite. From 43eff3f81ce822e6a8322dd929df1a6b6a3df00f Mon Sep 17 00:00:00 2001 From: Vallari Agrawal Date: Mon, 29 Aug 2022 21:06:08 +0530 Subject: [PATCH 4/5] Make 'logs_run' more generic and move it to helpers.py Signed-off-by: Vallari Agrawal --- src/services/helpers.py | 31 +++++++++++++++++++++++++++++++ src/services/suite.py | 34 ++++------------------------------ 2 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 src/services/helpers.py diff --git a/src/services/helpers.py b/src/services/helpers.py new file mode 100644 index 0000000..87430d3 --- /dev/null +++ b/src/services/helpers.py @@ -0,0 +1,31 @@ +import uuid, os +from multiprocessing import Process +import teuthology + +def logs_run(func, args): + """ + Run the command function in a seperate process (to isolate logs), + and return logs printed during the execution of the function. + """ + id = str(uuid.uuid4()) + log_file = f'/archive_dir/{id}.log' + + teuthology_process = Process( + target=_execute_with_logs, args=(func, args, log_file,)) + teuthology_process.start() + teuthology_process.join() + + logs = "" + with open(log_file) as f: + logs = f.readlines() + if os.path.isfile(log_file): + os.remove(log_file) + return logs + +def _execute_with_logs(func, args, log_file): + """ + To store logs, set a new FileHandler for teuthology root logger + and then execute the command function. + """ + teuthology.setup_log_file(log_file) + func(args) \ No newline at end of file diff --git a/src/services/suite.py b/src/services/suite.py index 5c24591..ed07218 100644 --- a/src/services/suite.py +++ b/src/services/suite.py @@ -1,6 +1,6 @@ import teuthology.suite -from multiprocessing import Process -import logging, requests, uuid, os # Note: import requests after teuthology +from services.helpers import logs_run +import logging, requests # Note: import requests after teuthology from datetime import datetime from config import settings @@ -19,12 +19,12 @@ def run(args, dry_run: bool, send_logs: bool): args["--timestamp"] = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') if dry_run: args['--dry-run'] = True - logs = logs_run(args) + logs = logs_run(teuthology.suite.main, args) return { "run": {}, "logs": logs } logs = [] if send_logs: - logs = logs_run(args) + logs = logs_run(teuthology.suite.main, args) else: teuthology.suite.main(args) @@ -44,32 +44,6 @@ def run(args, dry_run: bool, send_logs: bool): log.error("teuthology.suite.main failed with the error: " + repr(exc)) raise -def logs_run(args): - """ - Schedule suite in a seperate process (to isolate logs). - """ - id = str(uuid.uuid4()) - log_file = f'/archive_dir/{id}.log' - - teuthology_process = Process(target=run_with_logs, args=(args, log_file,)) - teuthology_process.start() - teuthology_process.join() - - logs = "" - with open(log_file) as f: - logs = f.readlines() - if os.path.isfile(log_file): - os.remove(log_file) - return logs - -def run_with_logs(args, log_file): - """ - Set a new log file to store logs - and then schedule suite. - """ - teuthology.setup_log_file(log_file) - teuthology.suite.main(args) - def get_run_details(run_name): """ Queries paddles to look if run is created. From 302ac7ef4f75d5f3893e5655f9269e9a75b55e6f Mon Sep 17 00:00:00 2001 From: Vallari Agrawal Date: Wed, 14 Sep 2022 22:54:29 +0530 Subject: [PATCH 5/5] Create logfiles in /tmp/ instead of /archive_dir Signed-off-by: Vallari Agrawal --- src/services/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/helpers.py b/src/services/helpers.py index 87430d3..a2ae497 100644 --- a/src/services/helpers.py +++ b/src/services/helpers.py @@ -8,7 +8,7 @@ def logs_run(func, args): and return logs printed during the execution of the function. """ id = str(uuid.uuid4()) - log_file = f'/archive_dir/{id}.log' + log_file = f'/tmp/{id}.log' teuthology_process = Process( target=_execute_with_logs, args=(func, args, log_file,))