diff --git a/ansible.cfg b/ansible.cfg index 73fcdfe..d0d26f1 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -14,6 +14,7 @@ timeout = 30 host_key_checking = false display_skipped_hosts = false deprecation_warnings = false +# task_timeout = 10 # callback_whitelist is deprecated # we only include here for backwards compatibility diff --git a/nfs_lazy_reboot.yml b/nfs_lazy_reboot.yml new file mode 100644 index 0000000..2744645 --- /dev/null +++ b/nfs_lazy_reboot.yml @@ -0,0 +1,44 @@ +--- +- name: Something + hosts: temp + become: true + gather_facts: true + + # With NFS share(s) being in a hung state, we cannot utilize `gather_facts` + # as that too hangs when it tries to figure out the current mounts on the + # system + tasks: + - name: Capture NFS mounts on host + ansible.builtin.set_fact: + captured_nfs_mounts: "{{ ansible_mounts | selectattr('fstype', 'search', 'nfs') }}" + + - name: Verify mount status and reboot + block: + - name: Verify mount status + ansible.builtin.command: "ls {{ item['mount'] }}" + timeout: 5 + register: r_verify_mounts + loop: "{{ captured_nfs_mounts }}" + loop_control: + label: "{{ item['mount'] }}" + + rescue: + - name: Debug item + ansible.builtin.debug: + msg: "{{ r_verify_mounts }}" + + - name: Group failed shares together + ansible.builtin.set_fact: + __failed_nfs_shares: + "{{ r_verify_mounts['results'] | selectattr('failed') | map(attribute='item') | list }}" + + - name: Lazily unmount failed shares + ansible.builtin.command: "umount -f -l {{ item['mount'] }}" + loop: "{{ __failed_nfs_shares }}" + loop_control: + label: "{{ item['mount'] }}" + + always: + - name: Reboot host # noqa: no-handler + ansible.builtin.import_role: + name: verified_reboot diff --git a/nfs_lazy_reboot2 copy.yml b/nfs_lazy_reboot2 copy.yml new file mode 100644 index 0000000..d14b5dc --- /dev/null +++ b/nfs_lazy_reboot2 copy.yml @@ -0,0 +1,54 @@ +--- +- name: Something + hosts: temp + become: true + gather_facts: false + + # With NFS share(s) being in a hung state, we cannot properly utilize + # `gather_facts` as that too hangs when it tries to figure out the current + # mounts on the system + tasks: + - name: Check for mounted NFS shares # noqa: command-instead-of-module + ansible.builtin.command: mount -t nfs,nfs4 + register: nfs_mounts_result + changed_when: false + failed_when: nfs_mounts_result['rc'] not in [0, 32] + + - name: Create a list of NFS mount points from command output + ansible.builtin.set_fact: + nfs_mount_list: "{{ nfs_mounts_result['stdout_lines'] | map('split') | map(attribute=2) | list }}" + + - name: Verify mount status and reboot + block: + - name: Verify mount status + ansible.builtin.command: "ls {{ item }}" + timeout: 5 + register: r_verify_mounts + loop: "{{ nfs_mount_list }}" + loop_control: + label: "{{ item }}" + + rescue: + - name: Debug item + ansible.builtin.debug: + msg: "{{ r_verify_mounts }}" + + - name: Group failed shares together + ansible.builtin.set_fact: + failed_nfs_shares: + "{{ r_verify_mounts['results'] | selectattr('failed') | map(attribute='item') | list }}" + + - name: Debugb failed_nfs_shares + ansible.builtin.debug: + msg: "{{ failed_nfs_shares }}" + + - name: Lazily unmount failed shares + ansible.builtin.command: "umount -f -l {{ item }}" + loop: "{{ failed_nfs_shares }}" + loop_control: + label: "{{ item }}" + + always: + - name: Reboot host if file changes # noqa: no-handler + ansible.builtin.import_role: + name: verified_reboot diff --git a/nfs_lazy_reboot2.yml b/nfs_lazy_reboot2.yml new file mode 100644 index 0000000..ae6dc21 --- /dev/null +++ b/nfs_lazy_reboot2.yml @@ -0,0 +1,49 @@ +--- +- name: Something + hosts: temp + become: true + gather_facts: false + + # With NFS share(s) being in a hung state, we cannot utilize `gather_facts` + # as that too hangs when it tries to figure out the current mounts on the + # system + tasks: + - name: Check for mounted NFS shares # noqa: command-instead-of-module + ansible.builtin.command: mount -t nfs,nfs4 + register: nfs_mounts_result + changed_when: false + failed_when: nfs_mounts_result['rc'] not in [0, 32] + + - name: Create a list of NFS mount points + ansible.builtin.set_fact: + nfs_mount_list: "{{ nfs_mounts_result['stdout_lines'] | map('split') | map(attribute=2) | list }}" + + - name: Verify mount status and reboot host + block: + - name: Verify mount status + ansible.builtin.command: "ls {{ item }}" + timeout: 5 + register: r_verify_mounts + loop: "{{ nfs_mount_list }}" + loop_control: + label: "{{ item }}" + + rescue: + - name: Group shares that failed check + ansible.builtin.set_fact: + failed_nfs_shares: + "{{ r_verify_mounts['results'] | selectattr('failed') | map(attribute='item') | list }}" + + - name: Lazily unmount the failed shares + ansible.builtin.command: "umount -f -l {{ item }}" + register: r_lazy_unmount + async: 30 + poll: 0 + loop: "{{ failed_nfs_shares }}" + loop_control: + label: "{{ item }}" + + always: + - name: Reboot host # noqa: no-handler + ansible.builtin.import_role: + name: verified_reboot diff --git a/nfs_lazy_reboot3.yml b/nfs_lazy_reboot3.yml new file mode 100644 index 0000000..7a941e4 --- /dev/null +++ b/nfs_lazy_reboot3.yml @@ -0,0 +1,46 @@ +--- +- name: Something + hosts: temp + become: true + gather_facts: false + + # With NFS share(s) being in a hung state, we cannot utilize `gather_facts` + # as that too hangs when it tries to figure out the current mounts on the + # system + tasks: + - name: Check for mounted NFS shares # noqa: command-instead-of-module + ansible.builtin.command: mount -t nfs,nfs4 + register: nfs_mounts_result + changed_when: false + failed_when: nfs_mounts_result['rc'] not in [0, 32] + + - name: Create a list of NFS mount points from command output + ansible.builtin.set_fact: + nfs_mount_list: "{{ nfs_mounts_result['stdout_lines'] | map('split') | map(attribute=2) | list }}" + + - name: Verify mount status + ansible.builtin.command: "ls {{ item }}" + timeout: 5 + register: r_verify_mounts + ignore_errors: true + loop: "{{ nfs_mount_list }}" + loop_control: + label: "{{ item }}" + + - name: Group failed shares together + ansible.builtin.set_fact: + failed_nfs_shares: + "{{ r_verify_mounts['results'] | selectattr('failed') | map(attribute='item') | list }}" + + - name: Lazily unmount failed shares + ansible.builtin.command: "umount -f -l {{ item }}" + async: 30 + poll: 0 + register: r_lazy_unmount + loop: "{{ failed_nfs_shares }}" + loop_control: + label: "{{ item }}" + + - name: Reboot host # noqa: no-handler + ansible.builtin.import_role: + name: verified_reboot diff --git a/nfs_lazy_reboot_debug.yml b/nfs_lazy_reboot_debug.yml new file mode 100644 index 0000000..a689723 --- /dev/null +++ b/nfs_lazy_reboot_debug.yml @@ -0,0 +1,23 @@ +--- +- name: Gather NFS mounts without using Ansible facts + hosts: temp + gather_facts: false + + tasks: + - name: Check for mounted NFS shares πŸ”Ž # noqa: command-instead-of-module + ansible.builtin.command: mount -t nfs,nfs4 + register: nfs_mounts_result + changed_when: false + failed_when: nfs_mounts_result.rc not in [0, 32] + + - name: Debug nfs_mounts_result πŸ”Ž + ansible.builtin.debug: + msg: "{{ nfs_mounts_result }}" + + - name: Create a list of NFS mount points from command output πŸ“ + ansible.builtin.set_fact: + nfs_mount_list: "{{ nfs_mounts_result.stdout_lines | map('split') | map(attribute=2) | list }}" + + - name: Display the discovered NFS mount points βœ… + ansible.builtin.debug: + msg: "Discovered NFS mounts: {{ nfs_mount_list }}" diff --git a/reboot_test3.yml b/reboot_test3.yml index 458e53b..3857b07 100644 --- a/reboot_test3.yml +++ b/reboot_test3.yml @@ -1,20 +1,8 @@ --- -# -l, --lazy -# Lazy unmount. Detach the filesystem from the file hierarchy now, -# and clean up all references to this filesystem as soon as it -# is not busy anymore. - -# A system reboot would be expected in near future if you’re going to use -# this option for network filesystem or local filesystem with submounts. -# -# The recommended use-case for umount -l is to prevent hangs on shutdown -# due to an unreachable network share where a normal umount will hang -# due to a downed server or a network partition. Remounts of the share -# will not be possible. - name: Something hosts: temp become: true - gather_facts: false + gather_facts: true vars: __nfs_src: 10.10.42.180 @@ -22,18 +10,17 @@ __nfs_mnt_pnt: /nfs/backups tasks: - - name: Lazily unmount the NFS share - ansible.builtin.command: "umount -f -l {{ __nfs_mnt_pnt }}" + - name: Verify mount status and reboot + block: + - name: Verify mount status + ansible.builtin.command: df + timeout: 5 - - name: Reboot host if file changes # noqa: no-handler - ansible.builtin.import_role: - name: verified_reboot + rescue: + - name: Lazily unmount the NFS share + ansible.builtin.command: "umount -f -l {{ __nfs_mnt_pnt }}" - - name: Unlazily re-mount the file system - ansible.posix.mount: - state: mounted - src: "{{ __nfs_src }}:{{ __nfs_share }}" - path: "{{ __nfs_mnt_pnt }}" - opts: rw,noatime - boot: false - fstype: nfs + always: + - name: Reboot host if file changes # noqa: no-handler + ansible.builtin.import_role: + name: verified_reboot diff --git a/scripts/write_file_msg2.py b/scripts/write_file_msg2.py new file mode 100644 index 0000000..30795f5 --- /dev/null +++ b/scripts/write_file_msg2.py @@ -0,0 +1,60 @@ +import os +import readline +import argparse +from datetime import datetime +import time + +# Tab completion setup for file paths +def complete_path(text, state): + expanded = os.path.expanduser(os.path.expandvars(text)) + + if os.path.isdir(expanded): + try: + entries = os.listdir(expanded) + matches = [os.path.abspath(os.path.join(expanded, entry)) + '/' + if os.path.isdir(os.path.join(expanded, entry)) + else os.path.abspath(os.path.join(expanded, entry)) + for entry in entries] + except FileNotFoundError: + matches = [] + else: + dirname = os.path.dirname(expanded) or '.' + basename = os.path.basename(expanded) + try: + entries = [entry for entry in os.listdir(dirname) if entry.startswith(basename)] + matches = [os.path.abspath(os.path.join(dirname, entry)) for entry in entries] + except FileNotFoundError: + matches = [] + + matches.sort() + try: + return matches[state] + except IndexError: + return None + +readline.set_completer_delims(' \t\n;') +readline.set_completer(complete_path) +readline.parse_and_bind('tab: complete') + +# Parse command-line arguments +parser = argparse.ArgumentParser(description="Write messages to a file.") +parser.add_argument("filename", nargs="?", help="Path to the file to write to.") +args = parser.parse_args() + +# Ask for filename if not provided +file_path = args.filename or input("Enter filename: ") + +# Message writing loop +name = "hi" +age = 30 +max_iterations = 1000 + +try: + for i in range(max_iterations): + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + message = "[{}] My name is {} and I am {} years old.\n".format(timestamp, name, age) + with open(file_path, "a") as file: + file.write(message) + time.sleep(1) +except KeyboardInterrupt: + print("\n[Interrupted] Ctrl+C detected. Stopped writing to file.") diff --git a/scripts/write_file_msg3.py b/scripts/write_file_msg3.py new file mode 100644 index 0000000..c9313f1 --- /dev/null +++ b/scripts/write_file_msg3.py @@ -0,0 +1,62 @@ +import os +import readline +import argparse +from datetime import datetime +import time + +# --- Tab Completion --- +def complete_path(text, state): + expanded = os.path.expanduser(os.path.expandvars(text)) + if os.path.isdir(expanded): + try: + entries = os.listdir(expanded) + matches = [os.path.abspath(os.path.join(expanded, entry)) + '/' + if os.path.isdir(os.path.join(expanded, entry)) + else os.path.abspath(os.path.join(expanded, entry)) + for entry in entries] + except FileNotFoundError: + matches = [] + else: + dirname = os.path.dirname(expanded) or '.' + basename = os.path.basename(expanded) + try: + entries = [entry for entry in os.listdir(dirname) if entry.startswith(basename)] + matches = [os.path.abspath(os.path.join(dirname, entry)) for entry in entries] + except FileNotFoundError: + matches = [] + matches.sort() + try: + return matches[state] + except IndexError: + return None + +readline.set_completer_delims(' \t\n;') +readline.set_completer(complete_path) +readline.parse_and_bind('tab: complete') + +# --- Argument Parsing --- +parser = argparse.ArgumentParser(description="Write messages to a file with optional verbosity and interval.") +parser.add_argument("filename", nargs="?", help="Path to the file to write to.") +parser.add_argument("--verbose", action="store_true", help="Print each message to console.") +parser.add_argument("--interval", type=float, default=1.0, help="Delay between writes in seconds.") +args = parser.parse_args() + +# --- Filename prompt fallback --- +file_path = args.filename or input("Enter filename: ") + +# --- Message writing loop --- +name = "hi" +age = 30 +max_iterations = 1000 + +try: + for i in range(max_iterations): + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + message = f"[{timestamp}] My name is {name} and I am {age} years old.\n" + with open(file_path, "a") as file: + file.write(message) + if args.verbose: + print(message.strip()) + time.sleep(args.interval) +except KeyboardInterrupt: + print("\n[Interrupted] Ctrl+C detected. Stopped writing to file.") diff --git a/timeout_test1.yml b/timeout_test1.yml new file mode 100644 index 0000000..e30b0fd --- /dev/null +++ b/timeout_test1.yml @@ -0,0 +1,22 @@ +--- +- name: Demonstrate module timeout + hosts: localhost + connection: local + gather_facts: false + + tasks: + - name: Check if network share is hung and reboot + block: + - name: Check mounts + ansible.builtin.command: sleep 30 + timeout: 10 + + rescue: + - name: Lazily unmount the file system + ansible.builtin.debug: + msg: unmount stuff goes here + + always: + - name: Reboot the host + ansible.builtin.debug: + msg: include_role verified_reboot diff --git a/timeout_test2.yml b/timeout_test2.yml new file mode 100644 index 0000000..af4f559 --- /dev/null +++ b/timeout_test2.yml @@ -0,0 +1,31 @@ +--- +- name: Demonstrate module timeout + hosts: temp + gather_facts: false + + tasks: + - name: Check mounts + ansible.builtin.command: umount -f -l /nfs/backups + register: r_check_mounts + async: 300 + poll: 0 + + - name: Debug r_check_mounts + ansible.builtin.debug: + var: r_check_mounts + + # - name: Check polling + # ansible.builtin.async_status: + # jid: "{{ r_check_mounts['ansible_job_id'] }}" + # register: async_check + # until: async_check['finished'] + # retries: 10 + # delay: 3 + + # - name: Debug async_check + # ansible.builtin.debug: + # var: async_check + + # - name: Hi + # ansible.builtin.debug: + # msg: "hi"