From 36ddea31a257b38aa22d3232f4c4389c851f5456 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Mon, 2 Oct 2017 10:05:17 -0500 Subject: [PATCH] Add devstack base job for zuul v3 This should be managed in the devstack repo, since it's a base job to run devstack. Change-Id: Iffe54fbccbccd68db08f79a1b51dd7f76dbff408 Depends-On: Ie2119f24360d56690ffd772b95a9ea6b98dd4a39 --- .zuul.yaml | 85 ++++++++ playbooks/devstack.yaml | 3 + playbooks/post.yaml | 4 + playbooks/pre.yaml | 22 +++ roles/configure-swap/README.rst | 11 ++ roles/configure-swap/defaults/main.yaml | 1 + roles/configure-swap/tasks/ephemeral.yaml | 110 +++++++++++ roles/configure-swap/tasks/main.yaml | 63 ++++++ roles/configure-swap/tasks/root.yaml | 63 ++++++ roles/export-devstack-journal/README.rst | 15 ++ .../defaults/main.yaml | 1 + roles/export-devstack-journal/tasks/main.yaml | 29 +++ roles/fetch-devstack-log-dir/README.rst | 10 + .../fetch-devstack-log-dir/defaults/main.yaml | 1 + roles/fetch-devstack-log-dir/tasks/main.yaml | 5 + roles/run-devstack/README.rst | 8 + roles/run-devstack/defaults/main.yaml | 1 + roles/run-devstack/tasks/main.yaml | 6 + roles/setup-devstack-cache/README.rst | 15 ++ roles/setup-devstack-cache/defaults/main.yaml | 2 + roles/setup-devstack-cache/tasks/main.yaml | 14 ++ roles/setup-devstack-log-dir/README.rst | 11 ++ .../setup-devstack-log-dir/defaults/main.yaml | 1 + roles/setup-devstack-log-dir/tasks/main.yaml | 5 + roles/setup-devstack-source-dirs/README.rst | 11 ++ .../defaults/main.yaml | 1 + .../tasks/main.yaml | 22 +++ roles/setup-stack-user/README.rst | 16 ++ roles/setup-stack-user/defaults/main.yaml | 2 + roles/setup-stack-user/files/50_stack_sh | 1 + roles/setup-stack-user/tasks/main.yaml | 45 +++++ roles/setup-tempest-user/README.rst | 10 + roles/setup-tempest-user/files/51_tempest_sh | 3 + roles/setup-tempest-user/tasks/main.yaml | 20 ++ roles/start-fresh-logging/README.rst | 11 ++ roles/start-fresh-logging/defaults/main.yaml | 1 + roles/start-fresh-logging/tasks/main.yaml | 56 ++++++ roles/write-devstack-local-conf/README.rst | 63 ++++++ .../defaults/main.yaml | 2 + .../library/devstack_local_conf.py | 185 ++++++++++++++++++ .../write-devstack-local-conf/tasks/main.yaml | 9 + 41 files changed, 944 insertions(+) create mode 100644 .zuul.yaml create mode 100644 playbooks/devstack.yaml create mode 100644 playbooks/post.yaml create mode 100644 playbooks/pre.yaml create mode 100644 roles/configure-swap/README.rst create mode 100644 roles/configure-swap/defaults/main.yaml create mode 100644 roles/configure-swap/tasks/ephemeral.yaml create mode 100644 roles/configure-swap/tasks/main.yaml create mode 100644 roles/configure-swap/tasks/root.yaml create mode 100644 roles/export-devstack-journal/README.rst create mode 100644 roles/export-devstack-journal/defaults/main.yaml create mode 100644 roles/export-devstack-journal/tasks/main.yaml create mode 100644 roles/fetch-devstack-log-dir/README.rst create mode 100644 roles/fetch-devstack-log-dir/defaults/main.yaml create mode 100644 roles/fetch-devstack-log-dir/tasks/main.yaml create mode 100644 roles/run-devstack/README.rst create mode 100644 roles/run-devstack/defaults/main.yaml create mode 100644 roles/run-devstack/tasks/main.yaml create mode 100644 roles/setup-devstack-cache/README.rst create mode 100644 roles/setup-devstack-cache/defaults/main.yaml create mode 100644 roles/setup-devstack-cache/tasks/main.yaml create mode 100644 roles/setup-devstack-log-dir/README.rst create mode 100644 roles/setup-devstack-log-dir/defaults/main.yaml create mode 100644 roles/setup-devstack-log-dir/tasks/main.yaml create mode 100644 roles/setup-devstack-source-dirs/README.rst create mode 100644 roles/setup-devstack-source-dirs/defaults/main.yaml create mode 100644 roles/setup-devstack-source-dirs/tasks/main.yaml create mode 100644 roles/setup-stack-user/README.rst create mode 100644 roles/setup-stack-user/defaults/main.yaml create mode 100644 roles/setup-stack-user/files/50_stack_sh create mode 100644 roles/setup-stack-user/tasks/main.yaml create mode 100644 roles/setup-tempest-user/README.rst create mode 100644 roles/setup-tempest-user/files/51_tempest_sh create mode 100644 roles/setup-tempest-user/tasks/main.yaml create mode 100644 roles/start-fresh-logging/README.rst create mode 100644 roles/start-fresh-logging/defaults/main.yaml create mode 100644 roles/start-fresh-logging/tasks/main.yaml create mode 100644 roles/write-devstack-local-conf/README.rst create mode 100644 roles/write-devstack-local-conf/defaults/main.yaml create mode 100644 roles/write-devstack-local-conf/library/devstack_local_conf.py create mode 100644 roles/write-devstack-local-conf/tasks/main.yaml diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000000..cee195cf39 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,85 @@ +- nodeset: + name: openstack-single-node + nodes: + - name: controller + label: ubuntu-xenial + groups: + - name: tempest + nodes: + - controller + +- nodeset: + name: openstack-two-node + nodes: + - name: controller + label: ubuntu-xenial + - name: compute1 + label: ubuntu-xenial + groups: + - name: tempest + nodes: + - controller + - name: compute + nodes: + - controller + - compute1 + +- job: + name: devstack + parent: multinode + description: Base devstack job + nodeset: openstack-single-node + required-projects: + - openstack-dev/devstack + - openstack/cinder + - openstack/glance + - openstack/keystone + - openstack/neutron + - openstack/nova + - openstack/requirements + - openstack/swift + timeout: 7200 + vars: + devstack_localrc: + DATABASE_PASSWORD: secretdatabase + RABBIT_PASSWORD: secretrabbit + ADMIN_PASSWORD: secretadmin + SERVICE_PASSWORD: secretservice + NETWORK_GATEWAY: 10.1.0.1 + Q_USE_DEBUG_COMMAND: True + FIXED_RANGE: 10.1.0.0/20 + IPV4_ADDRS_SAFE_TO_USE: 10.1.0.0/20 + FLOATING_RANGE: 172.24.5.0/24 + PUBLIC_NETWORK_GATEWAY: 172.24.5.1 + FLOATING_HOST_PREFIX: 172.24.4 + FLOATING_HOST_MASK: 23 + SWIFT_REPLICAS: 1 + SWIFT_START_ALL_SERVICES: False + LOGFILE: /opt/stack/logs/devstacklog.txt + LOG_COLOR: False + VERBOSE: True + NETWORK_GATEWAY: 10.1.0.1 + NOVNC_FROM_PACKAGE: True + ERROR_ON_CLONE: True + # NOTE(dims): etcd 3.x is not available in debian/ubuntu + # etc. As a stop gap measure, devstack uses wget to download + # from the location below for all the CI jobs. + ETCD_DOWNLOAD_URL: "http://tarballs.openstack.org/etcd/" + devstack_services: + horizon: False + tempest: False + pre-run: playbooks/pre + post-run: playbooks/post + + +- project: + name: openstack-dev/devstack + check: + jobs: + - devstack: + files: + - ^playbooks/pre + - ^playbooks/post + - ^playbooks/devstack + - ^roles/ + - .zuul.yaml diff --git a/playbooks/devstack.yaml b/playbooks/devstack.yaml new file mode 100644 index 0000000000..ede8382632 --- /dev/null +++ b/playbooks/devstack.yaml @@ -0,0 +1,3 @@ +- hosts: all + roles: + - run-devstack diff --git a/playbooks/post.yaml b/playbooks/post.yaml new file mode 100644 index 0000000000..6f5126ff16 --- /dev/null +++ b/playbooks/post.yaml @@ -0,0 +1,4 @@ +- hosts: all + roles: + - export-devstack-journal + - fetch-devstack-log-dir diff --git a/playbooks/pre.yaml b/playbooks/pre.yaml new file mode 100644 index 0000000000..4d07960fe8 --- /dev/null +++ b/playbooks/pre.yaml @@ -0,0 +1,22 @@ +- hosts: all + roles: + - configure-swap + - setup-stack-user + - setup-tempest-user + - setup-devstack-source-dirs + - setup-devstack-log-dir + - setup-devstack-cache + - start-fresh-logging + - write-devstack-local-conf + # TODO(jeblair): remove when configure-mirrors is fixed + tasks: + - name: Hack mirror_info + shell: + _raw_params: | + mkdir /etc/ci + cat << "EOF" > /etc/ci/mirror_info.sh + export NODEPOOL_UCA_MIRROR=http://mirror.dfw.rax.openstack.org/ubuntu-cloud-archive + EOF + args: + executable: /bin/bash + become: true diff --git a/roles/configure-swap/README.rst b/roles/configure-swap/README.rst new file mode 100644 index 0000000000..eaba5cf595 --- /dev/null +++ b/roles/configure-swap/README.rst @@ -0,0 +1,11 @@ +Configure a swap partition + +Creates a swap partition on the ephemeral block device (the rest of which +will be mounted on /opt). + +**Role Variables** + +.. zuul:rolevar:: configure_swap_size + :default: 8192 + + The size of the swap partition, in MiB. diff --git a/roles/configure-swap/defaults/main.yaml b/roles/configure-swap/defaults/main.yaml new file mode 100644 index 0000000000..4d622321c7 --- /dev/null +++ b/roles/configure-swap/defaults/main.yaml @@ -0,0 +1 @@ +configure_swap_size: 8192 diff --git a/roles/configure-swap/tasks/ephemeral.yaml b/roles/configure-swap/tasks/ephemeral.yaml new file mode 100644 index 0000000000..c2316eac2b --- /dev/null +++ b/roles/configure-swap/tasks/ephemeral.yaml @@ -0,0 +1,110 @@ +# Configure attached ephemeral devices for storage and swap + +- assert: + that: + - "ephemeral_device is defined" + +- name: Set partition names + set_fact: + swap_partition: "{{ ephemeral_device}}1" + opt_partition: "{{ ephemeral_device}}2" + +- name: Ensure ephemeral device is unmounted + become: yes + mount: + name: "{{ ephemeral_device }}" + state: unmounted + +- name: Get existing partitions + become: yes + parted: + device: "{{ ephemeral_device }}" + unit: MiB + register: ephemeral_partitions + +- name: Remove any existing partitions + become: yes + parted: + device: "{{ ephemeral_device }}" + number: "{{ item.num }}" + state: absent + with_items: + - "{{ ephemeral_partitions.partitions }}" + +- name: Create new disk label + become: yes + parted: + label: msdos + device: "{{ ephemeral_device }}" + +- name: Create swap partition + become: yes + parted: + device: "{{ ephemeral_device }}" + number: 1 + state: present + part_start: '0%' + part_end: "{{ configure_swap_size }}MiB" + +- name: Create opt partition + become: yes + parted: + device: "{{ ephemeral_device }}" + number: 2 + state: present + part_start: "{{ configure_swap_size }}MiB" + part_end: "100%" + +- name: Make swap on partition + become: yes + command: "mkswap {{ swap_partition }}" + +- name: Write swap to fstab + become: yes + mount: + path: none + src: "{{ swap_partition }}" + fstype: swap + opts: sw + passno: 0 + dump: 0 + state: present + +# XXX: does "parted" plugin ensure the partition is available +# before moving on? No udev settles here ... + +- name: Add all swap + become: yes + command: swapon -a + +- name: Create /opt filesystem + become: yes + filesystem: + fstype: ext4 + dev: "{{ opt_partition }}" + +# Rackspace at least does not have enough room for two devstack +# installs on the primary partition. We copy in the existing /opt to +# the new partition on the ephemeral device, and then overmount /opt +# to there for the test runs. +# +# NOTE(ianw): the existing "mount" touches fstab. There is currently (Sep2017) +# work in [1] to split mount & fstab into separate parts, but for now we bundle +# it into an atomic shell command +# [1] https://github.com/ansible/ansible/pull/27174 +- name: Copy old /opt + become: yes + shell: | + mount {{ opt_partition }} /mnt + find /opt/ -mindepth 1 -maxdepth 1 -exec mv {} /mnt/ \; + umount /mnt + +# This overmounts any existing /opt +- name: Add opt to fstab and mount + become: yes + mount: + path: /opt + src: "{{ opt_partition }}" + fstype: ext4 + opts: noatime + state: mounted diff --git a/roles/configure-swap/tasks/main.yaml b/roles/configure-swap/tasks/main.yaml new file mode 100644 index 0000000000..8960c726c8 --- /dev/null +++ b/roles/configure-swap/tasks/main.yaml @@ -0,0 +1,63 @@ +# On RAX hosts, we have a small root partition and a large, +# unallocated ephemeral device attached at /dev/xvde +- name: Set ephemeral device if /dev/xvde exists + when: ansible_devices["xvde"] is defined + set_fact: + ephemeral_device: "/dev/xvde" + +# On other providers, we have a device called "ephemeral0". +# +# NOTE(ianw): Once [1] is in our ansible (2.4 era?), we can figure +# this out more directly by walking the device labels in the facts +# +# [1] https://github.com/ansible/ansible/commit/d46dd99f47c0ee5081d15bc5b741e9096d8bfd3e +- name: Set ephemeral device by label + when: ephemeral_device is undefined + block: + - name: Get ephemeral0 device node + command: /sbin/blkid -L ephemeral0 + register: ephemeral0 + # If this doesn't exist, returns !0 + ignore_errors: yes + changed_when: False + + - name: Set ephemeral device if LABEL exists + when: "ephemeral0.rc == 0" + set_fact: + ephemeral_device: "{{ ephemeral0.stdout }}" + +# If we have ephemeral storage and we don't appear to have setup swap, +# we will create a swap and move /opt to a large data partition there. +- include: ephemeral.yaml + static: no + when: + - ephemeral_device is defined + - ansible_memory_mb['swap']['total'] | int + 10 <= configure_swap_size + +# If no ephemeral device and no swap, then we will setup some swap +# space on the root device to ensure all hosts a consistent memory +# environment. +- include: root.yaml + static: no + when: + - ephemeral_device is undefined + - ansible_memory_mb['swap']['total'] | int + 10 <= configure_swap_size + +# ensure a standard level of swappiness. Some platforms +# (rax+centos7) come with swappiness of 0 (presumably because the +# vm doesn't come with swap setup ... but we just did that above), +# which depending on the kernel version can lead to the OOM killer +# kicking in on some processes despite swap being available; +# particularly things like mysql which have very high ratio of +# anonymous-memory to file-backed mappings. +# +# This sets swappiness low; we really don't want to be relying on +# cloud I/O based swap during our runs if we can help it +- name: Set swappiness + become: yes + sysctl: + name: vm.swappiness + value: 30 + state: present + +- debug: var=ephemeral_device diff --git a/roles/configure-swap/tasks/root.yaml b/roles/configure-swap/tasks/root.yaml new file mode 100644 index 0000000000..f22b53700f --- /dev/null +++ b/roles/configure-swap/tasks/root.yaml @@ -0,0 +1,63 @@ +# If no ephemeral devices are available, use root filesystem + +- name: Calculate required swap + set_fact: + swap_required: "{{ configure_swap_size - ansible_memory_mb['swap']['total'] | int }}" + +- block: + - name: Get root filesystem + shell: df --output='fstype' /root | tail -1 + register: root_fs + + - name: Save root filesystem + set_fact: + root_filesystem: "{{ root_fs.stdout }}" + + - debug: var=root_filesystem + +# Note, we don't use a sparse device to avoid wedging when disk space +# and memory are both unavailable. + +# Cannot fallocate on filesystems like XFS, so use slower dd +- name: Create swap backing file for non-EXT fs + when: '"ext" not in root_filesystem' + become: yes + command: dd if=/dev/zero of=/root/swapfile bs=1M count={{ swap_required }} + args: + creates: /root/swapfile + +- name: Create sparse swap backing file for EXT fs + when: '"ext" in root_filesystem' + become: yes + command: fallocate -l {{ swap_required }}M /root/swapfile + args: + creates: /root/swapfile + +- name: Ensure swapfile perms + become: yes + file: + path: /root/swapfile + owner: root + group: root + mode: 0600 + +- name: Make swapfile + become: yes + command: mkswap /root/swapfile + +- name: Write swap to fstab + become: yes + mount: + path: none + src: /root/swapfile + fstype: swap + opts: sw + passno: 0 + dump: 0 + state: present + +- name: Add all swap + become: yes + command: swapon -a + +- debug: var=swap_required diff --git a/roles/export-devstack-journal/README.rst b/roles/export-devstack-journal/README.rst new file mode 100644 index 0000000000..5f00592a03 --- /dev/null +++ b/roles/export-devstack-journal/README.rst @@ -0,0 +1,15 @@ +Export journal files from devstack services + +Export the systemd journal for every devstack service in native +journal format as well as text. Also, export a syslog-style file with +kernal and sudo messages. + +Writes the output to the ``logs/`` subdirectory of +``devstack_base_dir``. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/export-devstack-journal/defaults/main.yaml b/roles/export-devstack-journal/defaults/main.yaml new file mode 100644 index 0000000000..fea05c8146 --- /dev/null +++ b/roles/export-devstack-journal/defaults/main.yaml @@ -0,0 +1 @@ +devstack_base_dir: /opt/stack diff --git a/roles/export-devstack-journal/tasks/main.yaml b/roles/export-devstack-journal/tasks/main.yaml new file mode 100644 index 0000000000..b9af02a591 --- /dev/null +++ b/roles/export-devstack-journal/tasks/main.yaml @@ -0,0 +1,29 @@ +# TODO: convert this to ansible +- name: Export journal files + become: true + shell: + cmd: | + u="" + name="" + for u in `systemctl list-unit-files | grep devstack | awk '{print $1}'`; do + name=$(echo $u | sed 's/devstack@/screen-/' | sed 's/\.service//') + journalctl -o short-precise --unit $u | tee {{ devstack_base_dir }}/logs/$name.txt > /dev/null + done + + # Export the journal in export format to make it downloadable + # for later searching. It can then be rewritten to a journal native + # format locally using systemd-journal-remote. This makes a class of + # debugging much easier. We don't do the native conversion here as + # some distros do not package that tooling. + journalctl -u 'devstack@*' -o export | \ + xz --threads=0 - > {{ devstack_base_dir }}/logs/devstack.journal.xz + + # The journal contains everything running under systemd, we'll + # build an old school version of the syslog with just the + # kernel and sudo messages. + journalctl \ + -t kernel \ + -t sudo \ + --no-pager \ + --since="$(cat {{ devstack_base_dir }}/log-start-timestamp.txt)" \ + | tee {{ devstack_base_dir }}/logs/syslog.txt > /dev/null diff --git a/roles/fetch-devstack-log-dir/README.rst b/roles/fetch-devstack-log-dir/README.rst new file mode 100644 index 0000000000..360a2e3dd0 --- /dev/null +++ b/roles/fetch-devstack-log-dir/README.rst @@ -0,0 +1,10 @@ +Fetch content from the devstack log directory + +Copy logs from every host back to the zuul executor. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/fetch-devstack-log-dir/defaults/main.yaml b/roles/fetch-devstack-log-dir/defaults/main.yaml new file mode 100644 index 0000000000..fea05c8146 --- /dev/null +++ b/roles/fetch-devstack-log-dir/defaults/main.yaml @@ -0,0 +1 @@ +devstack_base_dir: /opt/stack diff --git a/roles/fetch-devstack-log-dir/tasks/main.yaml b/roles/fetch-devstack-log-dir/tasks/main.yaml new file mode 100644 index 0000000000..5a198b21b4 --- /dev/null +++ b/roles/fetch-devstack-log-dir/tasks/main.yaml @@ -0,0 +1,5 @@ +- name: Collect devstack logs + synchronize: + dest: "{{ zuul.executor.log_root }}/{{ inventory_hostname }}" + mode: pull + src: "{{ devstack_base_dir }}/logs" diff --git a/roles/run-devstack/README.rst b/roles/run-devstack/README.rst new file mode 100644 index 0000000000..d77eb15e99 --- /dev/null +++ b/roles/run-devstack/README.rst @@ -0,0 +1,8 @@ +Run devstack + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/run-devstack/defaults/main.yaml b/roles/run-devstack/defaults/main.yaml new file mode 100644 index 0000000000..fea05c8146 --- /dev/null +++ b/roles/run-devstack/defaults/main.yaml @@ -0,0 +1 @@ +devstack_base_dir: /opt/stack diff --git a/roles/run-devstack/tasks/main.yaml b/roles/run-devstack/tasks/main.yaml new file mode 100644 index 0000000000..bafebafd65 --- /dev/null +++ b/roles/run-devstack/tasks/main.yaml @@ -0,0 +1,6 @@ +- name: Run devstack + command: ./stack.sh + args: + chdir: "{{devstack_base_dir}}/devstack" + become: true + become_user: stack diff --git a/roles/setup-devstack-cache/README.rst b/roles/setup-devstack-cache/README.rst new file mode 100644 index 0000000000..b8938c3dea --- /dev/null +++ b/roles/setup-devstack-cache/README.rst @@ -0,0 +1,15 @@ +Set up the devstack cache directory + +If the node has a cache of devstack image files, copy it into place. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. + +.. zuul:rolevar:: devstack_cache_dir + :default: /opt/cache + + The directory with the cached files. diff --git a/roles/setup-devstack-cache/defaults/main.yaml b/roles/setup-devstack-cache/defaults/main.yaml new file mode 100644 index 0000000000..c56720b4f5 --- /dev/null +++ b/roles/setup-devstack-cache/defaults/main.yaml @@ -0,0 +1,2 @@ +devstack_base_dir: /opt/stack +devstack_cache_dir: /opt/cache diff --git a/roles/setup-devstack-cache/tasks/main.yaml b/roles/setup-devstack-cache/tasks/main.yaml new file mode 100644 index 0000000000..84f33f0e16 --- /dev/null +++ b/roles/setup-devstack-cache/tasks/main.yaml @@ -0,0 +1,14 @@ +- name: Copy cached devstack files + # This uses hard links to avoid using extra space. + command: "find {{ devstack_cache_dir }}/files -mindepth 1 -maxdepth 1 -exec cp -l {} {{ devstack_base_dir }}/devstack/files/ ;" + become: true + +- name: Set ownership of cached files + file: + path: '{{ devstack_base_dir }}/devstack/files' + state: directory + recurse: true + owner: stack + group: stack + mode: a+r + become: yes diff --git a/roles/setup-devstack-log-dir/README.rst b/roles/setup-devstack-log-dir/README.rst new file mode 100644 index 0000000000..9d8dba3442 --- /dev/null +++ b/roles/setup-devstack-log-dir/README.rst @@ -0,0 +1,11 @@ +Set up the devstack log directory + +Create a log directory on the ephemeral disk partition to save space +on the root device. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/setup-devstack-log-dir/defaults/main.yaml b/roles/setup-devstack-log-dir/defaults/main.yaml new file mode 100644 index 0000000000..fea05c8146 --- /dev/null +++ b/roles/setup-devstack-log-dir/defaults/main.yaml @@ -0,0 +1 @@ +devstack_base_dir: /opt/stack diff --git a/roles/setup-devstack-log-dir/tasks/main.yaml b/roles/setup-devstack-log-dir/tasks/main.yaml new file mode 100644 index 0000000000..b9f38dfacb --- /dev/null +++ b/roles/setup-devstack-log-dir/tasks/main.yaml @@ -0,0 +1,5 @@ +- name: Create logs directory + file: + path: '{{ devstack_base_dir }}/logs' + state: directory + become: yes diff --git a/roles/setup-devstack-source-dirs/README.rst b/roles/setup-devstack-source-dirs/README.rst new file mode 100644 index 0000000000..4ebf8399c2 --- /dev/null +++ b/roles/setup-devstack-source-dirs/README.rst @@ -0,0 +1,11 @@ +Set up the devstack source directories + +Ensure that the base directory exists, and then move the source repos +into it. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/setup-devstack-source-dirs/defaults/main.yaml b/roles/setup-devstack-source-dirs/defaults/main.yaml new file mode 100644 index 0000000000..fea05c8146 --- /dev/null +++ b/roles/setup-devstack-source-dirs/defaults/main.yaml @@ -0,0 +1 @@ +devstack_base_dir: /opt/stack diff --git a/roles/setup-devstack-source-dirs/tasks/main.yaml b/roles/setup-devstack-source-dirs/tasks/main.yaml new file mode 100644 index 0000000000..e6bbae23b7 --- /dev/null +++ b/roles/setup-devstack-source-dirs/tasks/main.yaml @@ -0,0 +1,22 @@ +- name: Find all source repos used by this job + find: + paths: + - src/git.openstack.org/openstack + - src/git.openstack.org/openstack-dev + - src/git.openstack.org/openstack-infra + file_type: directory + register: found_repos + +- name: Copy Zuul repos into devstack working directory + command: rsync -a {{ item.path }} {{ devstack_base_dir }} + with_items: '{{ found_repos.files }}' + become: yes + +- name: Set ownership of repos + file: + path: '{{ devstack_base_dir }}' + state: directory + recurse: true + owner: stack + group: stack + become: yes diff --git a/roles/setup-stack-user/README.rst b/roles/setup-stack-user/README.rst new file mode 100644 index 0000000000..80c4d39eff --- /dev/null +++ b/roles/setup-stack-user/README.rst @@ -0,0 +1,16 @@ +Set up the `stack` user + +Create the stack user, set up its home directory, and allow it to +sudo. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. + +.. zuul:rolevar:: devstack_stack_home_dir + :default: {{ devstack_base_dir }} + + The home directory for the stack user. diff --git a/roles/setup-stack-user/defaults/main.yaml b/roles/setup-stack-user/defaults/main.yaml new file mode 100644 index 0000000000..6d0be666d4 --- /dev/null +++ b/roles/setup-stack-user/defaults/main.yaml @@ -0,0 +1,2 @@ +devstack_base_dir: /opt/stack +devstack_stack_home_dir: '{{ devstack_base_dir }}' diff --git a/roles/setup-stack-user/files/50_stack_sh b/roles/setup-stack-user/files/50_stack_sh new file mode 100644 index 0000000000..4c6b46bdb1 --- /dev/null +++ b/roles/setup-stack-user/files/50_stack_sh @@ -0,0 +1 @@ +stack ALL=(root) NOPASSWD:ALL diff --git a/roles/setup-stack-user/tasks/main.yaml b/roles/setup-stack-user/tasks/main.yaml new file mode 100644 index 0000000000..8384515ebe --- /dev/null +++ b/roles/setup-stack-user/tasks/main.yaml @@ -0,0 +1,45 @@ +- name: Create stack group + group: + name: stack + become: yes + +# NOTE(andreaf) Create a user home_dir is not safe via +# the user module since it will fail if the containing +# folder does not exists. If the folder does exists and +# it's empty, the skeleton is setup and ownership set. +- name: Create the stack user home folder + file: + path: '{{ devstack_stack_home_dir }}' + state: directory + become: yes + +- name: Create stack user + user: + name: stack + shell: /bin/bash + home: '{{ devstack_stack_home_dir }}' + group: stack + become: yes + +- name: Set stack user home directory permissions + file: + path: '{{ devstack_stack_home_dir }}' + mode: 0755 + become: yes + +- name: Copy 50_stack_sh file to /etc/sudoers.d + copy: + src: 50_stack_sh + dest: /etc/sudoers.d + mode: 0440 + owner: root + group: root + become: yes + +- name: Create new/.cache folder within BASE + file: + path: '{{ devstack_stack_home_dir }}/.cache' + state: directory + owner: stack + group: stack + become: yes diff --git a/roles/setup-tempest-user/README.rst b/roles/setup-tempest-user/README.rst new file mode 100644 index 0000000000..bb29c50a28 --- /dev/null +++ b/roles/setup-tempest-user/README.rst @@ -0,0 +1,10 @@ +Set up the `tempest` user + +Create the tempest user and allow it to sudo. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/setup-tempest-user/files/51_tempest_sh b/roles/setup-tempest-user/files/51_tempest_sh new file mode 100644 index 0000000000..f88ff9f4f2 --- /dev/null +++ b/roles/setup-tempest-user/files/51_tempest_sh @@ -0,0 +1,3 @@ +tempest ALL=(root) NOPASSWD:/sbin/ip +tempest ALL=(root) NOPASSWD:/sbin/iptables +tempest ALL=(root) NOPASSWD:/usr/bin/ovsdb-client diff --git a/roles/setup-tempest-user/tasks/main.yaml b/roles/setup-tempest-user/tasks/main.yaml new file mode 100644 index 0000000000..892eaf655a --- /dev/null +++ b/roles/setup-tempest-user/tasks/main.yaml @@ -0,0 +1,20 @@ +- name: Create tempest group + group: + name: tempest + become: yes + +- name: Create tempest user + user: + name: tempest + shell: /bin/bash + group: tempest + become: yes + +- name: Copy 51_tempest_sh to /etc/sudoers.d + copy: + src: 51_tempest_sh + dest: /etc/sudoers.d + owner: root + group: root + mode: 0440 + become: yes diff --git a/roles/start-fresh-logging/README.rst b/roles/start-fresh-logging/README.rst new file mode 100644 index 0000000000..11b029e182 --- /dev/null +++ b/roles/start-fresh-logging/README.rst @@ -0,0 +1,11 @@ +Restart logging on all hosts + +Restart syslog so that the system logs only include output from the +job. + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. diff --git a/roles/start-fresh-logging/defaults/main.yaml b/roles/start-fresh-logging/defaults/main.yaml new file mode 100644 index 0000000000..fea05c8146 --- /dev/null +++ b/roles/start-fresh-logging/defaults/main.yaml @@ -0,0 +1 @@ +devstack_base_dir: /opt/stack diff --git a/roles/start-fresh-logging/tasks/main.yaml b/roles/start-fresh-logging/tasks/main.yaml new file mode 100644 index 0000000000..6c7ba66de7 --- /dev/null +++ b/roles/start-fresh-logging/tasks/main.yaml @@ -0,0 +1,56 @@ +- name: Check for /bin/journalctl file + command: which journalctl + changed_when: False + failed_when: False + register: which_out + +- block: + - name: Get current date + command: date +"%Y-%m-%d %H:%M:%S" + register: date_out + + - name: Copy current date to log-start-timestamp.txt + copy: + dest: "{{ devstack_base_dir }}/log-start-timestamp.txt" + content: "{{ date_out.stdout }}" + when: which_out.rc == 0 + become: yes + +- block: + - name: Stop rsyslog + service: name=rsyslog state=stopped + + - name: Save syslog file prior to devstack run + command: mv /var/log/syslog /var/log/syslog-pre-devstack + + - name: Save kern.log file prior to devstack run + command: mv /var/log/kern.log /var/log/kern_log-pre-devstack + + - name: Recreate syslog file + file: name=/var/log/syslog state=touch + + - name: Recreate syslog file owner and group + command: chown /var/log/syslog --ref /var/log/syslog-pre-devstack + + - name: Recreate syslog file permissions + command: chmod /var/log/syslog --ref /var/log/syslog-pre-devstack + + - name: Add read permissions to all on syslog file + file: name=/var/log/syslog mode=a+r + + - name: Recreate kern.log file + file: name=/var/log/kern.log state=touch + + - name: Recreate kern.log file owner and group + command: chown /var/log/kern.log --ref /var/log/kern_log-pre-devstack + + - name: Recreate kern.log file permissions + command: chmod /var/log/kern.log --ref /var/log/kern_log-pre-devstack + + - name: Add read permissions to all on kern.log file + file: name=/var/log/kern.log mode=a+r + + - name: Start rsyslog + service: name=rsyslog state=started + when: which_out.rc == 1 + become: yes diff --git a/roles/write-devstack-local-conf/README.rst b/roles/write-devstack-local-conf/README.rst new file mode 100644 index 0000000000..e30dfa1e9f --- /dev/null +++ b/roles/write-devstack-local-conf/README.rst @@ -0,0 +1,63 @@ +Write the local.conf file for use by devstack + +**Role Variables** + +.. zuul:rolevar:: devstack_base_dir + :default: /opt/stack + + The devstack base directory. + +.. zuul:rolevar:: devstack_local_conf_path + :default: {{ devstack_base_dir }}/devstack/local.conf + + The path of the local.conf file. + +.. zuul:rolevar:: devstack_localrc + :type: dict + + A dictionary of variables that should be written to the localrc + section of local.conf. The values (which are strings) may contain + bash shell variables, and will be ordered so that variables used by + later entries appear first. + +.. zuul:rolevar:: devstack_local_conf + :type: dict + + A complex argument consisting of nested dictionaries which combine + to form the meta-sections of the local_conf file. The top level is + a dictionary of phases, followed by dictionaries of filenames, then + sections, which finally contain key-value pairs for the INI file + entries in those sections. + + The keys in this dictionary are the devstack phases. + + .. zuul:rolevar:: [phase] + :type: dict + + The keys in this dictionary are the filenames for this phase. + + .. zuul:rolevar:: [filename] + :type: dict + + The keys in this dictionary are the INI sections in this file. + + .. zuul:rolevar:: [section] + :type: dict + + This is a dictionary of key-value pairs which comprise + this section of the INI file. + +.. zuul:rolevar:: devstack_services + :type: dict + + A dictionary mapping service names to boolean values. If the + boolean value is ``false``, a ``disable_service`` line will be + emitted for the service name. If it is ``true``, then + ``enable_service`` will be emitted. All other values are ignored. + +.. zuul:rolevar:: devstack_plugins + :type: dict + + A dictionary mapping a plugin name to a git repo location. If the + location is a non-empty string, then an ``enable_plugin`` line will + be emmitted for the plugin name. diff --git a/roles/write-devstack-local-conf/defaults/main.yaml b/roles/write-devstack-local-conf/defaults/main.yaml new file mode 100644 index 0000000000..491fa0fdb9 --- /dev/null +++ b/roles/write-devstack-local-conf/defaults/main.yaml @@ -0,0 +1,2 @@ +devstack_base_dir: /opt/stack +devstack_local_conf_path: "{{ devstack_base_dir }}/devstack/local.conf" diff --git a/roles/write-devstack-local-conf/library/devstack_local_conf.py b/roles/write-devstack-local-conf/library/devstack_local_conf.py new file mode 100644 index 0000000000..4134beb048 --- /dev/null +++ b/roles/write-devstack-local-conf/library/devstack_local_conf.py @@ -0,0 +1,185 @@ +# Copyright (C) 2017 Red Hat, 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. + +import re + + +class VarGraph(object): + # This is based on the JobGraph from Zuul. + + def __init__(self, vars): + self.vars = {} + self._varnames = set() + self._dependencies = {} # dependent_var_name -> set(parent_var_names) + for k, v in vars.items(): + self._varnames.add(k) + for k, v in vars.items(): + self._addVar(k, str(v)) + + bash_var_re = re.compile(r'\$\{?(\w+)') + def getDependencies(self, value): + return self.bash_var_re.findall(value) + + def _addVar(self, key, value): + if key in self.vars: + raise Exception("Variable {} already added".format(key)) + self.vars[key] = value + # Append the dependency information + self._dependencies.setdefault(key, set()) + try: + for dependency in self.getDependencies(value): + if dependency == key: + # A variable is allowed to reference itself; no + # dependency link needed in that case. + continue + if dependency not in self._varnames: + # It's not necessary to create a link for an + # external variable. + continue + # Make sure a circular dependency is never created + ancestor_vars = self._getParentVarNamesRecursively( + dependency, soft=True) + ancestor_vars.add(dependency) + if any((key == anc_var) for anc_var in ancestor_vars): + raise Exception("Dependency cycle detected in var {}". + format(key)) + self._dependencies[key].add(dependency) + except Exception: + del self.vars[key] + del self._dependencies[key] + raise + + def getVars(self): + ret = [] + keys = sorted(self.vars.keys()) + seen = set() + for key in keys: + dependencies = self.getDependentVarsRecursively(key) + for var in dependencies + [key]: + if var not in seen: + ret.append((var, self.vars[var])) + seen.add(var) + return ret + + def getDependentVarsRecursively(self, parent_var): + dependent_vars = [] + + current_dependent_vars = self._dependencies[parent_var] + for current_var in current_dependent_vars: + if current_var not in dependent_vars: + dependent_vars.append(current_var) + for dep in self.getDependentVarsRecursively(current_var): + if dep not in dependent_vars: + dependent_vars.append(dep) + return dependent_vars + + def _getParentVarNamesRecursively(self, dependent_var, soft=False): + all_parent_vars = set() + vars_to_iterate = set([dependent_var]) + while len(vars_to_iterate) > 0: + current_var = vars_to_iterate.pop() + current_parent_vars = self._dependencies.get(current_var) + if current_parent_vars is None: + if soft: + current_parent_vars = set() + else: + raise Exception("Dependent var {} not found: ".format( + dependent_var)) + new_parent_vars = current_parent_vars - all_parent_vars + vars_to_iterate |= new_parent_vars + all_parent_vars |= new_parent_vars + return all_parent_vars + + +class LocalConf(object): + + def __init__(self, localrc, localconf, services, plugins): + self.localrc = [] + self.meta_sections = {} + if plugins: + self.handle_plugins(plugins) + if services: + self.handle_services(services) + if localrc: + self.handle_localrc(localrc) + if localconf: + self.handle_localconf(localconf) + + def handle_plugins(self, plugins): + for k, v in plugins.items(): + if v: + self.localrc.append('enable_plugin {} {}'.format(k, v)) + + def handle_services(self, services): + for k, v in services.items(): + if v is False: + self.localrc.append('disable_service {}'.format(k)) + elif v is True: + self.localrc.append('enable_service {}'.format(k)) + + def handle_localrc(self, localrc): + vg = VarGraph(localrc) + for k, v in vg.getVars(): + self.localrc.append('{}={}'.format(k, v)) + + def handle_localconf(self, localconf): + for phase, phase_data in localconf.items(): + for fn, fn_data in phase_data.items(): + ms_name = '[[{}|{}]]'.format(phase, fn) + ms_data = [] + for section, section_data in fn_data.items(): + ms_data.append('[{}]'.format(section)) + for k, v in section_data.items(): + ms_data.append('{} = {}'.format(k, v)) + ms_data.append('') + self.meta_sections[ms_name] = ms_data + + def write(self, path): + with open(path, 'w') as f: + f.write('[[local|localrc]]\n') + f.write('\n'.join(self.localrc)) + f.write('\n\n') + for section, lines in self.meta_sections.items(): + f.write('{}\n'.format(section)) + f.write('\n'.join(lines)) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + plugins=dict(type='dict'), + services=dict(type='dict'), + localrc=dict(type='dict'), + local_conf=dict(type='dict'), + path=dict(type='str'), + ) + ) + + p = module.params + lc = LocalConf(p.get('localrc'), + p.get('local_conf'), + p.get('services'), + p.get('plugins')) + lc.write(p['path']) + + module.exit_json() + + +from ansible.module_utils.basic import * # noqa +from ansible.module_utils.basic import AnsibleModule + +if __name__ == '__main__': + main() diff --git a/roles/write-devstack-local-conf/tasks/main.yaml b/roles/write-devstack-local-conf/tasks/main.yaml new file mode 100644 index 0000000000..1d67616dd4 --- /dev/null +++ b/roles/write-devstack-local-conf/tasks/main.yaml @@ -0,0 +1,9 @@ +- name: Write a job-specific local_conf file + become: true + become_user: stack + devstack_local_conf: + path: "{{ devstack_local_conf_path }}" + plugins: "{{ devstack_plugins|default(omit) }}" + services: "{{ devstack_services|default(omit) }}" + localrc: "{{ devstack_localrc|default(omit) }}" + local_conf: "{{ devstack_local_conf|default(omit) }}"