diff options
Diffstat (limited to 'roles/container_runtime/tasks')
-rw-r--r-- | roles/container_runtime/tasks/crio_firewall.yml | 40 | ||||
-rw-r--r-- | roles/container_runtime/tasks/main.yml | 85 | ||||
-rw-r--r-- | roles/container_runtime/tasks/package_docker.yml | 167 | ||||
-rw-r--r-- | roles/container_runtime/tasks/registry_auth.yml | 32 | ||||
-rw-r--r-- | roles/container_runtime/tasks/systemcontainer_crio.yml | 164 | ||||
-rw-r--r-- | roles/container_runtime/tasks/systemcontainer_docker.yml | 163 | ||||
-rw-r--r-- | roles/container_runtime/tasks/udev_workaround.yml | 24 |
7 files changed, 675 insertions, 0 deletions
diff --git a/roles/container_runtime/tasks/crio_firewall.yml b/roles/container_runtime/tasks/crio_firewall.yml new file mode 100644 index 000000000..fbd1ff515 --- /dev/null +++ b/roles/container_runtime/tasks/crio_firewall.yml @@ -0,0 +1,40 @@ +--- +- when: r_crio_firewall_enabled | bool and not r_crio_use_firewalld | bool + block: + - name: Add iptables allow rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: add + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_crio_os_firewall_allow }}" + + - name: Remove iptables rules + os_firewall_manage_iptables: + name: "{{ item.service }}" + action: remove + protocol: "{{ item.port.split('/')[1] }}" + port: "{{ item.port.split('/')[0] }}" + when: item.cond | default(True) + with_items: "{{ r_crio_os_firewall_deny }}" + +- when: r_crio_firewall_enabled | bool and r_crio_use_firewalld | bool + block: + - name: Add firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: enabled + when: item.cond | default(True) + with_items: "{{ r_crio_os_firewall_allow }}" + + - name: Remove firewalld allow rules + firewalld: + port: "{{ item.port }}" + permanent: true + immediate: true + state: disabled + when: item.cond | default(True) + with_items: "{{ r_crio_os_firewall_deny }}" diff --git a/roles/container_runtime/tasks/main.yml b/roles/container_runtime/tasks/main.yml new file mode 100644 index 000000000..6d68082b1 --- /dev/null +++ b/roles/container_runtime/tasks/main.yml @@ -0,0 +1,85 @@ +--- +- include_tasks: udev_workaround.yml + when: docker_udev_workaround | default(False) | bool + +- name: Add enterprise registry, if necessary + set_fact: + l2_docker_additional_registries: "{{ l2_docker_additional_registries + [openshift_docker_ent_reg] }}" + when: + - openshift.common.deployment_type == 'openshift-enterprise' + - openshift_docker_ent_reg != '' + - openshift_docker_ent_reg not in l2_docker_additional_registries + - not openshift_use_crio_only | bool + +- name: Use Package Docker if Requested + include_tasks: package_docker.yml + when: + - not openshift_docker_use_system_container + - not openshift_use_crio_only + +- name: Ensure /var/lib/containers exists + file: + path: /var/lib/containers + state: directory + +- name: Fix SELinux Permissions on /var/lib/containers + command: "restorecon -R /var/lib/containers/" + changed_when: false + +- name: Use System Container Docker if Requested + include_tasks: systemcontainer_docker.yml + when: + - openshift_docker_use_system_container + - not openshift_use_crio_only + +- name: Add CRI-O usage Requested + include_tasks: systemcontainer_crio.yml + when: + - openshift_use_crio + - openshift_docker_is_node_or_master | bool + +- name: stat the docker data dir + stat: + path: "{{ docker_default_storage_path }}" + register: dockerstat + +- when: + - openshift_use_crio + - dockerstat.stat.islnk is defined and not (dockerstat.stat.islnk | bool) + block: + - name: stop the current running docker + systemd: + state: stopped + name: "{{ openshift_docker_service_name }}" + + - name: copy "{{ docker_default_storage_path }}" to "{{ docker_alt_storage_path }}" + command: "cp -r {{ docker_default_storage_path }} {{ docker_alt_storage_path }}" + register: results + failed_when: + - results.rc != 0 + + - name: "Set the selinux context on {{ docker_alt_storage_path }}" + command: "semanage fcontext -a -e {{ docker_default_storage_path }} {{ docker_alt_storage_path }}" + register: results + failed_when: + - results.rc == 1 + - "'already exists' not in results.stderr" + + - name: "restorecon the {{ docker_alt_storage_path }}" + command: "restorecon -r {{ docker_alt_storage_path }}" + + - name: Remove the old docker location + file: + state: absent + path: "{{ docker_default_storage_path }}" + + - name: Setup the link + file: + state: link + src: "{{ docker_alt_storage_path }}" + path: "{{ docker_default_storage_path }}" + + - name: start docker + systemd: + state: started + name: "{{ openshift_docker_service_name }}" diff --git a/roles/container_runtime/tasks/package_docker.yml b/roles/container_runtime/tasks/package_docker.yml new file mode 100644 index 000000000..40ab75a25 --- /dev/null +++ b/roles/container_runtime/tasks/package_docker.yml @@ -0,0 +1,167 @@ +--- +- name: Get current installed Docker version + command: "{{ repoquery_installed }} --qf '%{version}' docker" + when: not openshift.common.is_atomic | bool + register: curr_docker_version + retries: 4 + until: curr_docker_version | succeeded + changed_when: false + +- name: Error out if Docker pre-installed but too old + fail: + msg: "Docker {{ curr_docker_version.stdout }} is installed, but >= 1.9.1 is required." + when: not curr_docker_version | skipped and curr_docker_version.stdout != '' and curr_docker_version.stdout | version_compare('1.9.1', '<') and not docker_version is defined + +- name: Error out if requested Docker is too old + fail: + msg: "Docker {{ docker_version }} requested, but >= 1.9.1 is required." + when: docker_version is defined and docker_version | version_compare('1.9.1', '<') + +# If a docker_version was requested, sanity check that we can install or upgrade to it, and +# no downgrade is required. +- name: Fail if Docker version requested but downgrade is required + fail: + msg: "Docker {{ curr_docker_version.stdout }} is installed, but version {{ docker_version }} was requested." + when: not curr_docker_version | skipped and curr_docker_version.stdout != '' and docker_version is defined and curr_docker_version.stdout | version_compare(docker_version, '>') + +# This involves an extremely slow migration process, users should instead run the +# Docker 1.10 upgrade playbook to accomplish this. +- name: Error out if attempting to upgrade Docker across the 1.10 boundary + fail: + msg: "Cannot upgrade Docker to >= 1.10, please upgrade or remove Docker manually, or use the Docker upgrade playbook if OpenShift is already installed." + when: not curr_docker_version | skipped and curr_docker_version.stdout != '' and curr_docker_version.stdout | version_compare('1.10', '<') and docker_version is defined and docker_version | version_compare('1.10', '>=') + +# Make sure Docker is installed, but does not update a running version. +# Docker upgrades are handled by a separate playbook. +# Note: The curr_docker_version.stdout check can be removed when https://github.com/ansible/ansible/issues/33187 gets fixed. +- name: Install Docker + package: name=docker{{ '-' + docker_version if docker_version is defined else '' }} state=present + when: not openshift.common.is_atomic | bool and not curr_docker_version | skipped and not curr_docker_version.stdout != '' + register: result + until: result | success + +- block: + # Extend the default Docker service unit file when using iptables-services + - name: Ensure docker.service.d directory exists + file: + path: "{{ docker_systemd_dir }}" + state: directory + + - name: Configure Docker service unit file + template: + dest: "{{ docker_systemd_dir }}/custom.conf" + src: custom.conf.j2 + notify: + - restart container runtime + when: not (os_firewall_use_firewalld | default(False)) | bool + +- stat: path=/etc/sysconfig/docker + register: docker_check + +- name: Set registry params + lineinfile: + dest: /etc/sysconfig/docker + regexp: '^{{ item.reg_conf_var }}=.*$' + line: "{{ item.reg_conf_var }}='{{ item.reg_fact_val | oo_prepend_strings_in_list(item.reg_flag ~ ' ') | join(' ') }}'" + when: + - item.reg_fact_val != [] + - docker_check.stat.isreg is defined + - docker_check.stat.isreg + with_items: + - reg_conf_var: ADD_REGISTRY + reg_fact_val: "{{ l2_docker_additional_registries }}" + reg_flag: --add-registry + - reg_conf_var: BLOCK_REGISTRY + reg_fact_val: "{{ l2_docker_blocked_registries }}" + reg_flag: --block-registry + - reg_conf_var: INSECURE_REGISTRY + reg_fact_val: "{{ l2_docker_insecure_registries }}" + reg_flag: --insecure-registry + notify: + - restart container runtime + +- name: Place additional/blocked/insecure registries in /etc/containers/registries.conf + template: + dest: "{{ containers_registries_conf_path }}" + src: registries.conf + when: openshift_docker_use_etc_containers | bool + notify: + - restart container runtime + +- name: Set Proxy Settings + lineinfile: + dest: /etc/sysconfig/docker + regexp: '^{{ item.reg_conf_var }}=.*$' + line: "{{ item.reg_conf_var }}='{{ item.reg_fact_val }}'" + state: "{{ 'present' if item.reg_fact_val != '' else 'absent'}}" + with_items: + - reg_conf_var: HTTP_PROXY + reg_fact_val: "{{ docker_http_proxy }}" + - reg_conf_var: HTTPS_PROXY + reg_fact_val: "{{ docker_https_proxy }}" + - reg_conf_var: NO_PROXY + reg_fact_val: "{{ docker_no_proxy }}" + notify: + - restart container runtime + when: + - docker_check.stat.isreg is defined + - docker_check.stat.isreg + - docker_http_proxy != '' or docker_https_proxy != '' + +- name: Set various Docker options + lineinfile: + dest: /etc/sysconfig/docker + regexp: '^OPTIONS=.*$' + line: "OPTIONS='\ + {% if ansible_selinux.status | default(None) == 'enabled' and openshift_docker_selinux_enabled | default(true) | bool %} --selinux-enabled {% endif %} \ + {% if openshift_docker_log_driver | bool %} --log-driver {{ openshift_docker_log_driver }}{% endif %} \ + {% if l2_docker_log_options != [] %} {{ l2_docker_log_options | oo_split() | oo_prepend_strings_in_list('--log-opt ') | join(' ')}}{% endif %} \ + {% if openshift_docker_hosted_registry_insecure and (openshift_docker_hosted_registry_network | bool) %} --insecure-registry={{ openshift_docker_hosted_registry_network }} {% endif %} \ + {% if docker_options is defined %} {{ docker_options }}{% endif %} \ + {% if openshift_docker_options %} {{ openshift_docker_options }}{% endif %} \ + {% if openshift_docker_disable_push_dockerhub %} --confirm-def-push={{ openshift_docker_disable_push_dockerhub | bool }}{% endif %} \ + --signature-verification={{ openshift_docker_signature_verification | bool }}'" + when: docker_check.stat.isreg is defined and docker_check.stat.isreg + notify: + - restart container runtime + +- stat: path=/etc/sysconfig/docker-network + register: sysconfig_docker_network_check + +- name: Configure Docker Network OPTIONS + lineinfile: + dest: /etc/sysconfig/docker-network + regexp: '^DOCKER_NETWORK_OPTIONS=.*$' + line: "DOCKER_NETWORK_OPTIONS='\ + {% if openshift.node is defined and openshift.node.sdn_mtu is defined %} --mtu={{ openshift.node.sdn_mtu }}{% endif %}'" + when: + - sysconfig_docker_network_check.stat.isreg is defined + - sysconfig_docker_network_check.stat.isreg + notify: + - restart container runtime + +# The following task is needed as the systemd module may report a change in +# state even though docker is already running. +- name: Detect if docker is already started + command: "systemctl show docker -p ActiveState" + changed_when: False + register: r_docker_already_running_result + +- name: Start the Docker service + systemd: + name: docker + enabled: yes + state: started + daemon_reload: yes + register: r_docker_package_docker_start_result + until: not r_docker_package_docker_start_result | failed + retries: 3 + delay: 30 + +- set_fact: + docker_service_status_changed: "{{ (r_docker_package_docker_start_result | changed) and (r_docker_already_running_result.stdout != 'ActiveState=active' ) }}" + +- meta: flush_handlers + +# This needs to run after docker is restarted to account for proxy settings. +- include_tasks: registry_auth.yml diff --git a/roles/container_runtime/tasks/registry_auth.yml b/roles/container_runtime/tasks/registry_auth.yml new file mode 100644 index 000000000..2c7bc5711 --- /dev/null +++ b/roles/container_runtime/tasks/registry_auth.yml @@ -0,0 +1,32 @@ +--- +- name: Check for credentials file for registry auth + stat: + path: "{{ docker_cli_auth_config_path }}/config.json" + when: oreg_auth_user is defined + register: docker_cli_auth_credentials_stat + +- name: Create credentials for docker cli registry auth + command: "docker --config={{ docker_cli_auth_config_path }} login -u {{ oreg_auth_user }} -p {{ oreg_auth_password }} {{ oreg_host }}" + register: openshift_docker_credentials_create_res + retries: 3 + delay: 5 + until: openshift_docker_credentials_create_res.rc == 0 + when: + - not openshift_docker_alternative_creds | bool + - oreg_auth_user is defined + - (not docker_cli_auth_credentials_stat.stat.exists or oreg_auth_credentials_replace) | bool + +# docker_creds is a custom module from lib_utils +# 'docker login' requires a docker.service running on the local host, this is an +# alternative implementation for non-docker hosts. This implementation does not +# check the registry to determine whether or not the credentials will work. +- name: Create credentials for docker cli registry auth (alternative) + docker_creds: + path: "{{ docker_cli_auth_config_path }}" + registry: "{{ oreg_host }}" + username: "{{ oreg_auth_user }}" + password: "{{ oreg_auth_password }}" + when: + - openshift_docker_alternative_creds | bool + - oreg_auth_user is defined + - (not docker_cli_auth_credentials_stat.stat.exists or oreg_auth_credentials_replace) | bool diff --git a/roles/container_runtime/tasks/systemcontainer_crio.yml b/roles/container_runtime/tasks/systemcontainer_crio.yml new file mode 100644 index 000000000..8dcfe60ef --- /dev/null +++ b/roles/container_runtime/tasks/systemcontainer_crio.yml @@ -0,0 +1,164 @@ +--- +# TODO: Much of this file is shared with container engine tasks + +- name: Ensure container-selinux is installed + package: + name: container-selinux + state: present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + +- name: Check we are not using node as a Docker container with CRI-O + fail: msg='Cannot use CRI-O with node configured as a Docker container' + when: + - openshift.common.is_containerized | bool + - not openshift.common.is_node_system_container | bool + +# Used to pull and install the system container +- name: Ensure atomic is installed + package: + name: atomic + state: present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + +# At the time of writing the atomic command requires runc for it's own use. This +# task is here in the even that the atomic package ever removes the dependency. +- name: Ensure runc is installed + package: + name: runc + state: present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + + +- name: Check that overlay is in the kernel + shell: lsmod | grep overlay + register: l_has_overlay_in_kernel + ignore_errors: yes + failed_when: false + +- when: l_has_overlay_in_kernel.rc != 0 + block: + + - name: Add overlay to modprobe.d + template: + dest: /etc/modules-load.d/overlay.conf + src: overlay.conf.j2 + backup: yes + + - name: Manually modprobe overlay into the kernel + command: modprobe overlay + + - name: Enable and start systemd-modules-load + service: + name: systemd-modules-load + enabled: yes + state: restarted + +- name: Ensure proxies are in the atomic.conf + include_role: + name: openshift_atomic + tasks_from: proxy + +- block: + + - name: Set CRI-O image defaults + set_fact: + l_crio_image_prepend: "docker.io/gscrivano" + l_crio_image_name: "cri-o-fedora" + l_crio_image_tag: "latest" + + - name: Use Centos based image when distribution is CentOS + set_fact: + l_crio_image_name: "cri-o-centos" + when: ansible_distribution == "CentOS" + + - name: Set CRI-O image tag + set_fact: + l_crio_image_tag: "{{ l_openshift_image_tag }}" + when: + - openshift_deployment_type == 'openshift-enterprise' + + - name: Use RHEL based image when distribution is Red Hat + set_fact: + l_crio_image_prepend: "registry.access.redhat.com/openshift3" + l_crio_image_name: "cri-o" + when: ansible_distribution == "RedHat" + + - name: Set the full image name + set_fact: + l_crio_image: "{{ l_crio_image_prepend }}/{{ l_crio_image_name }}:{{ l_crio_image_tag }}" + + # For https://github.com/openshift/aos-cd-jobs/pull/624#pullrequestreview-61816548 + - name: Use a specific image if requested + set_fact: + l_crio_image: "{{ openshift_crio_systemcontainer_image_override }}" + when: + - openshift_crio_systemcontainer_image_override is defined + - openshift_crio_systemcontainer_image_override != "" + + # Be nice and let the user see the variable result + - debug: + var: l_crio_image + +# NOTE: no_proxy added as a workaround until https://github.com/projectatomic/atomic/pull/999 is released +- name: Pre-pull CRI-O System Container image + command: "atomic pull --storage ostree {{ l_crio_image }}" + changed_when: false + environment: + NO_PROXY: "{{ openshift.common.no_proxy | default('') }}" + + +- name: Install CRI-O System Container + oc_atomic_container: + name: "cri-o" + image: "{{ l_crio_image }}" + state: latest + +- name: Remove CRI-O default configuration files + file: + path: "{{ item }}" + state: absent + with_items: + - /etc/cni/net.d/200-loopback.conf + - /etc/cni/net.d/100-crio-bridge.conf + +- name: Create the CRI-O configuration + template: + dest: /etc/crio/crio.conf + src: crio.conf.j2 + backup: yes + +- name: Ensure CNI configuration directory exists + file: + path: /etc/cni/net.d/ + state: directory + +- name: setup firewall for CRI-O + include_tasks: crio_firewall.yml + static: yes + +- name: Configure the CNI network + template: + dest: /etc/cni/net.d/openshift-sdn.conf + src: 80-openshift-sdn.conf.j2 + +- name: Start the CRI-O service + systemd: + name: "cri-o" + enabled: yes + state: started + daemon_reload: yes + register: start_result + +- meta: flush_handlers + +# If we are using crio only, docker.service might not be available for +# 'docker login' +- include_tasks: registry_auth.yml + vars: + openshift_docker_alternative_creds: "{{ openshift_use_crio_only }}" diff --git a/roles/container_runtime/tasks/systemcontainer_docker.yml b/roles/container_runtime/tasks/systemcontainer_docker.yml new file mode 100644 index 000000000..84217e50c --- /dev/null +++ b/roles/container_runtime/tasks/systemcontainer_docker.yml @@ -0,0 +1,163 @@ +--- +# If docker_options are provided we should fail. We should not install docker and ignore +# the users configuration. NOTE: docker_options == inventory:openshift_docker_options +- name: Fail quickly if openshift_docker_options are set + assert: + that: + - "{% if not openshift_docker_options %}1{% else %}0{% endif %}" + msg: | + Docker via System Container does not allow for the use of the openshift_docker_options + variable. If you want to use openshift_docker_options you will need to use the + traditional docker package install. Otherwise, comment out openshift_docker_options + in your inventory file. + +- name: Ensure container-selinux is installed + package: + name: container-selinux + state: present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + +# Used to pull and install the system container +- name: Ensure atomic is installed + package: + name: atomic + state: present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + +# At the time of writing the atomic command requires runc for it's own use. This +# task is here in the even that the atomic package ever removes the dependency. +- name: Ensure runc is installed + package: + name: runc + state: present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + +# Make sure Docker is installed so we are able to use the client +- name: Install Docker so we can use the client + package: name=docker{{ '-' + docker_version if docker_version is defined else '' }} state=present + when: not openshift.common.is_atomic | bool + register: result + until: result | success + +# Make sure docker is disabled. Errors are ignored. +- name: Disable Docker + systemd: + name: docker + enabled: no + state: stopped + daemon_reload: yes + ignore_errors: True + register: r_docker_systemcontainer_docker_stop_result + until: not r_docker_systemcontainer_docker_stop_result | failed + retries: 3 + delay: 30 + +- name: Ensure proxies are in the atomic.conf + include_role: + name: openshift_atomic + tasks_from: proxy + +- block: + + - name: Set to default prepend + set_fact: + l_docker_image_prepend: "gscrivano" + l_docker_image_tag: "latest" + + - name: Set container engine image tag + set_fact: + l_docker_image_tag: "{{ l_openshift_image_tag }}" + when: + - openshift_deployment_type == 'openshift-enterprise' + + - name: Use Red Hat Registry for image when distribution is Red Hat + set_fact: + l_docker_image_prepend: "registry.access.redhat.com/openshift3" + when: ansible_distribution == 'RedHat' + + - name: Use Fedora Registry for image when distribution is Fedora + set_fact: + l_docker_image_prepend: "registry.fedoraproject.org/f25" + when: ansible_distribution == 'Fedora' + + - name: Set the full image name + set_fact: + l_docker_image: "{{ l_docker_image_prepend }}/{{ openshift_docker_service_name }}:{{ l_docker_image_tag }}" + + # For https://github.com/openshift/openshift-ansible/pull/5354#issuecomment-328552959 + - name: Use a specific image if requested + set_fact: + l_docker_image: "{{ openshift_docker_systemcontainer_image_override }}" + when: + - openshift_docker_systemcontainer_image_override is defined + - openshift_docker_systemcontainer_image_override != "" + + # Be nice and let the user see the variable result + - debug: + var: l_docker_image + +# NOTE: no_proxy added as a workaround until https://github.com/projectatomic/atomic/pull/999 is released +- name: Pre-pull Container Engine System Container image + command: "atomic pull --storage ostree {{ l_docker_image }}" + changed_when: false + environment: + NO_PROXY: "{{ docker_no_proxy }}" + + +- name: Ensure container-engine.service.d directory exists + file: + path: "{{ container_engine_systemd_dir }}" + state: directory + +- name: Ensure /etc/docker directory exists + file: + path: "{{ docker_conf_dir }}" + state: directory + +- name: Install Container Engine System Container + oc_atomic_container: + name: "{{ openshift_docker_service_name }}" + image: "{{ l_docker_image }}" + state: latest + +- name: Configure Container Engine Service File + template: + dest: "{{ container_engine_systemd_dir }}/custom.conf" + src: systemcontainercustom.conf.j2 + +# Configure container-engine using the container-daemon.json file +# NOTE: daemon.json and container-daemon.json have been seperated to avoid +# collision. +- name: Configure Container Engine + template: + dest: "{{ docker_conf_dir }}/container-daemon.json" + src: daemon.json + +# Enable and start the container-engine service +- name: Start the Container Engine service + systemd: + name: "{{ openshift_docker_service_name }}" + enabled: yes + state: started + daemon_reload: yes + register: r_docker_systemcontainer_docker_start_result + until: not r_docker_systemcontainer_docker_start_result | failed + retries: 3 + delay: 30 + +- set_fact: + docker_service_status_changed: "{{ r_docker_systemcontainer_docker_start_result | changed }}" + +- meta: flush_handlers + +# Since docker is running as a system container, docker login will fail to create +# credentials. Use alternate method if requiring authenticated registries. +- include_tasks: registry_auth.yml + vars: + openshift_docker_alternative_creds: True diff --git a/roles/container_runtime/tasks/udev_workaround.yml b/roles/container_runtime/tasks/udev_workaround.yml new file mode 100644 index 000000000..257c3123d --- /dev/null +++ b/roles/container_runtime/tasks/udev_workaround.yml @@ -0,0 +1,24 @@ +--- + +- name: Getting current systemd-udevd exec command + command: grep -e "^ExecStart=" /lib/systemd/system/systemd-udevd.service + changed_when: false + register: udevw_udev_start_cmd + +- name: Assure systemd-udevd.service.d directory exists + file: + path: "{{ udevw_udevd_dir }}" + state: directory + +- name: Create systemd-udevd override file + copy: + content: | + [Service] + #Need blank ExecStart to "clear" pre-existing one + ExecStart= + {{ udevw_udev_start_cmd.stdout }} --event-timeout=300 + dest: "{{ udevw_udevd_dir }}/override.conf" + owner: root + mode: "0644" + notify: + - restart udev |