Update role to gate using all supported distros

The changes here remove the boiler plate code we had in favor of using
all of our common roles. This also updates the nspawn role using some of
the learnings we've had from our recent LXC changes and ensures we're
not breaking any compatibility we had with our various distros.

Add option to run a full config update if required

> As a deployer I need the ability to make a sweeping change to container
  configs if required. At present the nspawn container create role will
  attempt to preserve the configs and update only what's required, which
  is desirable when maintaining uptime. This change provides the option
  `nspawn_container_preserve_config` which, if set to "false" will
  template the container configs instead of trying to preserve it.

Document everything in config

Change-Id: Ie969c10578e1102767ad8991c9d6171b547aef87
Signed-off-by: Kevin Carter <kevin.carter@rackspace.com>
This commit is contained in:
Kevin Carter 2018-02-22 16:43:19 -06:00
parent 4cd14f883d
commit 32f3ffdcbc
No known key found for this signature in database
GPG Key ID: 9443251A787B9FB3
25 changed files with 836 additions and 252 deletions

View File

@ -13,25 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
container_domain: "openstack.local"
container_default_bind_mounts: []
container_bind_mounts: []
container_config_overrides: {}
container_network_config_overrides: {}
container_image: "{{ nspawn_map['distro'] }}-{{ nspawn_map['release'] }}-{{ nspawn_map['arch'] }}"
# === systemd-nspawn defaults ==================================================
# Default networks that will be applied ONLY to nspwan deployments.
nspawn_networks:
nspawn_address:
# The name of the interface, by default this is a dummy device on a private
# network however it could be a bridge or any other interface.
bridge: "nspawn0"
# Optional | Set bool to enable a private device. This will create a bridge
# not connecting to the underlying L2.
private_device: true
interface: "mv-mv-nspawn0"
address: dhcp
# This is a list of items that will be passed into the container as READ-ONLY
# files or directories. If any of these items are passed into the container
@ -39,10 +27,86 @@ nspawn_networks:
# contianer prior to writting the config.
# nspawn_read_only_host_bindmount:
# - { source: /etc/resolv.conf, dest: /etc/resolv.conf }
nspawn_read_only_host_bindmount: []
nspawn_read_only_host_bindmount: "{{ _nspawn_read_only_host_bindmount | default([]) }}"
# This is a list of items that will be passed into the container as a shared
# bind mount. If any of these items are passed into the container.
# nspawn_shared_host_bindmount:
# - /etc/apt
nspawn_shared_host_bindmount: []
nspawn_shared_host_bindmount: "{{ _nspawn_shared_host_bindmount | default([]) }}"
# Defined CPU architecture map
nspawn_architecture_mapping:
x86_64: amd64
ppc64le: ppc64el
s390x: s390x
armv7l: armhf
# Set the cache map used when creating the container.
# nspawn_map:
# distro: "$DISTRO_NAME"
# arch: "{{ nspawn_architecture_mapping[container_architecture] }}"
# release: "{{ hostvars[physical_host]['ansible_distribution_version'] }}"
nspawn_map: "{{ _nspawn_map | default({}) }}"
# Enable or Disable the use of systemd-resolved. Option is Boolean.
nspawn_container_enable_resolved: "{{ _nspawn_container_enable_resolved | default(true) }}"
# Enable or Disable config preservation. If this is disabled a new configuration
# file for the systemd container will be created, even if the container already
# exists. When this is set to "false" the container will be restarted should the
# configuration task(s) result in change.
nspawn_container_preserve_config: true
# Enable or Disable the BTRFS quota system for the "/var/lib/machines" mount
# point. More information on the BTRFS quota system can be found here:
# * https://btrfs.wiki.kernel.org/index.php/Quota_support
nspawn_host_machine_quota_disabled: false
# Set the default qgroup limits used for file system quotas. The default is
# "none". See the following documentation for more information:
# * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs-qgroup
nspawn_host_qgroup_space_limit: none
nspawn_host_qgroup_compression_limit: none
# === General container defaults ===============================================
# NOTE(cloudnull): These are host specific variables we capture up-front. These
# psuedo variables are used to capture the container host
# information but could be overriden to deploy whatever.
container_host: "{{ hostvars[inventory_hostname]['physical_host'] | default('localhost') }}"
container_architecture: "{{ hostvars[container_host]['ansible_architecture'] | lower }}"
# Name of the on-disk image to use for deployment.
container_image: "{{ nspawn_map['distro'] }}-{{ nspawn_map['release'] }}-{{ nspawn_map['arch'] }}"
# The domain the containers will exist within.
container_domain: "openstack.local"
# Container Default bind mounts. This option will be merged with the container
# bind mounts. This option takes a list of files on a host that will be mounted
# at the exact same path within the container.
container_default_bind_mounts: []
# Container bind mounts. This option takes a list of files on a host that will
# be mounted at the exact same path within the container.
container_bind_mounts: []
# option used to specific specific container config values which will be
# directly injected into the container service or nspawn config file. This
# option take Key=Value pairs and can be used to set any desired config options
# in any section. Review the following link for more details:
# * https://www.freedesktop.org/software/systemd/man/systemd.nspawn.html
container_config_overrides: {}
# Default networks that will be applied ALL containerized deployments.
# container_networks:
# management_address:
# address: "{{ ansible_host | default('localhost') }}"
# netmask: "255.255.255.0"
# bridge: "br-mgmt"
# static_routes:
# - cidr: 172.29.100.0/24
# gateway: 172.29.100.100
container_networks: {}

View File

@ -13,39 +13,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Wait for container connectivity
wait_for_connection:
delay: 3
timeout: 60
listen: check connection
- name: Gather facts for new container(s)
setup:
gather_subset: "!all"
listen: check connection
- name: Ensure dbus is installed
package:
name: "{{ nspawn_dbus_package }}"
state: present
update_cache: "{{ ansible_pkg_mgr in ['apt', 'zypper'] }}"
register: dbus_install
retries: 5
delay: 3
until: dbus_install | success
listen: check connection
- name: Restart new container (first boot only)
systemd:
name: "systemd-nspawn@{{ systemd_escape.stdout }}"
state: "restarted"
enabled: true
register: machinectl_first_boot
retries: 5
delay: 2
until: machinectl_first_boot | success
delegate_to: "{{ physical_host }}"
when:
- machinectl_container_image_status.rc != 0
notify:
- check connection
listen: Container first boot

View File

@ -33,4 +33,5 @@ galaxy_info:
- python
- development
- openstack
dependencies: []
dependencies:
- plugins

View File

@ -77,17 +77,57 @@
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 }}"
when:
- nspawn_systemd_version | int > 219
- 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
- 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: 5
retries: 3
delay: 2
until: machinectl_container_clone | success
until: machinectl_container_clone is success
delegate_to: "{{ physical_host }}"
notify:
- Container first boot
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:
@ -99,6 +139,7 @@
- "/openstack/log/{{ inventory_hostname }}"
- "/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
@ -127,56 +168,41 @@
# 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: Locate nspawn config
stat:
path: "/etc/systemd/nspawn/{{ inventory_hostname }}.nspawn"
register: nspawn_config_exists
delegate_to: "{{ physical_host }}"
when:
- nspawn_systemd_version | int > 219
- name: modern systemd block
block:
- name: Copy container config (new)
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"
delegate_to: "{{ physical_host }}"
when:
- nspawn_container_preserve_config | bool
- nspawn_config_exists.stat.exists | bool
- name: slurp existing nspawn config
slurp:
src: "/etc/systemd/nspawn/{{ inventory_hostname }}.nspawn"
register: nspawn_config
changed_when: false
delegate_to: "{{ physical_host }}"
# 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"
delegate_to: "{{ physical_host }}"
when:
- not nspawn_container_preserve_config | bool or
not nspawn_config_exists.stat.exists | bool
notify:
- Container first boot
when:
- nspawn_systemd_version | int > 219
- nspawn_config_exists.stat is defined
- nspawn_config_exists.stat.exists | bool
- name: Copy container config (new)
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"
delegate_to: "{{ physical_host }}"
when:
- nspawn_systemd_version | int > 219
- nspawn_config_exists.stat is defined
- nspawn_config_exists.stat.exists | bool
# 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"
delegate_to: "{{ physical_host }}"
when:
- nspawn_systemd_version | int > 219
- nspawn_config_exists.stat is defined
- not nspawn_config_exists.stat.exists | bool
- name: Copy container config (old)
template:
@ -189,59 +215,20 @@
delegate_to: "{{ physical_host }}"
when:
- nspawn_systemd_version | int < 220
- name: Copy container interface config
config_template:
src: container_mv.network.j2
dest: "/var/lib/machines/{{ inventory_hostname }}/etc/systemd/network/mv-mv-{{ item.key }}.network"
owner: "root"
group: "root"
mode: "0644"
config_overrides: "{{ container_network_config_overrides | default({}) }}"
config_type: "ini"
with_dict: "{{ container_networks | combine(nspawn_networks) }}"
delegate_to: "{{ physical_host }}"
- name: Create localhost config
lineinfile:
dest: "/var/lib/machines/{{ inventory_hostname }}/etc/hosts"
regexp: "^127.0.0.1"
line: "127.0.0.1 localhost"
owner: "root"
group: "root"
mode: "0644"
remote_user: root
delegate_to: "{{ physical_host }}"
- name: Create domain config
lineinfile:
dest: "/var/lib/machines/{{ inventory_hostname }}/etc/hosts"
regexp: "^127.0.1.1"
line: "127.0.1.1 {{ inventory_hostname | replace('_', '-') }}.{{ container_domain }} {{ inventory_hostname | replace('_', '-') }}"
owner: "root"
group: "root"
mode: "0644"
remote_user: root
delegate_to: "{{ physical_host }}"
- name: Create hostname
copy:
dest: "/var/lib/machines/{{ inventory_hostname }}/etc/hostname"
content: |
{{ inventory_hostname | replace('_', '-') }}
owner: "root"
group: "root"
mode: "0644"
remote_user: root
delegate_to: "{{ physical_host }}"
notify:
- Container first boot
- 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 }}"
notify:
- Container first boot
tags:
- skip_ansible_lint
- name: Create dbus machine-id
copy:
@ -252,7 +239,7 @@
remote_user: root
delegate_to: "{{ physical_host }}"
- name: Start (enable) new container
- name: Start new container (enable)
systemd:
daemon_reload: yes
name: "systemd-nspawn@{{ systemd_escape.stdout }}"
@ -261,18 +248,108 @@
register: machinectl_start
retries: 5
delay: 2
until: machinectl_start | success
until: machinectl_start is success
delegate_to: "{{ physical_host }}"
notify:
- check connection
- name: Restart networkd
service:
name: systemd-networkd
state: restarted
enabled: true
- 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: Create journal directory
file:
path: "/var/log/journal"
state: "directory"
- name: Run the systemd-networkd role
include_role:
name: systemd_networkd
private: true
vars:
systemd_interface_cleanup: false
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 (container_networks | combine(nspawn_networks)).items() %}
{% set _network = {'interface': value.interface | default('mv-mv-' + value.bridge.split('br-')[-1])} %}
{% 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: Create resolved link
file:
src: "/var/run/systemd/resolve/resolv.conf"
dest: "/etc/resolv.conf"
force: true
state: link
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: "/etc/resolv.conf"
register: nspawn_resolv_conf
- name: Remove resolv.conf link
file:
path: "/etc/resolv.conf"
state: absent
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: "/etc/resolv.conf"
when:
- not nspawn_container_enable_resolved | bool
- name: Force all notified handlers now
meta: flush_handlers
- name: (RE)Gather facts post setup
setup: {}

View File

@ -27,11 +27,8 @@ Bind={{ bind }}:{{ bind }}
{% set _ = container_networks.update(nspawn_networks) %}
{% for key, value in container_networks.items() %}
{% if value.bridge is defined %}
{% set macvlan = 'mv-' + value.bridge.split('br-')[-1] %}
{% else %}
{% set macvlan = 'mv-' + key %}
{% set _ = macvlans.append('mv-' + value.bridge.split('br-')[-1]) %}
{% endif %}
{% set _ = macvlans.append(macvlan) %}
{% endfor %}
{% if macvlans | length > 0 %}
Private=yes

View File

@ -1,44 +0,0 @@
# {{ ansible_managed }}
[Match]
{% if item.value.bridge is defined %}
Name=mv-mv-{{ item.value.bridge.split('br-')[-1] }}
{% else %}
Name=mv-mv-{{ item.key }}
{% endif %}
{% if item.value.address is defined %}
[Address]
{% set addr_cidr = (item.value.address | string + '/' + item.value.netmask | string) | ipaddr('prefix') %}
Address={{ item.value.address }}/{{ addr_cidr }}
{% else %}
[DHCP]
UseDNS=yes
UseNTP=yes
RouteMetric=20
{% endif %}
{% if item.value.static_routes is defined %}
[Route]
{% for route in item.value.static_routes %}
Source={{ route['cidr'] }}
Gateway={{ route['gateway'] }}
{% endfor %}
{% endif %}
[Network]
{% if item.value.address is defined %}
{% set addr_cidr = (item.value.address | string + '/' + item.value.netmask | string) | ipaddr('prefix') %}
Address={{ item.value.address }}/{{ addr_cidr }}
{% else %}
DHCP=yes
{% endif %}
{% if item.value.gateway is defined %}
Gateway={{ item.value.gateway }}
{% endif %}
[Link]
{% if item.value.mtu is defined %}
MTUBytes={{ item.value.mtu }}
{% endif %}
ARP=yes

View File

@ -10,7 +10,11 @@
src: https://git.openstack.org/openstack/openstack-ansible-openstack_hosts
scm: git
version: master
- name: lxc_hosts
src: https://git.openstack.org/openstack/openstack-ansible-lxc_hosts
- name: nspawn_hosts
src: https://git.openstack.org/openstack/openstack-ansible-nspawn_hosts
scm: git
version: master
- name: plugins
src: https://git.openstack.org/openstack/openstack-ansible-plugins
scm: git
version: master

View File

@ -13,13 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Gather nspawn container host facts
hosts: "localhost"
gather_facts: true
container_networks:
management_address:
address: "{{ ansible_host | default('localhost') }}"
netmask: "255.255.255.0"
bridge: "br-mgmt"
static_routes:
- cidr: 172.29.100.0/24
gateway: 172.29.100.100
- name: Create container(s)
hosts: "{{ container_group|default('all_containers') }}"
gather_facts: false
user: root
roles:
- role: "nspawn_container_create"
bridges:
- name: "br-mgmt"
ip_addr: "172.29.100.100"
netmask: "255.255.255.0"

View File

@ -15,19 +15,8 @@
container_name: "{{ inventory_hostname }}"
container_networks:
management_address:
address: "{{ ansible_host }}"
bridge: "br-mgmt"
interface: "eth1"
netmask: "255.255.252.0"
type: "veth"
static_routes:
- cidr: 10.100.100.0/24
gateway: 10.100.100.1
properties:
service_name: "{{ inventory_hostname }}"
global_environment_variables:
foo: "bar"
deployment_environment_variables:
foo: bar

View File

@ -1,6 +1,8 @@
---
ansible_host: 10.100.100.2
ansible_host: 172.29.100.101
ansible_become: True
ansible_user: root
physical_host: localhost
container_tech: nspawn

View File

@ -1,6 +1,8 @@
---
ansible_host: 10.100.100.3
ansible_host: 172.29.100.102
ansible_become: True
ansible_user: root
physical_host: localhost
container_tech: nspawn

View File

@ -1,6 +1,8 @@
---
ansible_host: 10.100.100.4
ansible_host: 172.29.100.103
ansible_become: True
ansible_user: root
physical_host: localhost
container_tech: nspawn

View File

@ -17,3 +17,5 @@ bridges:
- "br-mgmt"
ansible_python_interpreter: "/usr/bin/python2"
physical_host: localhost

View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
# {{ ansible_managed }}
ip route list > /var/log/container-{{ inventory_hostname }}-routes.log
ip a list > /var/log/container-{{ inventory_hostname }}-links.log
ps auxfww > /var/log/container-{{ inventory_hostname }}-process.log
cat /etc/resolv.conf > /var/log/container-{{ inventory_hostname }}-resolvers.log
systemd-resolve --statistics > /var/log/container-{{ inventory_hostname }}-resolver-statistics.log
hostnamectl status > /var/log/container-{{ inventory_hostname }}-hostname-status.log
systemctl status nspawn* > /var/log/container-{{ inventory_hostname }}-nspawn-general-status.log
systemctl status systemd-nspawn* > /var/log/container-{{ inventory_hostname }}-nspawn-container-status.log
journalctl -u dnsmasq-mv-nspawn0 -n 1024 > /var/log/container-{{ inventory_hostname }}-nspawn-dnsmasq-mv-nspawn0.log
# This script collects details and exits. It will always return 0
true

View File

@ -0,0 +1,41 @@
# {{ ansible_managed }}
[Unit]
Description=test networks service
After=syslog.target
After=network.target
[Service]
Type=oneshot
User=root
RemainAfterExit=yes
{% set seen_start_interfaces = [] %}
{% for item in bridges %}
{% if item is not mapping %}
{% set item = {'name': item} %}
{% endif %}
{% if item.name not in seen_start_interfaces %}
{% set _ = seen_start_interfaces.append(item.name) %}
# Interface [{{ item.name }}]
ExecStart=-/sbin/ip link add dev "{{ item.name }}" type bridge
ExecStart=-/sbin/ip link set dev "{{ item.name }}" up
{% if item.address is defined and item.netmask is defined %}
ExecStart=-/sbin/ip address add "{{ item.address }}/{{ item.netmask }}" dev "{{ item.bridge }}"
{% endif %}
{% if item.veth_peer is defined %}
ExecStart=-/sbin/ip link add "{{ item.name }}-veth" type veth peer name "{{ item.veth_peer }}"
ExecStart=-/sbin/ip link set "{{ item.name }}-veth" up
ExecStart=-/sbin/ip link set "{{ item.veth_peer }}-veth" up
ExecStart=-/sbin/ip link set dev "{{ item.name }}-veth" master "{{ item.name }}"
ExecStop=-/sbin/ip link delete dev "{{ item.veth_peer }}-veth"
ExecStop=-/sbin/ip link delete dev "{{ item.name }}-veth"
{% endif %}
{% endif %}
ExecStop=-/sbin/ip link delete dev "{{ item.name }}"
{% endfor %}
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,87 @@
---
# 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 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 host facts
hosts: localhost
connection: local
- name: Create container(s)
hosts: all_containers
gather_facts: false
become: true
pre_tasks:
- 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: Poweroff container
command: "machinectl poweroff {{ inventory_hostname }}"
register: machinectl_poweroff
retries: 3
delay: 2
until: machinectl_remove is success
delegate_to: "{{ physical_host }}"
failed_when: false
when:
- machinectl_container_status.rc == 0
tags:
- skip_ansible_lint
- name: Remove container
command: "machinectl remove {{ inventory_hostname }}"
register: machinectl_remove
retries: 3
delay: 2
until: machinectl_remove is success
delegate_to: "{{ physical_host }}"
failed_when: false
when:
- machinectl_container_image_status.rc == 0
tags:
- skip_ansible_lint
- name: Container cleanup
file:
path: "{{ item }}"
state: "absent"
with_items:
- "/openstack/log/{{ inventory_hostname }}"
- "/openstack/{{ inventory_hostname }}"
delegate_to: "{{ physical_host }}"
roles:
- role: "nspawn_container_create"
post_tasks:
- name: Create detail gathering script
template:
src: "dump-container-details.sh.j2"
dest: "/opt/dump-{{ inventory_hostname }}-details.sh"
mode: "0755"
- name: Collect details
command: "/opt/dump-{{ inventory_hostname }}-details.sh"
tags:
- skip_ansible_lint

View File

@ -0,0 +1,143 @@
---
# 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 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: Test containers
hosts: localhost
connection: local
become: true
tasks:
# NOTE(cloudnull): This should be revised at a future date.
- name: Test intra cluster connectivity
command: ping -c 1 8.8.8.8
register: machinectl_ping
retries: 3
delay: 2
until: machinectl_ping is success
failed_when: false
tags:
- skip_ansible_lint
- name: Stop container3
command: "machinectl poweroff container3"
register: container_stop
changed_when: container_stop.rc == 0
failed_when: not container_stop.rc in [0, 2]
until: container_stop.rc in [0, 2]
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Start container3
command: "machinectl start container3"
register: container_start
changed_when: container_start.rc == 0
until: container_start is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Status check container1
command: "machinectl status container1"
register: container_status
changed_when: container_status.rc == 0
until: container_status is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Clone container2
command: "machinectl clone container2 test1-container2"
register: container_clone
changed_when: container_clone.rc == 0
until: container_clone is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Show image test1-container2
command: "machinectl show-image test1-container2"
register: container_show
changed_when: container_show.rc == 0
until: container_show is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Rename image test1-container2
command: "machinectl rename test1-container2 test1"
register: container_rename
changed_when: container_rename.rc == 0
until: container_rename is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Check Status of test1 image
command: "machinectl image-status test1"
register: container_status
changed_when: container_status.rc == 0
until: container_status is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Start test1 container
command: "machinectl start test1"
register: container_start
changed_when: container_start.rc == 0
until: container_start is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Status test1 container
command: "machinectl status test1"
register: container_status
changed_when: container_status.rc == 0
until: container_status is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Poweroff test1 container
command: "machinectl poweroff test1"
register: container_poweroff
changed_when: container_poweroff.rc == 0
until: container_poweroff is success
retries: 3
delay: 2
tags:
- skip_ansible_lint
- name: Remove test container images
command: "machinectl remove {{ item }}"
with_items:
- test1
register: container_poweroff
changed_when: container_poweroff.rc == 0
until: container_poweroff is success
retries: 3
delay: 2
tags:
- skip_ansible_lint

View File

@ -0,0 +1,104 @@
---
# 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 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: Setup nspawn host
hosts: localhost
connection: local
become: true
pre_tasks:
- name: Gather facts
setup:
gather_subset: "!all"
- name: Show host facts
debug:
var: hostvars
- name: Create test network service
template:
src: "templates/test-networks.service.j2"
dest: "/etc/systemd/system/00-test-networks.service"
- name: Enable test network service
systemd:
name: "00-test-networks.service"
state: "started"
enabled: true
daemon_reload: true
- name: Ensure root ssh key
user:
name: "{{ ansible_user_id | default('root') }}"
generate_ssh_key: "yes"
ssh_key_bits: 2048
ssh_key_file: ".ssh/id_rsa"
- name: Get root ssh key
slurp:
src: '~/.ssh/id_rsa.pub'
register: _root_ssh_key
- name: Prepare container ssh key fact
set_fact:
nspawn_container_ssh_key: "{{ _root_ssh_key['content'] | b64decode }}"
- name: Ensure public ssh key is in authorized_keys
authorized_key:
user: "{{ ansible_env.USER | default('root') }}"
key: "{{ nspawn_container_ssh_key }}"
manage_dir: no
- name: Clear iptables rules
command: "{{ item }}"
with_items:
- "iptables -F"
- "iptables -X"
- "iptables -t nat -F"
- "iptables -t nat -X"
- "iptables -t mangle -F"
- "iptables -t mangle -X"
- "iptables -P INPUT ACCEPT"
- "iptables -P FORWARD ACCEPT"
- "iptables -P OUTPUT ACCEPT"
- "iptables -A POSTROUTING -t mangle -p tcp -j CHECKSUM --checksum-fill"
- "iptables -t nat -A POSTROUTING -o {{ ansible_default_ipv4.interface }} -j MASQUERADE"
tags:
- skip_ansible_lint
# This is a very dirty hack due to images.linuxcontainers.org
# constantly failing to resolve in openstack-infra.
- name: Implement hard-coded hosts entries for consistently failing name
lineinfile:
path: "/etc/hosts"
line: "{{ item }}"
state: present
with_items:
- "91.189.91.21 images.linuxcontainers.org us.images.linuxcontainers.org"
- "91.189.88.37 images.linuxcontainers.org uk.images.linuxcontainers.org"
roles:
- role: "nspawn_hosts"
post_tasks:
- name: Create detail gathering script
template:
src: "dump-container-details.sh.j2"
dest: "/opt/dump-{{ inventory_hostname }}-details.sh"
mode: "0755"
- name: Collect details
command: "/opt/dump-{{ inventory_hostname }}-details.sh"
tags:
- skip_ansible_lint

50
tests/test-user-setup.yml Normal file
View File

@ -0,0 +1,50 @@
---
# 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 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: Setup user environment
hosts: localhost
connection: local
become: false
tasks:
- name: Gather facts
setup:
gather_subset: "!all"
- name: Show host facts
debug:
var: hostvars
- name: Ensure user ssh key
user:
name: "{{ ansible_user_id | default('root') }}"
generate_ssh_key: "yes"
ssh_key_bits: 2048
ssh_key_file: ".ssh/id_rsa"
- name: Get root ssh key
slurp:
src: '~/.ssh/id_rsa.pub'
register: _root_ssh_key
- name: Prepare container ssh key fact
set_fact:
user_ssh_key: "{{ _root_ssh_key['content'] | b64decode }}"
- name: Ensure public ssh key is in authorized_keys
authorized_key:
user: "root"
key: "{{ user_ssh_key }}"
manage_dir: no
become: true

View File

@ -1,5 +1,5 @@
---
# Copyright 2015, Rackspace US, Inc.
# 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.
@ -13,11 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# Prepare the user ssh keys
- include: common/test-prepare-keys.yml
- import_playbook: test-user-setup.yml
# Prepare the host
- include: common/test-prepare-host.yml
- import_playbook: test-nspawn-host-setup.yml
# Test container creation
- include: test-containers-create.yml
- import_playbook: test-nspawn-containers-create.yml
- import_playbook: test-nspawn-containers.yml

View File

@ -13,25 +13,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
nspawn_map:
_nspawn_map:
distro: centos
arch: "{{ nspawn_architecture_mapping[ansible_container_architecture] }}"
arch: "{{ nspawn_architecture_mapping[container_architecture] }}"
release: 7
# This is a list of items that will be passed into the container as READ-ONLY
# files or directories. If any of these items are passed into the container
# the container create process will ensure the file is not present within the
# contianer prior to writting the config.
nspawn_read_only_host_bindmount:
_nspawn_read_only_host_bindmount:
- { source: /etc/environment, dest: /etc/environment }
- { source: /etc/localtime, dest: /etc/localtime }
# This is a list of items that will be passed into the container as a shared
# bind mount. If any of these items are passed into the container.
nspawn_shared_host_bindmount:
- /etc/yum
_nspawn_shared_host_bindmount:
- /etc/localtime
- /root
- /opt
nspawn_dbus_package:
- dbus
_nspawn_container_enable_resolved: true

37
vars/suse-42.yml Normal file
View File

@ -0,0 +1,37 @@
---
# 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 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.
_nspawn_map:
distro: opensuse
arch: "{{ nspawn_architecture_mapping[container_architecture] }}"
release: "{{ hostvars[physical_host]['ansible_distribution_version'] }}"
# This is a list of items that will be passed into the container as READ-ONLY
# files or directories. If any of these items are passed into the container
# the container create process will ensure the file is not present within the
# contianer prior to writting the config.
_nspawn_read_only_host_bindmount:
- { source: /etc/environment, dest: /etc/environment }
# This is a list of items that will be passed into the container as a shared
# bind mount. If any of these items are passed into the container.
_nspawn_shared_host_bindmount:
- /etc/localtime
- /root
- /opt
# This version of SUSE does not have access to systemd-resolved. This will need
# to remained disabled until it does.
_nspawn_container_enable_resolved: false

View File

@ -13,25 +13,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
nspawn_map:
_nspawn_map:
distro: ubuntu
arch: "{{ nspawn_architecture_mapping[ansible_container_architecture] }}"
arch: "{{ nspawn_architecture_mapping[container_architecture] }}"
release: xenial
# This is a list of items that will be passed into the container as READ-ONLY
# files or directories. If any of these items are passed into the container
# the container create process will ensure the file is not present within the
# contianer prior to writting the config.
nspawn_read_only_host_bindmount:
_nspawn_read_only_host_bindmount:
- { source: /etc/environment, dest: /etc/environment }
- { source: /etc/localtime, dest: /etc/localtime }
# This is a list of items that will be passed into the container as a shared
# bind mount. If any of these items are passed into the container.
nspawn_shared_host_bindmount:
- /etc/apt
_nspawn_shared_host_bindmount:
- /etc/localtime
- /root
- /opt
nspawn_dbus_package:
- dbus
_nspawn_container_enable_resolved: true

View File

@ -1,5 +1,5 @@
---
# Copyright 2018, Rackspace US, Inc.
# 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.
@ -13,14 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# NOTE(cloudnull): These are host specific variables we capture up-front. This
# These psuedo variables are used to capture the container host
# information.
ansible_container_host: "{{ hostvars[inventory_hostname]['physical_host'] }}"
ansible_container_architecture: "{{ hostvars[ansible_container_host]['ansible_architecture'] | lower }}"
- job:
name: openstack-ansible-nspawn-ubuntu-xenial
parent: openstack-ansible-functional
nodeset: ubuntu-xenial
nspawn_architecture_mapping:
x86_64: amd64
ppc64le: ppc64el
s390x: s390x
armv7l: armhf
- job:
name: openstack-ansible-nspawn-centos-7
parent: openstack-ansible-functional
nodeset: centos-7
- job:
name: openstack-ansible-nspawn-opensuse-423
parent: openstack-ansible-functional
nodeset: opensuse-423

30
zuul.d/project.yaml Normal file
View File

@ -0,0 +1,30 @@
# 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.
- project:
check:
jobs:
- openstack-ansible-linters
- openstack-ansible-nspawn-ubuntu-xenial
- openstack-ansible-nspawn-centos-7
- openstack-ansible-nspawn-opensuse-423
experimental:
jobs:
- openstack-ansible-integrated-deploy-aio
gate:
jobs:
- openstack-ansible-linters
- openstack-ansible-nspawn-ubuntu-xenial
- openstack-ansible-nspawn-centos-7
- openstack-ansible-nspawn-opensuse-423