--- # Copyright 2014, 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: Read custom facts from previous runs setup: filter: ansible_local delegate_to: "{{ physical_host }}" tags: - always - name: Check for lxc volume group shell: "(which vgs > /dev/null && vgs | grep -o '{{ lxc_container_vg_name }}') || false" register: vg_result failed_when: false changed_when: vg_result.rc != 0 delegate_to: "{{ physical_host }}" when: lxc_container_backing_store is not defined or lxc_container_backing_store == "lvm" tags: - lxc_container_create-vg-detect - name: Set container backend to "dir" or "lvm" based on whether the lxc VG was found set_fact: lxc_container_backing_store: "{{ (vg_result.rc != 0) | ternary('dir', 'lvm') }}" when: vg_result.rc is defined tags: - lxc_container_create-vg-detect - name: Container service directories file: path: "{{ item }}" state: "directory" with_items: - "/openstack/{{ inventory_hostname }}" - "/openstack/backup/{{ inventory_hostname }}" - "/openstack/log/{{ inventory_hostname }}" - "{{ lxc_container_directory }}/{{ inventory_hostname }}" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-directories - name: LXC autodev setup template: src: "autodev.j2" dest: "/var/lib/lxc/{{ inventory_hostname }}/autodev" owner: "root" group: "root" mode: "0755" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-autodev - name: Create container lxc_container: name: "{{ inventory_hostname }}" container_log: true container_log_level: "{{ (debug | bool) | ternary('DEBUG', 'INFO') }}" config: "{{ lxc_container_config }}" template: "{{ lxc_container_template }}" state: started backing_store: "{{ lxc_container_backing_store }}" directory: "{{ lxc_container_rootfs_directory }}" fs_size: "{{ properties.container_fs_size | default(lxc_container_fs_size) }}" fs_type: "{{ lxc_container_fs_type }}" vg_name: "{{ lxc_container_vg_name }}" template_options: "{{ lxc_container_download_template_options }}" delegate_to: "{{ physical_host }}" when: > lxc_container_backing_store != "overlayfs" or (lxc_container_backing_store == 'lvm' and not (lxc_container_backing_method is defined and lxc_container_backing_method == 'copy-on-write')) tags: - lxc_container_create-create # Due to https://github.com/ansible/ansible-modules-extras/issues/2577 the # next two tasks do not use the lxc_container module. # TODO(odyssey4me): Revisit this once a fix has merged - name: Check if container exists (copy-on-write backing store) command: "lxc-info -n {{ inventory_hostname }}" failed_when: false delegate_to: "{{ physical_host }}" register: lxc_container_info when: > lxc_container_backing_store == "overlayfs" or (lxc_container_backing_store == 'lvm' and lxc_container_backing_method is defined and lxc_container_backing_method == 'copy-on-write') # Due to https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/1591510 # the '-B' option is used, instead of the more understandable # '--backingstore'. # TODO(odyssey4me): Revisit this once a fix has merged # NOTE(hwoarang) lxc-copy is only available since lxc-2.0.0 so emulate # its behavior using the old lxc-clone command. This is only a problem # on openSUSE so it's safe to remove it when lxc-2.X becomes the default # option for openSUSE in the openstack-ansible-lxc_hosts role. - block: - name: Create container (copy-on-write backing store) command: > lxc-copy --snapshot -B {{ lxc_container_backing_store }} --name {{ lxc_container_base_name }} --newname {{ inventory_hostname }} -L {{ properties.container_fs_size | default(lxc_container_fs_size) }} --logfile {{ lxc_container_log_path }}/lxc-{{ inventory_hostname }}.log --logpriority {{ (debug | bool) | ternary('DEBUG', 'INFO') }} delegate_to: "{{ physical_host }}" rescue: # NOTE(hwoarang) lxc-clone does not support the common # --logfile/logpriority options so we just redirect everything to the log # which is probably the best we can do. - name: Create container (copy-on-write backing store) (fallback) shell: > lxc-clone -s -B {{ lxc_container_backing_store }} -L {{ properties.container_fs_size | default(lxc_container_fs_size) }} {{ lxc_container_base_name }} {{ inventory_hostname }} &>> {{ lxc_container_log_path }}/lxc-{{ inventory_hostname }}.log delegate_to: "{{ physical_host }}" when: > (lxc_container_backing_store == "overlayfs" or (lxc_container_backing_store == 'lvm' and lxc_container_backing_method is defined and lxc_container_backing_method == 'copy-on-write')) and lxc_container_info.rc != 0 tags: - lxc_container_create-create - name: Check container state command: "lxc-info -n {{ inventory_hostname }} --state" failed_when: false changed_when: false delegate_to: "{{ physical_host }}" register: _lxc_container_state # Due to https://github.com/ansible/ansible-modules-extras/issues/2691 # this uses the LXC CLI tools to ensure that we get logging. # TODO(odyssey4me): revisit this once the bug is fixed and released - name: Start the container if it is not already running command: > lxc-start --daemon --name {{ inventory_hostname }} --logfile {{ lxc_container_log_path }}/lxc-{{ inventory_hostname }}.log --logpriority {{ (debug | bool) | ternary('DEBUG', 'INFO') }} delegate_to: "{{ physical_host }}" when: _lxc_container_state.stdout.find('STOPPED') != -1 - name: Execute container commands lxc_container: name: "{{ inventory_hostname }}" container_command: | {{ lxc_container_commands }} delegate_to: "{{ physical_host }}" tags: - lxc_container_create-commands - name: Write default container config lineinfile: dest: "/var/lib/lxc/{{ inventory_hostname }}/config" line: "{{ item | replace('=', ' = ') | regex_replace('\\s+', ' ') }}" backup: "true" with_items: "{{ lxc_container_default_config_list | union(lxc_container_config_list) }}" delegate_to: "{{ physical_host }}" notify: - Lxc container restart tags: - lxc_container_create-config - name: Ensure bind mount host directories exists file: path: "{{ item['host_directory'] }}" state: "directory" recurse: true with_items: "{{ lxc_container_default_bind_mounts | union(lxc_container_bind_mounts) }}" delegate_to: "{{ physical_host }}" - name: Add bind mount configuration to container lineinfile: dest: "/var/lib/lxc/{{ inventory_hostname }}/config" line: "lxc.mount.entry = {{ item['host_directory'] }} {{ item['container_directory'].lstrip('/') }} none bind 0 0" backup: "true" with_items: "{{ lxc_container_default_bind_mounts | union(lxc_container_bind_mounts) }}" delegate_to: "{{ physical_host }}" notify: - Lxc container restart tags: - lxc_container_create-config # NOTE(cloudnull): Should a container already be up and running with a defined container interface # the shell command will use the MAC address already set within the container as # it's value. This allows the tasks to remain idempotent while ensuring that a # container restart isn't required to set a static mac. - name: Set define static mac address from an existing interface shell: | if lxc-attach -n {{ inventory_hostname }} -- cat /sys/class/net/{{ item.value.interface }}/address; then lxc-attach -n {{ inventory_hostname }} -- cat /sys/class/net/{{ item.value.interface }}/address > /var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.hwaddr fi args: executable: /bin/bash creates: "/var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.hwaddr" with_dict: "{{ container_networks | default({}) }}" delegate_to: "{{ physical_host }}" failed_when: false when: - lxc_container_fixed_mac | bool tags: - lxc_container_create-networks # NOTE(cloudnull): This task is being done to allow a container to have a static mac address. # before this task a container had a dynamic mac address which would # change when a container was restarted. This restart process causes terrible # issues in several network intensive systems (RabbitMQ, Neutron, etc). To # resolve the rotating mac address issue this task is setting the mac in a hwaddr # file and a lookup is being used in the container-interface.ini template to render # the static hardware address as expected. - name: Set unique interface mac address (when no facts exist) environment: hexchars: "0123456789abcdef" shell: | echo "00:16:3e$( for i in {1..6}; do echo -n ${hexchars:$(( $RANDOM % 16 )):1} done | sed -e 's/\(..\)/:\1/g' )" > /var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.hwaddr args: executable: /bin/bash creates: "/var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.hwaddr" with_dict: "{{ container_networks | default({}) }}" delegate_to: "{{ physical_host }}" when: - lxc_container_fixed_mac | bool - (ansible_local is not defined or 'mac' not in ansible_local or inventory_hostname not in ansible_local['mac']) tags: - lxc_container_create-networks # NOTE(palendae): If we have saved MACs, write those out instead of generating new ones. # Parentheses on the mac in ansible_local check to make the YAML parser happy. - name: Reuse saved interface mac address from host facts shell: | echo {{ item.value }} > /var/lib/lxc/{{ inventory_hostname }}/{{ item.key }}.hwaddr args: executable: /bin/bash creates: "/var/lib/lxc/{{ inventory_hostname }}/{{ item.key }}.hwaddr" with_dict: "{{ ansible_local['mac'][inventory_hostname] | default({}) }}" delegate_to: "{{ physical_host }}" when: - lxc_container_fixed_mac | bool - ansible_local is defined - ('mac' in ansible_local) - inventory_hostname in ansible_local['mac'] tags: - lxc_container_create-networks - name: Gather hardware addresses to be used as facts command: cat /var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.hwaddr changed_when: false register: macs with_dict: "{{ container_networks | default({}) }}" delegate_to: "{{ physical_host }}" when: - lxc_container_fixed_mac | bool tags: - lxc_container_create-networks # NOTE(cloudnull): To dynamically set the the mac address "facts" Ansible line format is being used - name: Set fixed hardware address fact set_fact: "{{item.item.value.interface }}_mac_address={{ item.stdout }}" with_items: - "{{ macs.results }}" when: - lxc_container_fixed_mac | bool tags: - lxc_container_create-networks # NOTE(palendae): If a unique identifier (like the hostname) is not provided as the marker, only one block will be written. # Each host will be written once, and whichever one came last will be the only one in the file. - name: Ensure MAC address fact cache is up-to-date blockinfile: dest: /etc/ansible/facts.d/mac.fact marker: "# {mark} Managed by OpenStack-Ansible {{ inventory_hostname }}" block: "{{ lookup('template', 'macs.fact.j2') }}" create: yes when: - lxc_container_fixed_mac | bool delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks # NOTE(palendae): This is necessary to read any local facts in to the 'ansible_local' dict. - name: Read local facts in for use setup: filter: ansible_local when: - lxc_container_fixed_mac | bool delegate_to: "{{ physical_host }}" tags: - always - name: LXC host config for container networks template: src: "container-interface.ini.j2" dest: "/var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.ini" owner: "root" group: "root" mode: "0644" with_dict: "{{ container_networks | default({}) }}" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks - name: Create start lxc_container: name: "{{ inventory_hostname }}" state: started delegate_to: "{{ physical_host }}" tags: - lxc_container_create-create - name: Drop container network file (interfaces) template: src: "{{ lxc_container_interface }}" dest: "{{ lxc_container_interface_target }}" owner: "root" group: "root" mode: "0644" with_dict: "{{ container_networks | default({}) }}" tags: - lxc_container_create-setup - name: Drop container network file (routes) template: src: "{{ lxc_container_route_interface }}" dest: "{{ lxc_container_default_route_interfaces }}" owner: "root" group: "root" mode: "0644" when: - lxc_container_route_interface is defined - lxc_container_default_route_interfaces is defined - item.value.static_routes is defined or (item.value.gateway is defined and ansible_pkg_mgr == "zypper") with_dict: "{{ container_networks | default({}) }}" tags: - lxc_container_create-setup - name: Drop container setup script template: src: "container-setup.sh.j2" dest: "/opt/container-setup.sh" owner: "root" group: "root" mode: "0755" tags: - lxc_container_create-setup - name: Run container setup script command: /opt/container-setup.sh register: container_setup changed_when: false failed_when: container_setup.rc != 0 tags: - lxc_container_create-setup # NOTE(major): the lxc.network.veth.pair line must appear *immediately* after # the lxc.network.name congfiguration line or it will be ignored. That's why # you'll find a "insertafter" in this YAML block. - name: Add veth pair name to match container name lineinfile: dest: "/var/lib/lxc/{{ inventory_hostname }}/config" line: "lxc.network.veth.pair = {{ lxc_container_network_veth_pair_prefix }}_eth0" insertafter: "^lxc.network.name" backup: "true" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks - name: Container network includes lineinfile: dest: "/var/lib/lxc/{{ inventory_hostname }}/config" line: "lxc.include = /var/lib/lxc/{{ inventory_hostname }}/{{ item.value.interface }}.ini" backup: "true" with_dict: "{{ container_networks | default({}) }}" when: item.value.interface is defined delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks - name: Create wiring script copy: src: "lxc-veth-wiring.sh" dest: "/usr/local/bin/lxc-veth-wiring" owner: "root" group: "root" mode: "0755" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks - name: Run container veth wiring script command: > /usr/local/bin/lxc-veth-wiring "{{ inventory_hostname }}" "{{ lxc_container_network_veth_pair[-15:] }}" "{{ item.value.interface }}" "{{ item.value.bridge }}" register: wiring_script with_dict: "{{ container_networks | default({}) }}" when: - item.value.interface is defined - item.value.type is not defined or item.value.type == 'veth' failed_when: wiring_script.rc not in [3, 0] changed_when: wiring_script.rc == 3 delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks # Adds post-down and pre-start hooks - name: Drop veth cleanup script template: src: "veth-cleanup.sh.j2" dest: "/var/lib/lxc/{{ inventory_hostname }}/veth-cleanup.sh" owner: "root" group: "root" mode: "0755" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks # This is being defined due to an issue with dangling veth pairs. # TODO(someone) This should be removed once an upstream patch has # been submitted to either the kernel or LXC to fix the veth issues. # Container restart is not happening here because it's not needed. - name: Defines a pre and post hook script lineinfile: dest: "/var/lib/lxc/{{ inventory_hostname }}/config" line: "{{ item }}" backup: "true" with_items: - "lxc.hook.pre-start = /var/lib/lxc/{{ inventory_hostname }}/veth-cleanup.sh" - "lxc.hook.post-stop = /var/lib/lxc/{{ inventory_hostname }}/veth-cleanup.sh" delegate_to: "{{ physical_host }}" tags: - lxc_container_create-networks # Flush the handlers to ensure the container and networking is online. - meta: flush_handlers - name: Wait for container connectivity wait_for_connection: connect_timeout: "{{ lxc_container_wait_params.connect_timeout | default(omit) }}" delay: "{{ lxc_container_wait_params.delay | default(omit) }}" sleep: "{{ lxc_container_wait_params.sleep | default(omit) }}" timeout: "{{ lxc_container_wait_params.timeout | default(omit) }}" tags: - lxc_container_create-networks - name: Add global_environment_variables to environment file blockinfile: dest: "/etc/environment" state: present marker: "# {mark} Managed by OpenStack-Ansible" insertbefore: EOF block: "{{ lookup('template', 'environment.j2') }}" remote_user: root tags: - lxc_container_create-proxy - name: Create localhost config lineinfile: dest: "/etc/hosts" regexp: "^127.0.0.1" line: "127.0.0.1 localhost" owner: "root" group: "root" mode: "0644" remote_user: root tags: - lxc_container_create-hostname - name: Create domain config lineinfile: dest: "/etc/hosts" regexp: "^127.0.1.1" line: "127.0.1.1 {{ inventory_hostname | replace('_', '-') }}.{{ lxc_container_domain }} {{ inventory_hostname | replace('_', '-') }}" owner: "root" group: "root" mode: "0644" remote_user: root tags: - lxc_container_create-hostname - name: Create hostname copy: dest: "/etc/hostname" content: "{{ inventory_hostname | replace('_', '-') }}" owner: "root" group: "root" mode: "0644" remote_user: root tags: - lxc_container_create-hostname - name: Setup hostname command: hostname -F /etc/hostname changed_when: false remote_user: root tags: - lxc_container_create-hostname - name: Allow the usage of local facts file: path: /etc/ansible/facts.d/ state: directory tags: - lxc_container_create-install - name: Record the container variant deployed ini_file: dest: "/etc/ansible/facts.d/openstack_ansible.fact" section: lxc option: variant value: "{{ properties['lxc_container_variant'] | default(lxc_container_variant) }}"