Jesse Pretorius 2958a629c7 MNAIO: Ensure that nested virt is enabled on host
Nested virtualization is important to improve VM performance
and enabling it is crucial to ensuring that VM images built
on one host work on boot on other hosts because the environment
is consistent.

In this patch add a task to enable it if it is available.

Change-Id: I812d8399cf45fab94f0f46976c9415591d45e463
2018-09-06 17:16:21 +01:00

495 lines
16 KiB
YAML

---
# Copyright 2018, 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 witing, 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 facts
hosts: mnaio_hosts
gather_facts: "{{ gather_facts | default(true) }}"
environment: "{{ deployment_environment_variables | default({}) }}"
tags:
- setup-host
tasks:
- name: Gather variables for each operating system
include_vars: "{{ item }}"
with_first_found:
- "{{ playbook_dir }}/vars/{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml"
- "{{ playbook_dir }}/vars/{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml"
- "{{ playbook_dir }}/vars/{{ ansible_os_family | lower }}-{{ ansible_distribution_major_version | lower }}.yml"
- "{{ playbook_dir }}/vars/{{ ansible_distribution | lower }}.yml"
- "{{ playbook_dir }}/vars/{{ ansible_os_family | lower }}.yml"
tags:
- always
- name: Install pre-requisite host distro packages
package:
name: "{{ mnaio_host_required_distro_packages }}"
state: "latest"
update_cache: yes
cache_valid_time: 600
register: _install_required_host_packages
until: _install_required_host_packages is success
retries: 3
delay: 15
when:
- "mnaio_host_required_distro_packages | length > 0"
- name: Add/Remove/Update apt repositories
apt_repository:
repo: "{{ repo.repo }}"
state: "{{ repo.state | default('present') }}"
filename: "{{ repo.filename | default(omit) }}"
update_cache: no
with_items: "{{ mnaio_host_package_repos }}"
loop_control:
loop_var: repo
register: _add_apt_repo
when:
- "ansible_os_family == 'Debian'"
- "mnaio_host_package_repos | length > 0"
- "(repo.condition | default(True)) | bool"
- name: Update apt cache
apt:
update_cache: yes
register: _update_apt_cache
until: _update_apt_cache is success
retries: 3
delay: 15
when:
- "ansible_os_family == 'Debian'"
- "_add_apt_repo is changed"
- name: Install host distro packages
package:
name: "{{ mnaio_host_distro_packages }}"
state: "latest"
update_cache: yes
cache_valid_time: 600
register: _install_host_packages
until: _install_host_packages is success
retries: 3
delay: 15
- name: Get version of libguestfs
shell: >-
guestfish --version | awk '{print $2}'
changed_when: false
register: _libguestfs_version
# See:
# https://bugzilla.redhat.com/show_bug.cgi?id=1591617
# https://bugs.launchpad.net/ubuntu/+source/libguestfs/+bug/1615337
- name: Apply workaround for older versions to make guestfish work
shell: |
echo dash > /usr/lib/x86_64-linux-gnu/guestfs/supermin.d/zz-dash-packages
rm -rf /var/tmp/.guestfs*
when:
- "{{ (_libguestfs_version.stdout is version('1.38.1', '<')) or
((_libguestfs_version.stdout is version('1.39.0', '>=')) and
(_libguestfs_version.stdout is version('1.39.1', '<'))) }}"
# If the host had already installed kvm_intel.ko without nested=1, then
# re-load it now, honoring whatever is in qemu-system-x86.modprobe
# Exit codes:
# 0 - Nested virt already enabled
# 1 - Error
# 2 - Nested virt enabled by task (should show task as changed)
# 3 - Nested virt not available
- name: Ensure that nested virtualization is enabled (if it is available)
shell: |
INTEL_NESTED=/sys/module/kvm_intel/parameters/nested
if grep -q kvm_intel /proc/modules; then
echo "Intel CPU found. Checking for nested virtualization capabilities."
if [ -f ${INTEL_NESTED} ]; then
echo "Nested virtualization capability found. Checking if it is enabled."
v=$(cat ${INTEL_NESTED})
if [ "x${v}" != "xY" ]; then
echo "Nested virtualization not enabled. Enabling it now."
rmmod kvm_intel && modprobe kvm_intel
exit 2
else
echo "Nested virtualization already enabled."
fi
else
echo "Nested virtualization capability not found."
exit 3
fi
else
echo "Intel CPU not found."
exit 3
fi
args:
executable: /bin/bash
register: _enable_nested_virt
changed_when: _enable_nested_virt.rc == 2
failed_when: _enable_nested_virt.rc not in [0, 2, 3]
- name: Ensure root has a .ssh directory
file:
path: /root/.ssh
state: directory
owner: root
group: root
mode: 0700
- name: Create ssh key pair for root
user:
name: root
generate_ssh_key: yes
ssh_key_bits: 2048
ssh_key_file: /root/.ssh/id_rsa
- name: Get root public key
command: cat /root/.ssh/id_rsa.pub
register: public_key_get
changed_when: false
- name: Set key facts
set_fact:
root_public_key: "{{ public_key_get.stdout }}"
- name: Ensure root can ssh to localhost
authorized_key:
user: "root"
key: "{{ root_public_key }}"
- name: Setup SSH client to disable strict host key checks
lineinfile:
path: /etc/ssh/ssh_config
regexp: "^.*StrictHostKeyChecking.*$"
line: " StrictHostKeyChecking no"
insertafter: "^Host \\*$"
state: present
- name: Setup SSH client to have a non-persistant known hosts file
lineinfile:
path: /etc/ssh/ssh_config
regexp: "^.*UserKnownHostsFile.*$"
line: " UserKnownHostsFile=/dev/null"
insertafter: "^Host \\*$"
state: present
- name: Setup SSH client to disable DNS host key checks
lineinfile:
path: /etc/ssh/ssh_config
regexp: "^.*VerifyHostKeyDNS.*$"
line: " VerifyHostKeyDNS no"
insertafter: "^Host \\*$"
state: present
- name: Add sysctl options
sysctl:
name: net.ipv4.ip_forward
value: 1
sysctl_set: yes
state: present
reload: yes
sysctl_file: /etc/sysctl.conf
- name: Get gateway interface
shell: "/sbin/ip r g 1 | awk '{print $5}'"
register: gw_iface
- set_fact:
masquerade_interface: "{{ gw_iface.stdout.strip() }}"
- name: Add IPtables rules
iptables:
table: "{{ item.table | default(omit) }}"
chain: "{{ item.chain | default(omit) }}"
in_interface: "{{ item.in_interface | default(omit) }}"
out_interface: "{{ item.out_interface | default(omit) }}"
source: "{{ item.source | default(omit) }}"
destination: "{{ item.destination | default(omit) }}"
protocol: "{{ item.protocol | default(omit) }}"
match: "{{ item.match | default(omit) }}"
destination_port: "{{ item.destination_port | default(omit) }}"
jump: "{{ item.jump | default(omit) }}"
to_ports: "{{ item.to_ports | default(omit) }}"
with_items: "{{ mnaio_host_iptables_rules }}"
# These rules are added manually due to bugs in the iptables module.
- name: Add IPtables rules
shell: |
if ! iptables -w -t {{ item.table }} -C {{ item.rule }};then
iptables -w -t {{ item.table }} -I {{ item.rule }}
fi
with_items:
- table: 'nat'
rule: 'POSTROUTING -s 10.0.2.0/22 ! -d 10.0.2.0/22 -j MASQUERADE'
- table: 'mangle'
rule: 'POSTROUTING -s 10.0.2.0/22 -o vm-br-dhcp -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill'
- table: 'mangle'
rule: 'POSTROUTING -s 10.0.2.0/22 -o vm-br-dhcp -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill'
- name: Add IPtables pre-routing rules to allow external access to VMs
shell: |
if ! iptables -w -t nat -C PREROUTING -p tcp -d {{ ansible_default_ipv4.address }} --dport {{ item.host_port }} -j DNAT --to {{ item.vm_ip }}:{{ item.vm_port }};then
iptables -w -t nat -I PREROUTING -p tcp -d {{ ansible_default_ipv4.address }} --dport {{ item.host_port }} -j DNAT --to {{ item.vm_ip }}:{{ item.vm_port }}
fi
with_items: "{{ mnaio_host_iptables_prerouting_ports }}"
when: config_prerouting | default(false) | bool
- name: Start netfilter persistent
service:
name: "{{ mnaio_host_iptables_service }}"
state: started
enabled: yes
when:
- ansible_distribution | lower == 'ubuntu'
- name: Drop host network interfaces
template:
src: "pxe/configs/{{ ansible_os_family | lower }}/mnaio-bridges.cfg.j2"
dest: /etc/network/interfaces.d/mnaio-bridges.cfg
mode: "0644"
owner: root
group: root
register: mnaio_bridges
- name: Ensure extra interfaces are sourced
lineinfile:
line: "source /etc/network/interfaces.d/*.cfg"
dest: "/etc/network/interfaces"
regexp: "^source /etc/network/interfaces.d/.*"
- name: Set the host interfaces up
command: "/sbin/ifup {{ item.value.iface }}"
with_dict: "{{ mnaio_host_networks }}"
when: mnaio_bridges is changed
- name: Disable default virt network
virt_net:
name: "default"
state: inactive
- name: Prevent default virt network autostart
virt_net:
name: "default"
autostart: no
- name: Define virt network(s)
virt_net:
name: "{{ item.value.iface }}"
state: present
xml: "{{ lookup('template', 'kvm/libvirt-network-template.xml.j2') }}"
with_dict: "{{ mnaio_host_networks }}"
- name: Set virt network(s) to active
virt_net:
name: "{{ item.value.iface }}"
state: active
with_dict: "{{ mnaio_host_networks }}"
- name: Set virt network(s) to autostart
virt_net:
name: "{{ item.value.iface }}"
autostart: yes
with_dict: "{{ mnaio_host_networks }}"
- name: Locate the largest writable data disk if mnaio_data_disk is not set
shell: >
lsblk -brndo NAME,TYPE,FSTYPE,RO,SIZE | awk '/d[b-z]+ disk +0/{ if ($4>m){m=$4; d=$1}}; END{print d}'
register: lsblk
changed_when: false
when:
- mnaio_data_disk is undefined
- name: Get info about existing virt storage pools
virt_pool:
command: info
register: _virt_pools
- name: If an existing virt pool does not match default_vm_disk_mode, remove it
when:
- _virt_pools.pools.default is defined
- (default_vm_disk_mode == "file" and _virt_pools.pools.default.format is defined) or
(default_vm_disk_mode == "lvm" and _virt_pools.pools.default.format is not defined)
block:
- name: Stop running VMs
virt:
name: "{{ item }}"
command: destroy
failed_when: false
with_items: "{{ _virt_pools.pools.default.volumes }}"
- name: Delete VM LVs
lvol:
vg: "{{ default_vm_disk_vg }}"
lv: "{{ item }}"
state: absent
force: yes
failed_when: false
with_items: "{{ _virt_pools.pools.default.volumes }}"
- name: Delete VM Disk Images
file:
path: "{{ _virt_pools.pools.default.path | default('/data/images') }}/{{ item }}.img"
state: absent
with_items: "{{ _virt_pools.pools.default.volumes }}"
- name: Undefine the VMs
virt:
name: "{{ item }}"
command: undefine
failed_when: false
with_items: "{{ _virt_pools.pools.default.volumes }}"
- name: Dismount the mount point if default_vm_disk_mode is 'lvm'
mount:
path: /data
state: unmounted
when:
- default_vm_disk_mode == "lvm"
- name: Stop the pool
virt_pool:
command: destroy
name: default
- name: Delete the pool, destroying its contents
virt_pool:
command: delete
name: default
- name: Undefine the pool
virt_pool:
command: undefine
name: default
- name: Remove the mount point if default_vm_disk_mode is 'lvm'
mount:
path: /data
state: absent
when:
- default_vm_disk_mode == "lvm"
- name: Reload systemd to remove generated unit files for mount
systemd:
daemon_reload: yes
when:
- default_vm_disk_mode == "lvm"
- name: Remove the volume group if default_vm_disk_mode is 'file'
lvg:
vg: vg01
state: absent
register: _remove_vg
when:
- default_vm_disk_mode == "file"
- name: Remove the existing disk partition
parted:
device: "/dev/{{ mnaio_data_disk | default(lsblk.stdout) }}"
number: 1
state: absent
- name: Setup the data disk partition
parted:
device: "/dev/{{ mnaio_data_disk | default(lsblk.stdout) }}"
label: gpt
number: 1
name: data1
state: present
register: _add_partition
- name: Prepare the data disk for 'lvm' default_vm_disk_mode
when:
- default_vm_disk_mode == "lvm"
block:
- name: Create the volume group
lvg:
vg: vg01
pvs: "/dev/{{ mnaio_data_disk | default(lsblk.stdout) }}1"
- name: Define the default virt storage pool
virt_pool:
name: default
state: present
xml: |
<pool type='logical'>
<name>default</name>
<source>
<name>vg01</name>
<format type='lvm2'/>
</source>
<target>
<path>/dev/vg01</path>
</target>
</pool>
- name: Prepare the data disk for 'file' default_vm_disk_mode
when:
- default_vm_disk_mode == "file"
block:
- name: Prepare the data disk file system
filesystem:
fstype: ext4
dev: "/dev/{{ mnaio_data_disk | default(lsblk.stdout) }}1"
force: yes
when:
- _add_partition is changed
- name: Mount the data disk
mount:
src: "/dev/{{ mnaio_data_disk | default(lsblk.stdout) }}1"
path: /data
state: mounted
fstype: ext4
- name: Create the images directory
file:
path: /data/images
owner: root
group: root
mode: "0755"
state: directory
- name: Define the default virt storage pool
virt_pool:
name: default
state: present
xml: |
<pool type='dir'>
<name>default</name>
<target>
<path>/data/images</path>
<permissions>
<mode>0755</mode>
<owner>0</owner>
<group>0</group>
</permissions>
</target>
</pool>
- name: Set default virt storage pool to active
virt_pool:
name: default
state: active
- name: Set default virt storage pool to autostart
virt_pool:
name: default
autostart: yes
- name: Load virtio kernel modules
shell: |
for mod in $(find /lib/modules/$(uname -r) -type f -name 'virtio*.ko'); do
module=$(echo $(basename $mod) | sed 's/\.ko//g')
modprobe ${module}
if ! grep ${module} /etc/modules; then
echo ${module} | tee -a /etc/modules
fi
done