--- # Copyright 2017, Rackspace US, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - name: Gather physical host facts setup: gather_subset: "network,virtual" delegate_to: "{{ physical_host }}" delegate_facts: true run_once: true - name: Pull systemd version command: "systemctl --version" changed_when: false register: systemd_version delegate_to: "{{ physical_host }}" tags: - skip_ansible_lint - always - name: Pull filesystem information command: "btrfs filesystem show --raw /var/lib/machines" changed_when: false register: filesystem delegate_to: "{{ physical_host }}" tags: - skip_ansible_lint - always - name: Set qgroup space fact set_fact: nspawn_host_qgroup_space_limit: "{{ filesystem.stdout.strip().splitlines()[-1].split()[3] }}" when: - nspawn_host_qgroup_space_limit == 'none' tags: - always - name: Set qgroup compression fact set_fact: nspawn_host_qgroup_compression_limit: "{{ filesystem.stdout.strip().splitlines()[-1].split()[3] }}" when: - nspawn_host_qgroup_compression_limit == 'none' tags: - always - name: Set facts set_fact: nspawn_systemd_version: "{{ systemd_version.stdout_lines[0].split()[-1] }}" tags: - always - name: Gather variables for each operating system include_vars: "{{ item }}" with_first_found: - "{{ hostvars[physical_host]['ansible_distribution'] | lower }}-{{ hostvars[physical_host]['ansible_distribution_version'] | lower }}.yml" - "{{ hostvars[physical_host]['ansible_distribution'] | lower }}-{{ hostvars[physical_host]['ansible_distribution_major_version'] | lower }}.yml" - "{{ hostvars[physical_host]['ansible_os_family'] | lower }}-{{ hostvars[physical_host]['ansible_distribution_major_version'] | lower }}.yml" - "{{ hostvars[physical_host]['ansible_distribution'] | lower }}.yml" - "{{ hostvars[physical_host]['ansible_os_family'] | lower }}.yml" tags: - always - name: Escape quote container name command: "systemd-escape {{ inventory_hostname }}" changed_when: false register: systemd_escape delegate_to: "{{ physical_host }}" tags: - skip_ansible_lint - always - name: Get container status command: machinectl status "{{ inventory_hostname }}" register: machinectl_container_status failed_when: false changed_when: false delegate_to: "{{ physical_host }}" - name: Get container image status command: machinectl image-status "{{ inventory_hostname }}" register: machinectl_container_image_status failed_when: false changed_when: false delegate_to: "{{ physical_host }}" - name: Get image status command: machinectl image-status "{{ container_image }}" register: machinectl_image_status failed_when: false changed_when: false delegate_to: "{{ physical_host }}" - name: Fail if base image does not exist fail: msg: > The base container image "{{ container_image }}" does not exist. Check the name and try again. when: - machinectl_image_status.rc != 0 - name: Locate nspawn config stat: path: "/etc/systemd/nspawn/{{ inventory_hostname }}.nspawn" register: nspawn_config_exists delegate_to: "{{ physical_host }}" - name: Slurp existing nspawn config slurp: src: "/etc/systemd/nspawn/{{ inventory_hostname }}.nspawn" register: nspawn_config changed_when: false delegate_to: "{{ physical_host }}" when: - nspawn_container_preserve_config | bool - nspawn_systemd_version | int > 219 - nspawn_config_exists.stat.exists | bool # Check for the existance of an nspawn configuration file. If found slurp it up # and use it as the base nspawn config file with the option to config template # override. - name: Copy container config (existing) config_template: content: "{{ nspawn_config.content | b64decode }}" dest: "/etc/systemd/nspawn/{{ inventory_hostname }}.nspawn" owner: "root" group: "root" mode: "0644" config_overrides: "{{ container_config_overrides | default({}) }}" config_type: "ini" register: container_config_existing delegate_to: "{{ physical_host }}" when: - nspawn_container_preserve_config | bool - nspawn_config_exists.stat.exists | bool - nspawn_systemd_version | int > 219 # If no nspawn configuration file exists, create a new config file using the # default template. - name: Copy container config (new) config_template: src: templates/container_config.nspawn.j2 dest: "/etc/systemd/nspawn/{{ inventory_hostname }}.nspawn" owner: "root" group: "root" mode: "0644" config_overrides: "{{ container_config_overrides | default({}) }}" config_type: "ini" register: container_config_new delegate_to: "{{ physical_host }}" when: - not nspawn_container_preserve_config | bool or not nspawn_config_exists.stat.exists | bool - nspawn_systemd_version | int > 219 - name: Copy container config (old) template: src: templates/container_config_old.nspawn.j2 dest: "/etc/systemd/system/systemd-nspawn@{{ systemd_escape.stdout }}.service" owner: "root" group: "root" mode: "0644" register: container_config_old delegate_to: "{{ physical_host }}" when: - nspawn_systemd_version | int < 220 - name: Notice existing container config changed debug: msg: >- The existing container config has changed. While this change has been noticed, no restart has been performed. By not restarting the container the system is preserving the state of the environment by not causing unexpected downtime. For these changes to go into effect the container must be restarted. This can be done using the following command, `machinectl [poweroff|start] {{ inventory_hostname }}`. To disable the configation saving set `nspawn_container_preserve_config` to false. when: - container_config_existing is changed - name: Clone the base container image command: machinectl clone "{{ container_image }}" "{{ inventory_hostname }}" when: - machinectl_container_image_status.rc != 0 register: machinectl_container_clone retries: 3 delay: 2 until: machinectl_container_clone is success delegate_to: "{{ physical_host }}" tags: - skip_ansible_lint - name: Set the qgroup limits block: - name: Set the qgroup size|compression limits on machines command: "btrfs qgroup limit {{ item }} /var/lib/machines/{{ inventory_hostname }}" changed_when: false delegate_to: "{{ physical_host }}" with_items: - "-e {{ nspawn_host_qgroup_space_limit }}" - "-c {{ nspawn_host_qgroup_compression_limit }}" when: - not nspawn_host_machine_quota_disabled rescue: - name: Notice regarding quota system debug: msg: >- There was an error processing the setup of qgroups. Check the system to ensure they're available otherwise disable the quota system by setting `nspawn_host_machine_quota_disabled` to true. - name: Container directories file: path: "{{ item }}" state: "directory" with_items: - "/openstack/{{ inventory_hostname }}" - "/openstack/backup/{{ inventory_hostname }}" - "/openstack/log/{{ inventory_hostname }}" - "/var/lib/machines/{{ inventory_hostname }}/var/log/journal" - "/var/lib/machines/{{ inventory_hostname }}/etc/systemd/network" - "/var/lib/machines/{{ inventory_hostname }}/etc/systemd/nspawn" - "/var/lib/machines/{{ inventory_hostname }}/var/lib/dbus" delegate_to: "{{ physical_host }}" - name: Container RO bind path cleanup file: path: "/var/lib/machines/{{ inventory_hostname }}{{ item.dest }}" state: "absent" with_items: "{{ nspawn_read_only_host_bindmount }}" delegate_to: "{{ physical_host }}" when: - machinectl_container_status.rc != 0 - name: Container inner service directories file: path: "/var/lib/machines/{{ inventory_hostname }}/{{ item.bind_dir_path }}" state: "directory" with_items: "{{ container_default_bind_mounts | union(container_bind_mounts | default([])) }}" delegate_to: "{{ physical_host }}" - name: Container outer service directories file: path: "{{ item.mount_path }}" state: "directory" with_items: "{{ container_default_bind_mounts | union(container_bind_mounts | default([])) }}" delegate_to: "{{ physical_host }}" - name: Generate machine-id command: "systemd-machine-id-setup --root=/var/lib/machines/{{ inventory_hostname }}" args: creates: "/var/lib/machines/{{ inventory_hostname }}/etc/machine-id" register: machineid_set retries: 3 delay: 2 until: machineid_set is success delegate_to: "{{ physical_host }}" tags: - skip_ansible_lint - name: Create dbus machine-id copy: src: "/var/lib/machines/{{ inventory_hostname }}/etc/machine-id" dest: "/var/lib/machines/{{ inventory_hostname }}/var/lib/dbus/machine-id" mode: "0444" remote_src: "yes" remote_user: root delegate_to: "{{ physical_host }}" - name: Create resolved link file: src: "/var/run/systemd/resolve/resolv.conf" dest: "/var/lib/machines/{{ inventory_hostname }}/etc/resolv.conf" force: true state: link delegate_to: "{{ physical_host }}" when: - nspawn_container_enable_resolved | bool # Some distros do not have access to systemd-resolved. If the option # `nspawn_container_enable_resolved` is disabled this will ensure functionality # in the absence of modern systemd. - name: Legacy resolvers block: - name: Check resolv.conf stat: path: "/var/lib/machines/{{ inventory_hostname }}/etc/resolv.conf" delegate_to: "{{ physical_host }}" register: nspawn_resolv_conf - name: Remove resolv.conf link file: path: "/var/lib/machines/{{ inventory_hostname }}/etc/resolv.conf" state: absent delegate_to: "{{ physical_host }}" when: - nspawn_resolv_conf.stat.islnk is defined and nspawn_resolv_conf.stat.islnk - name: Place resolv.conf copy: content: | nameserver {{ hostvars[physical_host]['ansible_mv_' + nspawn_networks['nspawn_address']['bridge']]['ipv4']['address'] }} search {{ container_domain }} dest: "/var/lib/machines/{{ inventory_hostname }}/etc/resolv.conf" delegate_to: "{{ physical_host }}" when: - not nspawn_container_enable_resolved | bool - name: Start new container (enable) systemd: daemon_reload: yes name: "systemd-nspawn@{{ systemd_escape.stdout }}" state: "{{ ((machinectl_container_clone is changed or container_config_new is changed or container_config_old is changed) | default(false)) | ternary('restarted', 'started') }}" enabled: "{{ (nspawn_systemd_version | int > 219) | ternary('true', 'false') }}" register: machinectl_start retries: 5 delay: 2 until: machinectl_start is success delegate_to: "{{ physical_host }}" - name: Generate hostname command: >- hostnamectl --machine="{{ inventory_hostname }}" {{ item }} --pretty --static --transient with_items: - "set-hostname {{ inventory_hostname | replace('_', '-') }}.{{ container_domain }}" - "set-location {{ physical_host }}" - "set-chassis container" - "set-deployment {{ container_domain }}" - "set-icon-name container" register: hostnamectl_set retries: 3 delay: 2 until: hostnamectl_set is success delegate_to: "{{ physical_host }}" tags: - skip_ansible_lint # This point the container is running. Delegation should no longer be required. # ============================================================================== - name: Run the systemd-networkd role include_role: name: systemd_networkd private: true vars: systemd_interface_cleanup: true systemd_run_networkd: true systemd_resolved_available: "{{ nspawn_container_enable_resolved }}" systemd_resolved: DNS: "{{ hostvars[physical_host]['ansible_mv_' + nspawn_networks['nspawn_address']['bridge']]['ipv4']['address'] }}" Domains: "{{ container_domain }}" systemd_networks: |- {% set _networks = [] %} {% for _, value in nspawn_combined_networks.items() %} {% if value.bridge is defined %} {% set _network = {'interface': 'mv-mv-' + value.bridge.split('br-')[-1]} %} {% else %} {% set _network = {'interface': value.interface} %} {% endif %} {% if value.address is defined %} {% set _ = _network.__setitem__('address', value.address) %} {% if (value.netmask is defined) and (_network.address != 'dhcp') %} {% set _ = _network.__setitem__('netmask', value.netmask) %} {% set prefix = (value.address + '/' + value.netmask) | ipaddr('prefix') %} {% set _ = _network.__setitem__('address', [value.address + '/' + prefix | string]) %} {% endif %} {% endif %} {% set _ = _network.__setitem__('usedns', (value.usedns | default(true) | bool) | ternary('yes', 'no')) %} {% set _ = _network.__setitem__('static_routes', value.static_routes | default([])) %} {% if value.gateway is defined %} {% set _ = _network.__setitem__('gateway', value.gateway) %} {% endif %} {% set _ = _network.__setitem__('mtu', value.mtu | default(1500 | string)) %} {% set _ = _networks.append(_network) %} {% endfor %} {{ _networks | sort(attribute='interface') }} tags: - network-config - name: Force all notified handlers now meta: flush_handlers - name: (RE)Gather facts post setup setup: gather_subset: "network,hardware,virtual"