From 9ea110391eb13c69cd547d60957793733d73cd7b Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Thu, 31 Aug 2023 09:48:53 -0600 Subject: [PATCH] add --cleanup-yml to provide cleanup playbooks --- README.md | 42 +++++++++++++--------- src/tox_lsr/test_scripts/runqemu.py | 55 ++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 53446d6..3dddea3 100644 --- a/README.md +++ b/README.md @@ -469,6 +469,15 @@ You must provide one of `--image-file` or `--image-name`. applied to the snapshot if using `--use-snapshot`. If you have playbooks which do other types of per-test setup, do not use `--setup-yml`. Just specify them in order on the command line after all of the arguments. +* `--cleanup-yml` - You can specify one or more of your own cleanup playbooks to + use. The cleanup playbooks are run no matter if the main playbooks succeeded + or failed. This is really useful if you want to ensure some resources are + restored after the playbooks run, or if you want to gather some information + from the VM when the playbooks fail e.g. a handler fails and you want to get + the systemd journal from the VM. The variable `main_success` boolean can be + used to tell if the main playbooks succeeded or failed. The corresponding + environment variable is `LSR_QEMU_CLEANUP_YML`, which is a comma-delimited + list of playbook files. * `--write-inventory` - Specify a file to write the generated inventory to. The filename must be simply `inventory`, or must end in `.yml`. Examples: `/path/to/inventory` or `/tmp/inventory.xxx.yml`. The user is responsible for @@ -584,16 +593,16 @@ line is an invocation of `ansible-playbook`. The contents of the line are the same as the command line arguments to `runqemu`. You can use almost all of the same command-line parameters. For example: ``` ---log-file /path/to/test1.log --artifacts /path/to/test1-artifacts --setup-yml /path/to/setup-snapshot.yml --tests-dir /path/to/tests -e some_ansible_var="some ansible value" --batch-id tests_test1.yml -- _setup.yml save.yml /path/to/tests/tests_test1.yml restore.yml cleanup.yml ---log-file /path/to/test2.log --artifacts /path/to/test2-artifacts --setup-yml /path/to/setup-snapshot.yml --tests-dir /path/to/tests -e some_ansible_var="some ansible value" --batch-id tests_test2.yml -- _setup.yml save.yml /path/to/tests/tests_test2.yml restore.yml cleanup.yml +--log-file /path/to/test1.log --artifacts /path/to/test1-artifacts --setup-yml /path/to/setup-snapshot.yml --tests-dir /path/to/tests -e some_ansible_var="some ansible value" --batch-id tests_test1.yml -- _setup.yml save.yml /path/to/tests/tests_test1.yml --cleanup-yml restore.yml --cleanup-yml cleanup.yml +--log-file /path/to/test2.log --artifacts /path/to/test2-artifacts --setup-yml /path/to/setup-snapshot.yml --tests-dir /path/to/tests -e some_ansible_var="some ansible value" --batch-id tests_test2.yml -- _setup.yml save.yml /path/to/tests/tests_test2.yml --cleanup-yml restore.yml --cleanup-yml cleanup.yml ... ``` if you pass this as `runqemu.py --batch-file this-file.txt` it will start a VM and create an inventory, then run ``` -ansible-playbook --inventory inventory -e some_ansible_var="some ansible value" -- _setup.yml save.yml /path/to/tests/tests_test1.yml restore.yml cleanup.yml >> /path/to/test1.log 2>&1 +ansible-playbook --inventory inventory -e some_ansible_var="some ansible value" -- _setup.yml save.yml /path/to/tests/tests_test1.yml --cleanup-yml restore.yml --cleanup-yml cleanup.yml >> /path/to/test1.log 2>&1 # artifacts such as default_provisioner.log and the vm logs will go to /path/to/test1-artifacts -ansible-playbook --inventory inventory -e some_ansible_var="some ansible value" -- _setup.yml save.yml /path/to/tests/tests_test2.yml restore.yml cleanup.yml >> /path/to/test2.log 2>&1 +ansible-playbook --inventory inventory -e some_ansible_var="some ansible value" -- _setup.yml save.yml /path/to/tests/tests_test2.yml --cleanup-yml restore.yml --cleanup-yml cleanup.yml >> /path/to/test2.log 2>&1 # artifacts such as default_provisioner.log and the vm logs will go to /path/to/test2-artifacts ``` then it will shutdown the VM. If you want to leave the VM running for @@ -601,18 +610,19 @@ debugging, use `--debug` in the *last* entry in the batch file e.g. `--debug --log-file /path/to/testN.log ...` Only the following `runqemu` arguments are supported in batch files: -`--log-file`, `--artifacts`, `--setup-yml`, `--tests-dir`, and `--debug` (only -on last line). In addition, there is an argument used only in batch files - -`--batch-id` - which you can use as an identifier to correlate lines in your -batch file with the corresponding line in your batch report file. You can use -many/most `ansible-playbook` arguments. Arguments passed in on the `runqemu` -command line will be the default values. Specifying arguments in the batch file -will override the `runqemu` command line arguments. NOTE: With batch file, you -can use `runqemu` without providing any playbooks on the command line. However, -if you want to provide Ansible arguments on the `runqemu` command line, you will -need to add `--` to the end of the `runqemu` command line, because `runqemu` -cannot tell the difference between an Ansible argument and a playbook. Also, it -is recommended to put any Ansible arguments *after* any `runqemu` arguments. +`--log-file`, `--artifacts`, `--setup-yml`, `--tests-dir`, `--cleanup-yml`, and +`--debug` (only on last line). In addition, there is an argument used only in +batch files - `--batch-id` - which you can use as an identifier to correlate +lines in your batch file with the corresponding line in your batch report file. +You can use many/most `ansible-playbook` arguments. Arguments passed in on the +`runqemu` command line will be the default values. Specifying arguments in the +batch file will override the `runqemu` command line arguments. NOTE: With batch +file, you can use `runqemu` without providing any playbooks on the command line. +However, if you want to provide Ansible arguments on the `runqemu` command line, +you will need to add `--` to the end of the `runqemu` command line, because +`runqemu` cannot tell the difference between an Ansible argument and a playbook. +Also, it is recommended to put any Ansible arguments *after* any `runqemu` +arguments. Example: ``` runqemu.py --log-level info --batch-file batch.txt --batch-report report.txt \ diff --git a/src/tox_lsr/test_scripts/runqemu.py b/src/tox_lsr/test_scripts/runqemu.py index 1543afd..4480996 100644 --- a/src/tox_lsr/test_scripts/runqemu.py +++ b/src/tox_lsr/test_scripts/runqemu.py @@ -611,10 +611,12 @@ def internal_run_ansible_playbooks( cwd, wait_on_qemu=False, log_file=None, + cleanup_yml=None, ): """Run ansible-playbook with the LOCK_ON_FILE if wait_on_qemu is True.""" if wait_on_qemu: test_env["LOCK_ON_FILE"] = tempfile.NamedTemporaryFile().name + success = False try: with file_or_stdout(log_file) as (stdout, stderr): subprocess.check_call( # nosec @@ -630,7 +632,25 @@ def internal_run_ansible_playbooks( stdout=stdout, stderr=stderr, ) + success = True finally: + if cleanup_yml is not None: + with file_or_stdout(log_file) as (stdout, stderr): + subprocess.check_call( # nosec + [ + "ansible-playbook", + "-vv", + "--inventory=" + inventory, + "-e", + "main_success=" + str(success), + ] + + ansible_args + + cleanup_yml, + env=test_env, + cwd=cwd, + stdout=stdout, + stderr=stderr, + ) if wait_on_qemu: stop_qemu(test_env) @@ -782,6 +802,7 @@ def handle_vault(tests_dir, ansible_args, playbooks, test_env): def run_ansible_playbooks( # noqa: C901 image, setup_yml, + cleanup_yml, test_env, debug, image_alias, @@ -873,6 +894,12 @@ def run_ansible_playbooks( # noqa: C901 if args and args.setup_yml: local_setup_yml.extend(args.setup_yml) local_setup_yml = [os.path.abspath(setup) for setup in local_setup_yml] + local_cleanup_yml = list(cleanup_yml) + if args and args.cleanup_yml: + local_cleanup_yml.extend(args.cleanup_yml) + local_cleanup_yml = [ + os.path.abspath(cleanup) for cleanup in local_cleanup_yml + ] if args and args.tests_dir: cwd = args.tests_dir elif tests_dir: @@ -912,6 +939,7 @@ def run_ansible_playbooks( # noqa: C901 cwd, wait_on_qemu, local_log_file, + local_cleanup_yml, ) if local_log_file: logging.info("Playbook run was successful") @@ -977,6 +1005,7 @@ def install_requirements(sourcedir, collection_path, test_env, collection): force_flag = "--force" coll_rqf = os.path.join(sourcedir, "meta", "collection-requirements.yml") tests_rqf = os.path.join(sourcedir, "tests", "collection-requirements.yml") + galaxy_env = {"ANSIBLE_COLLECTIONS_PATHS": collection_path} for reqfile in [coll_rqf, tests_rqf]: if os.path.isfile(reqfile): ag_cmd = [ @@ -985,7 +1014,7 @@ def install_requirements(sourcedir, collection_path, test_env, collection): "install", "-p", collection_path, - "-vvv", + "-vv", "-r", reqfile, ] @@ -995,6 +1024,7 @@ def install_requirements(sourcedir, collection_path, test_env, collection): ag_cmd, stdout=sys.stdout, stderr=sys.stderr, + env=galaxy_env, ) test_env["ANSIBLE_COLLECTIONS_PATHS"] = collection_path if collection_save_file: @@ -1024,6 +1054,9 @@ def setup_callback_plugins(pretty, profile, profile_task_limit, test_env): ) if not os.path.isdir(callback_plugin_dir): os.makedirs(callback_plugin_dir) + galaxy_env = { + "ANSIBLE_COLLECTIONS_PATHS": os.environ["LSR_TOX_ENV_TMP_DIR"] + } debug_py = os.path.join(callback_plugin_dir, "debug.py") profile_py = os.path.join(callback_plugin_dir, "profile_tasks.py") if (pretty and not os.path.isfile(debug_py)) or ( @@ -1041,6 +1074,7 @@ def setup_callback_plugins(pretty, profile, profile_task_limit, test_env): ], stdout=sys.stdout, stderr=sys.stderr, + env=galaxy_env, ) tmp_debug_py = os.path.join( os.environ["LSR_TOX_ENV_TMP_DIR"], @@ -1102,6 +1136,7 @@ def runqemu( use_snapshot=False, use_ansible_log=False, setup_yml=None, + cleanup_yml=None, wait_on_qemu=False, write_inventory=None, erase_old_snapshot=False, @@ -1134,6 +1169,9 @@ def runqemu( local_setup_yml.extend(setup_yml) if post_setup_yml: local_setup_yml.append(post_setup_yml) + local_cleanup_yml = [] + if cleanup_yml: + local_cleanup_yml.extend(cleanup_yml) if collection_path is None and "TOX_WORK_DIR" in os.environ: collection_path = os.environ["TOX_WORK_DIR"] test_env = dict(image.get("env", {})) @@ -1152,6 +1190,7 @@ def runqemu( run_ansible_playbooks( image, local_setup_yml, + local_cleanup_yml, test_env, debug, image_alias, @@ -1310,6 +1349,17 @@ def get_arg_parser(): default=[], help="one or more setup.yml to use in addition to config.", ) + parser.add_argument( + "--cleanup-yml", + action="append", + default=[], + help=( + "one or more cleanup.yml playbooks. These will be run even " + "if the main playbooks fail. The status will be passed to " + "the playbooks so they can take action depending on if the main " + "playbooks succeeded or failed." + ), + ) parser.add_argument( "--wait-on-qemu", action="store_true", @@ -1427,6 +1477,8 @@ def main(): args.ansible_args = ansible_args if not args.setup_yml and "LSR_QEMU_SETUP_YML" in os.environ: args.setup_yml = os.environ["LSR_QEMU_SETUP_YML"].split(",") + if not args.cleanup_yml and "LSR_QEMU_CLEANUP_YML" in os.environ: + args.cleanup_yml = os.environ["LSR_QEMU_CLEANUP_YML"].split(",") logging.getLogger().setLevel(getattr(logging, args.log_level.upper())) # either image-name or image-file must be given if not any([args.image_name, args.image_file]) or all( @@ -1459,6 +1511,7 @@ def main(): use_snapshot=args.use_snapshot, use_ansible_log=True, setup_yml=args.setup_yml, + cleanup_yml=args.cleanup_yml, wait_on_qemu=args.wait_on_qemu, write_inventory=args.write_inventory, erase_old_snapshot=args.erase_old_snapshot,