openstack-ansible-lxc_conta.../tasks/container_create.yml

534 lines
19 KiB
YAML

---
# 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) }}"