diff --git a/library/haproxy_conf.py b/library/haproxy_conf.py new file mode 100644 index 0000000..de2e452 --- /dev/null +++ b/library/haproxy_conf.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- +# 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 + +from ansible.module_utils.basic import AnsibleModule +from yaml import safe_load as yaml_safe_load + +DOCUMENTATION = ''' +--- +module: haproxy_conf +short_description: Gather the HAProxy config +description: + - Gather the HAProxy config +options: + path: + required: true + description: + - file path to the config file + type: str +author: "Tomas Sedovic" +''' + +EXAMPLES = ''' +- hosts: webservers + tasks: + - name: Gather the HAProxy config + haproxy_conf: path=/etc/haproxy/haproxy.cfg +''' + + +# ConfigParser chokes on both mariadb and haproxy files. Luckily They have +# a syntax approaching ini config file so they are relatively easy to parse. +# This generic ini style config parser is not perfect -- it can ignore some +# valid options -- but good enough for our use case. +def generic_ini_style_conf_parser(file_path, section_regex, option_regex): + config = {} + current_section = None + with open(file_path) as config_file: + for line in config_file: + match_section = re.match(section_regex, line) + if match_section: + current_section = match_section.group(1) + config[current_section] = {} + match_option = re.match(option_regex, line) + if match_option and current_section: + option = re.sub(r'\s+', ' ', match_option.group(1)) + config[current_section][option] = match_option.group(2) + return config + + +def parse_haproxy_conf(file_path): + section_regex = r'^(\w+)' + option_regex = r'^(?:\s+)(\w+(?:\s+\w+)*?)\s+([\w/]*)$' + return generic_ini_style_conf_parser(file_path, section_regex, + option_regex) + + +def main(): + module = AnsibleModule( + argument_spec=yaml_safe_load(DOCUMENTATION)['options'] + ) + + haproxy_conf_path = module.params.get('path') + + try: + config = parse_haproxy_conf(haproxy_conf_path) + except IOError: + module.fail_json(msg="Could not open the haproxy conf file at: '%s'" % + haproxy_conf_path) + + module.exit_json(changed=False, ansible_facts={u'haproxy_conf': config}) + + +if __name__ == '__main__': + main() diff --git a/library/hiera.py b/library/hiera.py new file mode 100644 index 0000000..c5edd02 --- /dev/null +++ b/library/hiera.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# Copyright 2016 Red Hat, Inc. +# All Rights Reserved. +# +# 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 subprocess + +from ansible.module_utils.basic import AnsibleModule +from yaml import safe_load as yaml_safe_load + +DOCUMENTATION = ''' +--- +module: hiera +short_description: Get data from hiera +description: + - Get data from hiera +options: + name: + required: true + description: + - Name to lookup + type: str +author: "Martin Andre (@mandre)" +''' + +EXAMPLES = ''' +- hosts: webservers + tasks: + - name: Lookup foo + hiera: name=foo +''' + + +def main(): + module = AnsibleModule( + argument_spec=yaml_safe_load(DOCUMENTATION)['options'] + ) + + name = module.params.get('name') + + cmd = ['/usr/bin/hiera', '-c', '/etc/puppet/hiera.yaml', name] + result = subprocess.check_output(cmd, universal_newlines=True).rstrip() + + if result == 'nil': + module.fail_json(msg="Failed to retrieve hiera data for {}" + .format(name)) + + module.exit_json(changed=False, + ansible_facts={name: result}) + + +if __name__ == '__main__': + main() diff --git a/library/validations_read_ini.py b/library/validations_read_ini.py new file mode 100644 index 0000000..63cc4af --- /dev/null +++ b/library/validations_read_ini.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- +# 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. + +# Ansible module to read a value from an Ini file. +# Usage: +# - validations_read_ini: path=/path/to/file.ini section=default key=something +# register: my_ini +# +# This will read the `path/to/file.ini` file and read the `Hello!` value under: +# [default] +# something = Hello! +# +# You can register the result and use it later with `{{ my_ini.value }}` + +try: + import configparser as ConfigParser +except ImportError: + import ConfigParser + +from enum import Enum +import os + +from ansible.module_utils.basic import AnsibleModule +from yaml import safe_load as yaml_safe_load + + +# Possible return values +class ReturnValue(Enum): + OK = 0 + INVALID_FORMAT = 1 + KEY_NOT_FOUND = 2 + + +def check_file(path, ignore_missing): + '''Validate entered path''' + + if not (os.path.exists(path) and os.path.isfile(path)): + return "Could not open the ini file: '{}'".format(path) + else: + return '' + + +def get_result(path, section, key, default=None): + '''Get value based on section and key''' + + msg = '' + value = None + config = ConfigParser.SafeConfigParser() + + try: + config.read(path) + except Exception: + msg = "The file '{}' is not in a valid INI format.".format(path) + ret = ReturnValue.INVALID_FORMAT + return (ret, msg, value) + + try: + value = config.get(section, key) + msg = ("The key '{}' under the section '{}' in file {} " + "has the value: '{}'").format(key, section, path, value) + ret = ReturnValue.OK + return (ret, msg, value) + except ConfigParser.Error: + if default: + msg = ("There is no key '{}' under section '{}' in file {}. Using" + " default value '{}'".format(key, section, path, default)) + ret = ReturnValue.OK + value = default + else: + value = None + msg = "There is no key '{}' under the section '{}' in file {}.".format( + key, section, path) + ret = ReturnValue.KEY_NOT_FOUND + return (ret, msg, value) + + +DOCUMENTATION = ''' +--- +module: validations_read_ini +short_description: Get data from an ini file +description: + - Get data from an ini file +options: + path: + required: true + description: + - File path + type: str + section: + required: true + description: + - Section to look up + type: str + key: + required: true + description: + - Section key to look up + type: str + default: + required: false + description: + - Default value if key isn't found + ignore_missing_file: + required: false + description: + - Flag if a missing file should be ignored + type: bool +author: "Tomas Sedovic" +''' + +EXAMPLES = ''' +- hosts: webservers + tasks: + - name: Lookup bar value + validations_read_ini: path=config.ini section=foo key=bar ignore_missing_file=True +''' + + +def main(): + module = AnsibleModule( + argument_spec=yaml_safe_load(DOCUMENTATION)['options'] + ) + + ini_file_path = module.params.get('path') + ignore_missing = module.params.get('ignore_missing_file') + + # Check that file exists + msg = check_file(ini_file_path, ignore_missing) + + if msg != '': + # Opening file failed + if ignore_missing: + module.exit_json(msg=msg, changed=False, value=None) + else: + module.fail_json(msg=msg) + else: + # Try to parse the result from ini file + section = module.params.get('section') + key = module.params.get('key') + default = module.params.get('default') + + ret, msg, value = get_result(ini_file_path, section, key, default) + + if ret == ReturnValue.INVALID_FORMAT: + module.fail_json(msg=msg) + elif ret == ReturnValue.KEY_NOT_FOUND: + module.exit_json(msg=msg, changed=False, value=None) + elif ret == ReturnValue.OK: + module.exit_json(msg=msg, changed=False, value=value) + + +if __name__ == '__main__': + main() diff --git a/library/warn.py b/library/warn.py new file mode 100644 index 0000000..61852f3 --- /dev/null +++ b/library/warn.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# Copyright 2017 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +from ansible.module_utils.basic import AnsibleModule +from yaml import safe_load as yaml_safe_load + +DOCUMENTATION = ''' +--- +module: warn +short_description: Add warning to playbook output +description: + - Add warning to playbook output +options: + msg: + required: true + description: + - The warning text + type: str +author: "Martin Andre (@mandre)" +''' + +EXAMPLES = ''' +- hosts: webservers + tasks: + - name: Output warning message + warn: msg="Warning!" +''' + + +def main(): + module = AnsibleModule( + argument_spec=yaml_safe_load(DOCUMENTATION)['options'] + ) + + msg = module.params.get('msg') + + module.exit_json(changed=False, + warnings=[msg]) + + +if __name__ == '__main__': + main() diff --git a/roles/advanced_format_512e_support/molecule/default/Dockerfile.j2 b/roles/advanced_format_512e_support/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/advanced_format_512e_support/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/advanced_format_512e_support/molecule/default/molecule.yml b/roles/advanced_format_512e_support/molecule/default/molecule.yml new file mode 100644 index 0000000..f87e178 --- /dev/null +++ b/roles/advanced_format_512e_support/molecule/default/molecule.yml @@ -0,0 +1,47 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/advanced_format_512e_support/molecule/default/playbook.yml b/roles/advanced_format_512e_support/molecule/default/playbook.yml new file mode 100644 index 0000000..6c61a39 --- /dev/null +++ b/roles/advanced_format_512e_support/molecule/default/playbook.yml @@ -0,0 +1,26 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + tasks: + - name: Warn developers about the lack of molecule testing + fail: + msg: >- + This role needs molecule tests! diff --git a/roles/advanced_format_512e_support/tasks/main.yml b/roles/advanced_format_512e_support/tasks/main.yml new file mode 100644 index 0000000..0d507e0 --- /dev/null +++ b/roles/advanced_format_512e_support/tasks/main.yml @@ -0,0 +1,10 @@ +--- +- name: List the available drives + register: drive_list + command: "ls /sys/class/block/" + changed_when: false + +- name: Detect whether the drive uses Advanced Format + advanced_format: drive={{ item }} + when: item is match("^sd.$") + with_items: "{{ drive_list.stdout_lines }}" diff --git a/roles/advanced_format_512e_support/vars/main.yml b/roles/advanced_format_512e_support/vars/main.yml new file mode 100644 index 0000000..6ed23ac --- /dev/null +++ b/roles/advanced_format_512e_support/vars/main.yml @@ -0,0 +1,9 @@ +--- +metadata: + name: Advanced Format 512e Support + description: > + Detect whether the undercloud disks use Advanced Format. If they do, + the overcloud images may fail to upload to Glance. + groups: + - prep + - pre-deployment diff --git a/roles/check_latest_packages_version/defaults/main.yml b/roles/check_latest_packages_version/defaults/main.yml new file mode 100644 index 0000000..e8e9d2e --- /dev/null +++ b/roles/check_latest_packages_version/defaults/main.yml @@ -0,0 +1,10 @@ +--- +tripleoclient: >- + {%- if ansible_distribution == 'RedHat' and ansible_distribution_major_version == '8' -%} + python3-tripleoclient + {%- else -%} + python2-tripleoclient + {%- endif -%} + +packages: + - "{{ tripleoclient }}" diff --git a/roles/check_latest_packages_version/molecule/default/Dockerfile.j2 b/roles/check_latest_packages_version/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/check_latest_packages_version/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/check_latest_packages_version/molecule/default/molecule.yml b/roles/check_latest_packages_version/molecule/default/molecule.yml new file mode 100644 index 0000000..b78ef8d --- /dev/null +++ b/roles/check_latest_packages_version/molecule/default/molecule.yml @@ -0,0 +1,47 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools PyYAML + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/check_latest_packages_version/molecule/default/playbook.yml b/roles/check_latest_packages_version/molecule/default/playbook.yml new file mode 100644 index 0000000..db4a659 --- /dev/null +++ b/roles/check_latest_packages_version/molecule/default/playbook.yml @@ -0,0 +1,51 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + + tasks: + - name: Validate No Available Update for patch rpm + include_role: + name: check_latest_packages_version + vars: + packages: + - patch + + - name: Working Detection of Update for Pam package + block: + - include_role: + name: check_latest_packages_version + vars: + packages: + - pam + + rescue: + - name: Clear host errors + meta: clear_host_errors + + - debug: + msg: The validation works! End the playbook run + + - name: End play + meta: end_play + + - name: Fail the test + fail: + msg: | + The check_latest_packages_version role should have detected + that packages have available updates. diff --git a/roles/check_latest_packages_version/molecule/default/prepare.yml b/roles/check_latest_packages_version/molecule/default/prepare.yml new file mode 100644 index 0000000..c55cfc7 --- /dev/null +++ b/roles/check_latest_packages_version/molecule/default/prepare.yml @@ -0,0 +1,25 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + gather_facts: false + + tasks: + - name: install patch rpm + package: + name: patch diff --git a/roles/check_latest_packages_version/tasks/main.yml b/roles/check_latest_packages_version/tasks/main.yml new file mode 100644 index 0000000..dd782ea --- /dev/null +++ b/roles/check_latest_packages_version/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: Get available updates for packages + check_package_update: + package: "{{ item }}" + pkg_mgr: "{{ ansible_pkg_mgr }}" + with_items: "{{ packages }}" + register: updates + +- name: Check if current version is the latest one + fail: + msg: >- + A newer version of the {{ item.name }} package is + available: {{ item.new_version }}-{{ item.new_release }} + (currently {{ item.current_version }}-{{ item.current_release }}) + with_items: "{{ updates.results }}" + when: item.new_version diff --git a/roles/check_latest_packages_version/vars/main.yml b/roles/check_latest_packages_version/vars/main.yml new file mode 100644 index 0000000..fa84039 --- /dev/null +++ b/roles/check_latest_packages_version/vars/main.yml @@ -0,0 +1,8 @@ +--- +metadata: + name: Check if latest version of packages is installed + description: > + Makes sure python-tripleoclient is at its latest version + before starting an upgrade. + groups: + - pre-upgrade diff --git a/roles/dns/defaults/main.yml b/roles/dns/defaults/main.yml new file mode 100644 index 0000000..bb0cae8 --- /dev/null +++ b/roles/dns/defaults/main.yml @@ -0,0 +1,2 @@ +--- +server_to_lookup: example.com diff --git a/roles/dns/molecule/default/Dockerfile.j2 b/roles/dns/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/dns/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/dns/molecule/default/molecule.yml b/roles/dns/molecule/default/molecule.yml new file mode 100644 index 0000000..5eb59c2 --- /dev/null +++ b/roles/dns/molecule/default/molecule.yml @@ -0,0 +1,46 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/dns/molecule/default/playbook.yml b/roles/dns/molecule/default/playbook.yml new file mode 100644 index 0000000..1a6d6bd --- /dev/null +++ b/roles/dns/molecule/default/playbook.yml @@ -0,0 +1,47 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + + tasks: + - name: Should get a success + include_role: + name: dns + vars: + server_to_lookup: www.redhat.com + - name: Should properly fail + block: + - include_role: + name: dns + vars: + server_to_lookup: role.dns.domain.do-not.exists + + rescue: + - name: Clear host errors + meta: clear_host_errors + + - debug: + msg: The validation works! End the playbook run + + - name: End play + meta: end_play + + - name: Fail the test + fail: + msg: | + The dns role should have detected a faulty DNS configuration diff --git a/roles/dns/tasks/main.yml b/roles/dns/tasks/main.yml new file mode 100644 index 0000000..aefea08 --- /dev/null +++ b/roles/dns/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: Ensure DNS resolution works + command: "getent hosts {{ server_to_lookup }}" + changed_when: false diff --git a/roles/dns/vars/main.yml b/roles/dns/vars/main.yml new file mode 100644 index 0000000..e3e6f38 --- /dev/null +++ b/roles/dns/vars/main.yml @@ -0,0 +1,7 @@ +--- +metadata: + name: Verify DNS + description: > + Verify that the DNS resolution works + groups: + - pre-deployment diff --git a/roles/haproxy/README.md b/roles/haproxy/README.md new file mode 100644 index 0000000..8c10180 --- /dev/null +++ b/roles/haproxy/README.md @@ -0,0 +1,42 @@ +haproxy +======= + +An Ansible role to check if the HAProxy configuration has recommended values. + +Requirements +------------ + +This role requires an Up and Running Overcloud + +Role Variables +-------------- + +- config_file: '/var/lib/config-data/puppet-generated/haproxy/etc/haproxy/haproxy.cfg' +- global_maxconn_min: 20480 +- defaults_maxconn_min: 4096 +- defaults_timeout_queue: '2m' +- defaults_timeout_client: '2m' +- defaults_timeout_server: '2m' +- defaults_timeout_check: '10s' + +Dependencies +------------ + +No dependencies + +Example Playbook +---------------- + + - hosts: undercloud + roles: + - { role: haproxy } + +License +------- + +Apache + +Author Information +------------------ + +Red Hat TripleO Validations Team. diff --git a/roles/haproxy/defaults/main.yml b/roles/haproxy/defaults/main.yml new file mode 100644 index 0000000..4cd0eb2 --- /dev/null +++ b/roles/haproxy/defaults/main.yml @@ -0,0 +1,8 @@ +--- +haproxy_config_file: '/var/lib/config-data/puppet-generated/haproxy/etc/haproxy/haproxy.cfg' +global_maxconn_min: 20480 +defaults_maxconn_min: 4096 +defaults_timeout_queue: '2m' +defaults_timeout_client: '2m' +defaults_timeout_server: '2m' +defaults_timeout_check: '10s' diff --git a/roles/haproxy/molecule/default/Dockerfile.j2 b/roles/haproxy/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..a2e66f4 --- /dev/null +++ b/roles/haproxy/molecule/default/Dockerfile.j2 @@ -0,0 +1,36 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/haproxy/molecule/default/molecule.yml b/roles/haproxy/molecule/default/molecule.yml new file mode 100644 index 0000000..9fa3f4c --- /dev/null +++ b/roles/haproxy/molecule/default/molecule.yml @@ -0,0 +1,47 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools haproxy PyYAML + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools haproxy PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: true + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/haproxy/molecule/default/playbook.yml b/roles/haproxy/molecule/default/playbook.yml new file mode 100644 index 0000000..9960f97 --- /dev/null +++ b/roles/haproxy/molecule/default/playbook.yml @@ -0,0 +1,71 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + vars: + haproxy_config_file: /haproxy.cfg + + tasks: + - name: create haproxy config file + copy: + dest: /haproxy.cfg + content: | + # This file managed by Puppet + global + daemon + group haproxy + log /dev/log local0 + maxconn 100 + pidfile /var/run/haproxy.pid + ssl-default-bind-ciphers !SSLv2:kEECDH:kRSA:kEDH:kPSK:+3DES:!aNULL:!eNULL:!MD5:!EXP:!RC4:!SEED:!IDEA:!DES + ssl-default-bind-options no-sslv3 no-tlsv10 + stats socket /var/lib/haproxy/stats mode 600 level user + stats timeout 1s + user haproxy + + defaults + log global + maxconn 100 + mode tcp + retries 1 + timeout http-request 1s + timeout queue 1s + timeout connect 1s + timeout client 1s + timeout server 1s + timeout check 1s + - block: + - include_role: + name: haproxy + rescue: + - name: Clear host errors + meta: clear_host_errors + + - debug: + msg: The validation works! End the playbook run + + - name: End play + meta: end_play + + - name: Fail the test + fail: + msg: | + The haproxy role should have detected issues within haproxy + configuration file! diff --git a/roles/haproxy/tasks/main.yml b/roles/haproxy/tasks/main.yml new file mode 100644 index 0000000..6ed7b80 --- /dev/null +++ b/roles/haproxy/tasks/main.yml @@ -0,0 +1,51 @@ +--- +- name: Gather the HAProxy config + become: true + haproxy_conf: + path: "{{ haproxy_config_file }}" + +- name: Verify global maxconn + fail: + msg: >- + The 'global maxconn' value '{{ haproxy_conf.global.maxconn }}' + must be greater than {{ global_maxconn_min }} + failed_when: haproxy_conf.global.maxconn|int < global_maxconn_min + +- name: Verify defaults maxconn + fail: + msg: >- + The 'defaults maxconn' value '{{ haproxy_conf.defaults.maxconn }}' + must be greater than {{ defaults_maxconn_min }} + failed_when: haproxy_conf.defaults.maxconn|int < defaults_maxconn_min + +- name: Verify defaults timeout queue + fail: + msg: >- + The 'timeout queue' option in 'defaults' is + '{{ haproxy_conf.defaults['timeout queue'] }}', + but must be set to {{ defaults_timeout_queue }} + failed_when: "haproxy_conf.defaults['timeout queue'] != defaults_timeout_queue" + +- name: Verify defaults timeout client + fail: + msg: >- + The 'timeout client' option in 'defaults' is + '{{ haproxy_conf.defaults['timeout client'] }}', + but must be set to {{ defaults_timeout_client }} + failed_when: "haproxy_conf.defaults['timeout client'] != defaults_timeout_client" + +- name: Verify defaults timeout server + fail: + msg: >- + The 'timeout server' option in 'defaults' is + '{{ haproxy_conf.defaults['timeout server'] }}', + but must be set to {{ defaults_timeout_server }} + failed_when: "haproxy_conf.defaults['timeout server'] != defaults_timeout_server" + +- name: Verify defaults timeout check + fail: + msg: >- + The 'timeout check' option in 'defaults' is + '{{ haproxy_conf.defaults['timeout check'] }}', + but must be set to {{ defaults_timeout_check }} + failed_when: "haproxy_conf.defaults['timeout check'] != defaults_timeout_check" diff --git a/roles/haproxy/vars/main.yml b/roles/haproxy/vars/main.yml new file mode 100644 index 0000000..a04d0e8 --- /dev/null +++ b/roles/haproxy/vars/main.yml @@ -0,0 +1,6 @@ +--- +metadata: + name: HAProxy configuration + description: Verify the HAProxy configuration has recommended values. + groups: + - post-deployment diff --git a/roles/no_op/tasks/main.yml b/roles/no_op/tasks/main.yml new file mode 100644 index 0000000..904ba20 --- /dev/null +++ b/roles/no_op/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: Run a no-op validation everywhere + debug: + msg: "This is a no-op action for testing that the validations framework runs" diff --git a/roles/no_op/vars/main.yml b/roles/no_op/vars/main.yml new file mode 100644 index 0000000..4202d35 --- /dev/null +++ b/roles/no_op/vars/main.yml @@ -0,0 +1,8 @@ +--- +metadata: + name: NO-OP validation + description: > + A simple validation doing nothing in order to test that + the validations framework works. + groups: + - no-op diff --git a/roles/ntp/molecule/default/Dockerfile.j2 b/roles/ntp/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/ntp/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/ntp/molecule/default/molecule.yml b/roles/ntp/molecule/default/molecule.yml new file mode 100644 index 0000000..f87e178 --- /dev/null +++ b/roles/ntp/molecule/default/molecule.yml @@ -0,0 +1,47 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/ntp/molecule/default/playbook.yml b/roles/ntp/molecule/default/playbook.yml new file mode 100644 index 0000000..6c61a39 --- /dev/null +++ b/roles/ntp/molecule/default/playbook.yml @@ -0,0 +1,26 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + tasks: + - name: Warn developers about the lack of molecule testing + fail: + msg: >- + This role needs molecule tests! diff --git a/roles/ntp/tasks/main.yml b/roles/ntp/tasks/main.yml new file mode 100644 index 0000000..36c67fa --- /dev/null +++ b/roles/ntp/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: Get if chrony is enabled + become: true + hiera: + name: "chrony_enabled" + +- when: chrony_enabled|bool + block: + - name: Populate service facts + service_facts: # needed to make yaml happy + + - name: Fail if chronyd service is not running + fail: + msg: "Chronyd service is not running" + when: "ansible_facts.services['chronyd.service'].state != 'running'" + + - name: Run chronyc + become: true + command: chronyc -a 'burst 4/4' + changed_when: false + +# ntpstat returns 0 if synchronised and non-zero otherwise: +- name: Run ntpstat + command: ntpstat + changed_when: false + when: not chrony_enabled|bool diff --git a/roles/ntp/vars/main.yml b/roles/ntp/vars/main.yml new file mode 100644 index 0000000..992ea6f --- /dev/null +++ b/roles/ntp/vars/main.yml @@ -0,0 +1,10 @@ +--- +metadata: + name: Verify all deployed nodes have their clock synchronised + description: > + Each overcloud node should have their clocks synchronised. + + The deployment should configure and run chronyd. This validation verifies + that it is indeed running and connected to an NTP server on all nodes. + groups: + - post-deployment diff --git a/roles/service_status/defaults/main.yaml b/roles/service_status/defaults/main.yaml new file mode 100644 index 0000000..87e9c20 --- /dev/null +++ b/roles/service_status/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +service_status_podman_opt: '' diff --git a/roles/service_status/molecule/default/Dockerfile.j2 b/roles/service_status/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/service_status/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/service_status/molecule/default/molecule.yml b/roles/service_status/molecule/default/molecule.yml new file mode 100644 index 0000000..b8430d5 --- /dev/null +++ b/roles/service_status/molecule/default/molecule.yml @@ -0,0 +1,49 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + command: /sbin/init + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + command: /sbin/init + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/service_status/molecule/default/playbook.yml b/roles/service_status/molecule/default/playbook.yml new file mode 100644 index 0000000..39290cd --- /dev/null +++ b/roles/service_status/molecule/default/playbook.yml @@ -0,0 +1,25 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + tasks: + - name: Full check with defaults + include_role: + name: service_status diff --git a/roles/service_status/molecule/docker/Dockerfile.j2 b/roles/service_status/molecule/docker/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/service_status/molecule/docker/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/service_status/molecule/docker/molecule.yml b/roles/service_status/molecule/docker/molecule.yml new file mode 100644 index 0000000..c4b5b91 --- /dev/null +++ b/roles/service_status/molecule/docker/molecule.yml @@ -0,0 +1,61 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + command: /sbin/init + capabilities: + - SYS_ADMIN + privileged: true + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /sys/fs/cgroup:/sys/fs/cgroup:ro + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + command: /sbin/init + capabilities: + - SYS_ADMIN + privileged: true + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /sys/fs/cgroup:/sys/fs/cgroup:ro + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML python*-libselinux + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/service_status/molecule/docker/playbook.yml b/roles/service_status/molecule/docker/playbook.yml new file mode 100644 index 0000000..b3db61a --- /dev/null +++ b/roles/service_status/molecule/docker/playbook.yml @@ -0,0 +1,59 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + become: true + + tasks: + - name: "Check containers - docker version, no service" + include_role: + name: service_status + tasks_from: containers.yaml + + - name: "Check containers - docker version, with service" + block: + - name: Activate docker service + service: + name: docker + state: started + enabled: true + + - name: Catch failure + block: + - name: Run check + include_role: + name: service_status + tasks_from: containers.yaml + + rescue: + - name: Clear host errors + meta: clear_host_errors + + - name: Test output + debug: + msg: | + Success finding broken containers + + - name: End play + meta: end_play + + - name: Fail if this point is reached + fail: + msg: | + Did not find broken containers diff --git a/roles/service_status/molecule/docker/prepare.yml b/roles/service_status/molecule/docker/prepare.yml new file mode 100644 index 0000000..eb96fe0 --- /dev/null +++ b/roles/service_status/molecule/docker/prepare.yml @@ -0,0 +1,65 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + gather_facts: false + + tasks: + - name: install docker + package: + name: docker + + - name: fake docker exe + copy: + dest: /usr/bin/docker + mode: 0755 + content: | + #!/bin/sh + echo 'thirsty_goldwasser Exited (0) 12 seconds ago' + echo 'fedora28 Exited (255) 7 hours ago' + echo 'centos7 Exited (255) 7 hours ago' + + - name: docker unit override basedir + file: + path: /etc/systemd/system/docker.service.d + state: directory + + - name: fake docker unit + copy: + dest: /etc/systemd/system/docker.service.d/override.conf + content: | + [Unit] + After=network.target + Wants= + Requires= + + [Service] + Type=simple + ExecStart= + ExecStart=/usr/bin/fake + Restart= + + - name: fake docker exec for unit + copy: + dest: /usr/bin/fake + mode: 0755 + content: | + #!/bin/sh + while true; do + sleep 5; + done diff --git a/roles/service_status/molecule/podman/Dockerfile.j2 b/roles/service_status/molecule/podman/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/service_status/molecule/podman/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/service_status/molecule/podman/bolt_state.db b/roles/service_status/molecule/podman/bolt_state.db new file mode 100644 index 0000000..bab3585 Binary files /dev/null and b/roles/service_status/molecule/podman/bolt_state.db differ diff --git a/roles/service_status/molecule/podman/molecule.yml b/roles/service_status/molecule/podman/molecule.yml new file mode 100644 index 0000000..b8430d5 --- /dev/null +++ b/roles/service_status/molecule/podman/molecule.yml @@ -0,0 +1,49 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + command: /sbin/init + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + command: /sbin/init + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/service_status/molecule/podman/playbook.yml b/roles/service_status/molecule/podman/playbook.yml new file mode 100644 index 0000000..9bef3a2 --- /dev/null +++ b/roles/service_status/molecule/podman/playbook.yml @@ -0,0 +1,46 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + vars: + service_status_podman_opt: '--storage-driver=vfs' + + tasks: + - name: Check podman container state + block: + - name: Detect failed podman containers + include_role: + name: service_status + tasks_from: containers.yaml + rescue: + - name: Clear host errors + meta: clear_host_errors + + - name: Test output + debug: + msg: | + Properly detected failed container + + - name: End play now + meta: end_play + + - name: Fail if we get to this point + fail: + msg: | + Did not detect failed container diff --git a/roles/service_status/molecule/podman/prepare.yml b/roles/service_status/molecule/podman/prepare.yml new file mode 100644 index 0000000..ee46fa0 --- /dev/null +++ b/roles/service_status/molecule/podman/prepare.yml @@ -0,0 +1,39 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + gather_facts: false + + tasks: + - name: install podman + package: + name: podman + + - name: Create libpod arbo + file: + path: '/var/lib/containers/{{ item }}' + state: directory + loop: + - storage + - storage/libpod + + - name: Insert failed container DB + copy: + src: ./bolt_state.db + dest: /var/lib/containers/storage/libpod/bolt_state.db + setype: container_var_lib_t diff --git a/roles/service_status/molecule/systemd/Dockerfile.j2 b/roles/service_status/molecule/systemd/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/service_status/molecule/systemd/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/service_status/molecule/systemd/molecule.yml b/roles/service_status/molecule/systemd/molecule.yml new file mode 100644 index 0000000..b8430d5 --- /dev/null +++ b/roles/service_status/molecule/systemd/molecule.yml @@ -0,0 +1,49 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + command: /sbin/init + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + command: /sbin/init + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/service_status/molecule/systemd/playbook.yml b/roles/service_status/molecule/systemd/playbook.yml new file mode 100644 index 0000000..0f2cd0b --- /dev/null +++ b/roles/service_status/molecule/systemd/playbook.yml @@ -0,0 +1,44 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + tasks: + - name: Check service + block: + - name: Run validation + include_role: + name: service_status + tasks_from: systemd.yaml + rescue: + - name: Clear errors + meta: clear_host_errors + + - name: Test output + debug: + msg: | + Successfully detected failed unit + + - name: End play + meta: end_play + + - name: Fail if this point is reached + fail: + msg: | + Did not detect failed unit diff --git a/roles/service_status/molecule/systemd/prepare.yml b/roles/service_status/molecule/systemd/prepare.yml new file mode 100644 index 0000000..4e1cc89 --- /dev/null +++ b/roles/service_status/molecule/systemd/prepare.yml @@ -0,0 +1,39 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + gather_facts: false + + tasks: + - name: Create fake, failing unit + copy: + dest: /etc/systemd/system/tripleo_failed-unit.service + content: | + [Unit] + After=network.target + + [Service] + Type=simple + ExecStart=/usr/bin/false + + - name: Enable and start broken thing + ignore_errors: true + service: + name: tripleo_failed-unit + state: started + enabled: true diff --git a/roles/service_status/tasks/containers.yaml b/roles/service_status/tasks/containers.yaml new file mode 100644 index 0000000..3817943 --- /dev/null +++ b/roles/service_status/tasks/containers.yaml @@ -0,0 +1,59 @@ +--- +- name: Gather package facts + package_facts: + manager: auto + +- name: Is docker running + systemd: + name: docker + register: docker_svc + when: ansible_facts.packages['docker'] is defined + +- name: Do we have podman + stat: + path: /usr/bin/podman + register: podman_stat + +- name: Podman related block + when: podman_stat.stat.exists + block: + - name: Get failed containers for podman + become: true + shell: | + podman {{ service_status_podman_opt }} ps -a --filter 'status=exited' --format {{ "'{{ .Names }} {{ .Status }}'" }} + register: failed_podman + + - name: Fail if we detect failed podman container + fail: + msg: | + Failed container detected. + On CI, please check the following locations + /var/log/extras/failed_containers.log + /var/log/extras/podman + when: item is not match(".* Exited \(0\) .* ago") + loop: "{{ failed_podman.stdout_lines }}" + +- name: Docker related block + when: + - ansible_facts.packages['docker'] is defined + - docker_svc.status['SubState'] == 'running' + block: + - name: Get failed containers from docker + become: true + shell: > + {% raw %} + docker ps -a --filter 'status=exited' --format '{{ .Names }} {{ .Status }}' + {% endraw %} + register: failed_docker + + - name: Fail if we detect failed docker container + fail: + msg: | + Failed container detected. + On CI, please check the following locations + /var/log/extras/failed_containers.log + /var/log/extras/docker + when: + - failed_docker is defined + - item is not match(".* Exited \(0\) .* ago") + loop: "{{ failed_docker.stdout_lines }}" diff --git a/roles/service_status/tasks/main.yaml b/roles/service_status/tasks/main.yaml new file mode 100644 index 0000000..8e23251 --- /dev/null +++ b/roles/service_status/tasks/main.yaml @@ -0,0 +1,3 @@ +--- +- include_tasks: containers.yaml +- include_tasks: systemd.yaml diff --git a/roles/service_status/tasks/systemd.yaml b/roles/service_status/tasks/systemd.yaml new file mode 100644 index 0000000..a7b726b --- /dev/null +++ b/roles/service_status/tasks/systemd.yaml @@ -0,0 +1,13 @@ +--- +- name: Get failed services from Systemd + shell: > + systemctl list-units --failed --plain --no-legend --no-pager "tripleo_*" + register: systemd_state + changed_when: false + +- name: Fails if we find failed systemd units + assert: + that: + - systemd_state.stdout_lines|length == 0 + fail_msg: "The following services failed {{ systemd_state.stdout_lines }}" + success_msg: "All tripleo units are working fine" diff --git a/roles/stonith_exists/molecule/default/Dockerfile.j2 b/roles/stonith_exists/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/stonith_exists/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/stonith_exists/molecule/default/molecule.yml b/roles/stonith_exists/molecule/default/molecule.yml new file mode 100644 index 0000000..5eb59c2 --- /dev/null +++ b/roles/stonith_exists/molecule/default/molecule.yml @@ -0,0 +1,46 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/stonith_exists/molecule/default/playbook.yml b/roles/stonith_exists/molecule/default/playbook.yml new file mode 100644 index 0000000..aefd9ff --- /dev/null +++ b/roles/stonith_exists/molecule/default/playbook.yml @@ -0,0 +1,56 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + + tasks: + - name: Safe run + include_role: + name: stonith_exists + + - name: Fail the validation + block: + - name: Faulty pcs script + copy: + dest: /usr/bin/pcs + mode: 0755 + content: | + #!/bin/sh + echo "NO stonith devices configured" + exit 0 + + - name: Run validation + include_role: + name: stonith_exists + + rescue: + - name: Clear host errors + meta: clear_host_errors + + - name: Test output + debug: + msg: The validation works! End play + + - name: End play + meta: end_play + + - name: Fail playbook if reached + fail: + msg: | + The stonith_exists validation didn't properly detect failed + stonith config diff --git a/roles/stonith_exists/molecule/default/prepare.yml b/roles/stonith_exists/molecule/default/prepare.yml new file mode 100644 index 0000000..eba0c86 --- /dev/null +++ b/roles/stonith_exists/molecule/default/prepare.yml @@ -0,0 +1,30 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + gather_facts: false + + tasks: + - name: Populate successful stonith + copy: + dest: /usr/bin/pcs + mode: 0755 + content: | + #!/bin/sh + echo "Stonith service configured" + exit 0 diff --git a/roles/stonith_exists/tasks/main.yml b/roles/stonith_exists/tasks/main.yml new file mode 100644 index 0000000..4277134 --- /dev/null +++ b/roles/stonith_exists/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Check if we are in HA cluster environment + become: true + register: pcs_cluster_status + command: pcs cluster status + failed_when: false + changed_when: false + +- name: Get all currently configured stonith devices + become: true + command: "pcs stonith" + register: stonith_devices + changed_when: false + when: "pcs_cluster_status.rc == 0" + +- name: Verify the stonith device are configured + fail: + msg: "Stonith devices are not configured." + when: > + pcs_cluster_status.rc == 0 + and + 'NO stonith devices configured' in stonith_devices.stdout diff --git a/roles/stonith_exists/vars/main.yml b/roles/stonith_exists/vars/main.yml new file mode 100644 index 0000000..34a26d1 --- /dev/null +++ b/roles/stonith_exists/vars/main.yml @@ -0,0 +1,11 @@ +--- +metadata: + name: Validate stonith devices + description: > + Verify that stonith devices are configured for your OpenStack Platform HA cluster. + We don't configure stonith device with TripleO Installer. Because the hardware + configuration may be differ in each environment and requires different fence agents. + How to configure fencing please read + https://access.redhat.com/documentation/en/red-hat-openstack-platform/8/paged/director-installation-and-usage/86-fencing-the-controller-nodes + groups: + - post-deployment diff --git a/roles/undercloud_cpu/README.md b/roles/undercloud_cpu/README.md new file mode 100644 index 0000000..3b7728a --- /dev/null +++ b/roles/undercloud_cpu/README.md @@ -0,0 +1,36 @@ +Undercloud-cpu +============== + +An Ansible role to check if the Undercloud fits the CPU core requirements + +Requirements +------------ + +This role could be used before or/and after the Undercloud installation. + +Role Variables +-------------- + +- min_undercloud_cpu_count: <8> -- Minimal number of CPU core + +Dependencies +------------ + +No dependencies. + +Example Playbook +---------------- + + - hosts: undercloud + roles: + - { role: undercloud-cpu, min_undercloud_cpu_count: 42 } + +License +------- + +Apache 2.0 + +Author Information +------------------ + +Red Hat TripleO Validations Team diff --git a/roles/undercloud_cpu/defaults/main.yml b/roles/undercloud_cpu/defaults/main.yml new file mode 100644 index 0000000..5a15292 --- /dev/null +++ b/roles/undercloud_cpu/defaults/main.yml @@ -0,0 +1,3 @@ +--- + +min_undercloud_cpu_count: 8 diff --git a/roles/undercloud_cpu/molecule/default/Dockerfile.j2 b/roles/undercloud_cpu/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/undercloud_cpu/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/undercloud_cpu/molecule/default/molecule.yml b/roles/undercloud_cpu/molecule/default/molecule.yml new file mode 100644 index 0000000..5eb59c2 --- /dev/null +++ b/roles/undercloud_cpu/molecule/default/molecule.yml @@ -0,0 +1,46 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/undercloud_cpu/molecule/default/playbook.yml b/roles/undercloud_cpu/molecule/default/playbook.yml new file mode 100644 index 0000000..f6802bf --- /dev/null +++ b/roles/undercloud_cpu/molecule/default/playbook.yml @@ -0,0 +1,42 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + + vars: + min_undercloud_cpu_count: 100 + + tasks: + - block: + - include_role: + name: undercloud_cpu + rescue: + - name: Clear host errors + meta: clear_host_errors + + - debug: + msg: The validation works! End the playbook run + + - name: End play + meta: end_play + + - name: Fail the test + fail: + msg: | + The undercloud_cpu role should have detected that there is not + enough CPU diff --git a/roles/undercloud_cpu/tasks/main.yml b/roles/undercloud_cpu/tasks/main.yml new file mode 100644 index 0000000..eeb0031 --- /dev/null +++ b/roles/undercloud_cpu/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- name: Verify the number of CPU cores + fail: + msg: >- + There are {{ ansible_processor_vcpus }} cores in the system, + but there should be at least {{ min_undercloud_cpu_count }} + failed_when: "ansible_processor_vcpus|int < min_undercloud_cpu_count|int" diff --git a/roles/undercloud_cpu/vars/main.yaml b/roles/undercloud_cpu/vars/main.yaml new file mode 100644 index 0000000..2766e82 --- /dev/null +++ b/roles/undercloud_cpu/vars/main.yaml @@ -0,0 +1,10 @@ +--- +metadata: + name: Verify undercloud fits the CPU core requirements + description: > + Make sure that the undercloud has enough CPU cores. + + https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux_OpenStack_Platform/7/html/Director_Installation_and_Usage/sect-Undercloud_Requirements.html + groups: + - prep + - pre-introspection diff --git a/roles/undercloud_disk_space/README.md b/roles/undercloud_disk_space/README.md new file mode 100644 index 0000000..73bf492 --- /dev/null +++ b/roles/undercloud_disk_space/README.md @@ -0,0 +1,36 @@ +Undercloud-disk-space +===================== + +An Ansible role to verify if the Undercloud fits the disk space requirements. + +Requirements +------------ + +This role could be used before or/and after the Undercloud installation. + +Role Variables +-------------- + +- Volumes: a dictionary of mount points and their minimum sizes + +Dependencies +------------ + +No Dependencies + +Example Playbook +---------------- + + - hosts: servers + roles: + - { role: undercloud-disk-space} + +License +------- + +Apache + +Author Information +------------------ + +Red Hat TripleO Validation Team diff --git a/roles/undercloud_disk_space/defaults/main.yml b/roles/undercloud_disk_space/defaults/main.yml new file mode 100644 index 0000000..1484cfd --- /dev/null +++ b/roles/undercloud_disk_space/defaults/main.yml @@ -0,0 +1,8 @@ +--- +volumes: + - {mount: /var/lib/docker, min_size: 10} + - {mount: /var/lib/config-data, min_size: 3} + - {mount: /var/log, min_size: 3} + - {mount: /usr, min_size: 5} + - {mount: /var, min_size: 20} + - {mount: /, min_size: 25} diff --git a/roles/undercloud_disk_space/molecule/default/Dockerfile.j2 b/roles/undercloud_disk_space/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/undercloud_disk_space/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/undercloud_disk_space/molecule/default/molecule.yml b/roles/undercloud_disk_space/molecule/default/molecule.yml new file mode 100644 index 0000000..5eb59c2 --- /dev/null +++ b/roles/undercloud_disk_space/molecule/default/molecule.yml @@ -0,0 +1,46 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/undercloud_disk_space/molecule/default/playbook.yml b/roles/undercloud_disk_space/molecule/default/playbook.yml new file mode 100644 index 0000000..31826ae --- /dev/null +++ b/roles/undercloud_disk_space/molecule/default/playbook.yml @@ -0,0 +1,44 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + vars: + volumes: + - {mount: /var, min_size: 20} + - {mount: /, min_size: 150} + + tasks: + - block: + - include_role: + name: undercloud_disk_space + rescue: + - name: Clear host errors + meta: clear_host_errors + + - debug: + msg: The validation works! End the playbook run + + - name: End play + meta: end_play + + - name: Fail the test + fail: + msg: | + The validation did not detect a too small disk space diff --git a/roles/undercloud_disk_space/tasks/main.yml b/roles/undercloud_disk_space/tasks/main.yml new file mode 100644 index 0000000..67c989b --- /dev/null +++ b/roles/undercloud_disk_space/tasks/main.yml @@ -0,0 +1,39 @@ +--- +- name: Set a constant defining number of Bytes in 1 GB + set_fact: + const_bytes_in_gb: 1073741824 + +- name: Stat volume directories + stat: + path: "{{ item.mount }}" + with_items: "{{ volumes }}" + register: volumes_stat + +- name: Initialize existing_volumes to an empty array + set_fact: + existing_volumes="{{ [] }}" + +- name: Filter out non-existing volumes + set_fact: + existing_volumes: "{{ existing_volumes +[item.item] }}" + with_items: "{{ volumes_stat.results }}" + when: item.stat.exists + loop_control: + label: "{{ item.item.mount }}" + +- name: Loop on volumes and gather available space + shell: df -B1 {{ item.mount }} --output=avail | sed 1d + register: volume_size + with_items: "{{ existing_volumes }}" + changed_when: false + +- name: Fail if any of the volumes are too small + fail: + msg: > + Minimum free space required for {{ item.item.mount }}: {{ item.item.min_size }}G + - current free space: {{ (item.stdout|int / const_bytes_in_gb|int) |round(1) }}G + when: > + item.stdout|int / const_bytes_in_gb|int < item.item.min_size|int + with_items: "{{ volume_size.results }}" + loop_control: + label: "{{ item.item.mount }}" diff --git a/roles/undercloud_disk_space/vars/main.yaml b/roles/undercloud_disk_space/vars/main.yaml new file mode 100644 index 0000000..69b5444 --- /dev/null +++ b/roles/undercloud_disk_space/vars/main.yaml @@ -0,0 +1,11 @@ +--- +metadata: + name: Verify undercloud fits the disk space requirements + description: > + Make sure that the root partition on the undercloud node has enough + free space. + + http://tripleo.org/install/environments/baremetal.html#minimum-system-requirements + groups: + - prep + - pre-introspection diff --git a/roles/undercloud_ram/README.md b/roles/undercloud_ram/README.md new file mode 100644 index 0000000..53224d1 --- /dev/null +++ b/roles/undercloud_ram/README.md @@ -0,0 +1,36 @@ +Undercloud-ram +============== + +An Ansible role to check if the Undercloud fits the RAM requirements + +Requirements +------------ + +This role could be used before or/and after the Undercloud installation + +Role Variables +-------------- + +- min_undercloud_ram_gb: <24> -- Minimal amount of RAM in GB + +Dependencies +------------ + +No dependencies. + +Example Playbook +---------------- + + - hosts: undercloud + roles: + - { role: undercloud-ram, min_undercloud_ram_gb: 24 } + +License +------- + +Apache + +Author Information +------------------ + +Red Hat TripleO Validations Team diff --git a/roles/undercloud_ram/defaults/main.yml b/roles/undercloud_ram/defaults/main.yml new file mode 100644 index 0000000..c9dbb34 --- /dev/null +++ b/roles/undercloud_ram/defaults/main.yml @@ -0,0 +1,3 @@ +--- + +min_undercloud_ram_gb: 24 diff --git a/roles/undercloud_ram/molecule/default/Dockerfile.j2 b/roles/undercloud_ram/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/undercloud_ram/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/undercloud_ram/molecule/default/molecule.yml b/roles/undercloud_ram/molecule/default/molecule.yml new file mode 100644 index 0000000..5eb59c2 --- /dev/null +++ b/roles/undercloud_ram/molecule/default/molecule.yml @@ -0,0 +1,46 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/undercloud_ram/molecule/default/playbook.yml b/roles/undercloud_ram/molecule/default/playbook.yml new file mode 100644 index 0000000..22e52b9 --- /dev/null +++ b/roles/undercloud_ram/molecule/default/playbook.yml @@ -0,0 +1,42 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + + vars: + min_undercloud_ram_gb: 1000000 + + tasks: + - block: + - include_role: + name: undercloud_ram + rescue: + - name: Clear host errors + meta: clear_host_errors + + - debug: + msg: The validation works! End the playbook run + + - name: End play + meta: end_play + + - name: Fail the test + fail: + msg: | + The undercloud_ram role should have detected that there is not + enough RAM diff --git a/roles/undercloud_ram/tasks/main.yml b/roles/undercloud_ram/tasks/main.yml new file mode 100644 index 0000000..bd2c4e9 --- /dev/null +++ b/roles/undercloud_ram/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: Verify the RAM requirements + fail: + msg: >- + The RAM on the undercloud node is {{ ansible_memtotal_mb }} MB, + the minimal recommended value is + {{ min_undercloud_ram_gb|int * 1024 }} MB. + # NOTE(shadower): converting GB to MB + failed_when: "(ansible_memtotal_mb) < min_undercloud_ram_gb|int * 1024" diff --git a/roles/undercloud_ram/vars/main.yaml b/roles/undercloud_ram/vars/main.yaml new file mode 100644 index 0000000..3bde5ed --- /dev/null +++ b/roles/undercloud_ram/vars/main.yaml @@ -0,0 +1,11 @@ +--- +metadata: + name: Verify the undercloud fits the RAM requirements + description: > + Verify that the undercloud has enough RAM. + + https://access.redhat.com/documentation/en-us/red_hat_openstack_platform/14/html/director_installation_and_usage/planning-your-undercloud#determining-environment-scale + groups: + - prep + - pre-introspection + - pre-upgrade diff --git a/roles/undercloud_selinux_mode/README.md b/roles/undercloud_selinux_mode/README.md new file mode 100644 index 0000000..eee51ef --- /dev/null +++ b/roles/undercloud_selinux_mode/README.md @@ -0,0 +1,37 @@ +Undercloud-selinux-mode +======================= + +An Ansible role to check the Undercloud SELinux Enforcing mode + + +Requirements +------------ + +This role could be used before or/and after the Undercloud installation + +Role Variables +-------------- + +None + +Dependencies +------------ + +No dependencies. + +Example Playbook +---------------- + + - hosts: undercloud + roles: + - { role: undercloud-selinux-mode } + +License +------- + +Apache + +Author Information +------------------ + +Red Hat TripleO Validations Team diff --git a/roles/undercloud_selinux_mode/molecule/default/Dockerfile.j2 b/roles/undercloud_selinux_mode/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/undercloud_selinux_mode/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/undercloud_selinux_mode/molecule/default/molecule.yml b/roles/undercloud_selinux_mode/molecule/default/molecule.yml new file mode 100644 index 0000000..f87e178 --- /dev/null +++ b/roles/undercloud_selinux_mode/molecule/default/molecule.yml @@ -0,0 +1,47 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + pkg_extras: python-setuptools python-enum34 python-netaddr ruby epel-release PyYAML + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + pkg_extras: python*-setuptools python*-enum python*-netaddr ruby PyYAML + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/undercloud_selinux_mode/molecule/default/playbook.yml b/roles/undercloud_selinux_mode/molecule/default/playbook.yml new file mode 100644 index 0000000..6c61a39 --- /dev/null +++ b/roles/undercloud_selinux_mode/molecule/default/playbook.yml @@ -0,0 +1,26 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + + tasks: + - name: Warn developers about the lack of molecule testing + fail: + msg: >- + This role needs molecule tests! diff --git a/roles/undercloud_selinux_mode/tasks/main.yml b/roles/undercloud_selinux_mode/tasks/main.yml new file mode 100644 index 0000000..1cd8733 --- /dev/null +++ b/roles/undercloud_selinux_mode/tasks/main.yml @@ -0,0 +1,24 @@ +--- +- name: Get current SELinux mode + command: getenforce + become: true + register: sestatus + changed_when: false + +- name: Fail if SELinux is not in Enforced mode (RHEL) + fail: + msg: >- + SELinux is running in {{ sestatus.stdout }} mode on the Undercloud. + Ensure that SELinux is enabled and running in Enforcing mode. + when: + - "sestatus.stdout != 'Enforcing'" + - "ansible_distribution == 'RedHat'" + +- name: Warn if SELinux is not in Enforced mode (CentOS) + warn: + msg: >- + SELinux is running in {{ sestatus.stdout }} mode on the Undercloud. + Ensure that SELinux is enabled and running in Enforcing mode. + when: + - "sestatus.stdout != 'Enforcing'" + - "ansible_distribution == 'CentOS'" diff --git a/roles/undercloud_selinux_mode/vars/main.yml b/roles/undercloud_selinux_mode/vars/main.yml new file mode 100644 index 0000000..60e9515 --- /dev/null +++ b/roles/undercloud_selinux_mode/vars/main.yml @@ -0,0 +1,8 @@ +--- +metadata: + name: Undercloud SELinux Enforcing Mode Check + description: > + Check if the Undercloud is running SELinux in Enforcing mode. + groups: + - prep + - pre-introspection diff --git a/roles/undercloud_service_status/README.md b/roles/undercloud_service_status/README.md new file mode 100644 index 0000000..946bfa0 --- /dev/null +++ b/roles/undercloud_service_status/README.md @@ -0,0 +1,38 @@ +Undercloud-service-status +========================= + +An Ansible role to verify the Undercloud services states before running an +Update or Upgrade. + +Requirements +------------ + +This role needs to be run against an installed Undercloud. + +Role Variables +-------------- + +- undercloud_service_list: A list of services actually coming from the tripleo-ansible-inventory + +Dependencies +------------ + +No dependencies. + +Example Playbook +---------------- + + + - hosts: undercloud + roles: + - { role: undercloud-service-status } + +License +------- + +Apache + +Author Information +------------------ + +Red Hat TripleO Validations Team. diff --git a/roles/undercloud_service_status/defaults/main.yml b/roles/undercloud_service_status/defaults/main.yml new file mode 100644 index 0000000..e9e29ba --- /dev/null +++ b/roles/undercloud_service_status/defaults/main.yml @@ -0,0 +1,8 @@ +--- +undercloud_service_list: + - tripleo_nova_compute + - tripleo_heat_engine + - tripleo_ironic_conductor + - tripleo_swift_container_server + - tripleo_swift_object_server + - tripleo_mistral_engine diff --git a/roles/undercloud_service_status/tasks/main.yml b/roles/undercloud_service_status/tasks/main.yml new file mode 100644 index 0000000..8291e81 --- /dev/null +++ b/roles/undercloud_service_status/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: Check Services are running + command: "/usr/bin/systemctl show {{ item }} --property ActiveState" + become: true + with_items: "{{ undercloud_service_list }}" + register: "check_services" + changed_when: false + ignore_errors: true + +- name: Fail if services were not running + fail: + msg: >- + One of the undercloud services was not active. + Please check {{ item.item }} first and then confirm the status of + undercloud services in general before attempting to update or + upgrade the environment. + failed_when: "item.stdout != 'ActiveState=active'" + with_items: "{{ check_services.results }}" diff --git a/roles/undercloud_service_status/vars/main.yaml b/roles/undercloud_service_status/vars/main.yaml new file mode 100644 index 0000000..f934d5c --- /dev/null +++ b/roles/undercloud_service_status/vars/main.yaml @@ -0,0 +1,8 @@ +--- +metadata: + name: Verify undercloud services state before running update or upgrade + description: > + Check undercloud status before running a stack update - especially minor update and major upgrade. + groups: + - post-upgrade + - pre-upgrade diff --git a/roles/validate_selinux/defaults/main.yml b/roles/validate_selinux/defaults/main.yml new file mode 100644 index 0000000..c7f0a32 --- /dev/null +++ b/roles/validate_selinux/defaults/main.yml @@ -0,0 +1,27 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +# All variables intended for modification should place placed in this file. + +# All variables within this role should have a prefix of "validate_selinux" +validate_selinux_working_dir: /var/log/validations +validate_selinux_audit_source: /var/log/audit/audit.log +validate_selinux_skip_list_dest: "{{ validate_selinux_working_dir }}/denials-skip-list.txt" +validate_selinux_filtered_denials_dest: "{{ validate_selinux_working_dir }}/denials-filtered.log" +validate_selinux_strict: false +validate_selinux_filter: "None" +validate_selinux_skip_list: {} diff --git a/roles/validate_selinux/handlers/main.yml b/roles/validate_selinux/handlers/main.yml new file mode 100644 index 0000000..dfd4c73 --- /dev/null +++ b/roles/validate_selinux/handlers/main.yml @@ -0,0 +1,15 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. diff --git a/roles/validate_selinux/molecule/default/Dockerfile b/roles/validate_selinux/molecule/default/Dockerfile new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/validate_selinux/molecule/default/Dockerfile @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/validate_selinux/molecule/default/molecule.yml b/roles/validate_selinux/molecule/default/molecule.yml new file mode 100644 index 0000000..1dbdf89 --- /dev/null +++ b/roles/validate_selinux/molecule/default/molecule.yml @@ -0,0 +1,49 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + dockerfile: Dockerfile + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + dockerfile: Dockerfile + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + ANSIBLE_LIBRARY: "../../../../library" + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/validate_selinux/molecule/default/playbook.yml b/roles/validate_selinux/molecule/default/playbook.yml new file mode 100644 index 0000000..00c31d7 --- /dev/null +++ b/roles/validate_selinux/molecule/default/playbook.yml @@ -0,0 +1,63 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + gather_facts: false + vars: + validate_selinux_working_dir: '/tmp' + + tasks: + - name: Simple run without filter against clean auditlog + include_role: + name: validate_selinux + vars: + validate_selinux_audit_source: '/var/log/audit-clean.log' + + - name: Run with filter against unclean auditlog + include_role: + name: validate_selinux + vars: + validate_selinux_audit_source: '/var/log/audit-unclean.log' + validate_selinux_skip_list: + - entry: 'tcontext=system_u:system_r:init_t' + comment: 'This one is a real-life entry' + - entry: 'tcontext=system_u:system_r:system_dbusd_t' + comment: 'This one is another real-life entry' + + - name: Run without filter against unclean auditlog + block: + - name: Run role + include_role: + name: validate_selinux + vars: + validate_selinux_audit_source: '/var/log/audit-unclean.log' + validate_selinux_strict: true + rescue: + - name: Clear host error + meta: clear_host_errors + + - name: Status message + debug: + msg: 'Successfully detected denials issue!' + + - name: End play + meta: end_play + + - name: Fail if we get to this place + fail: + msg: 'Unit test failed: did not detect untracked denials!' diff --git a/roles/validate_selinux/molecule/default/prepare.yml b/roles/validate_selinux/molecule/default/prepare.yml new file mode 100644 index 0000000..6d8f478 --- /dev/null +++ b/roles/validate_selinux/molecule/default/prepare.yml @@ -0,0 +1,60 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + gather_facts: false + + tasks: + - name: Populate fake clean auditlog + copy: + dest: /var/log/audit-clean.log + owner: root + mode: 0600 + group: root + # yamllint disable rule:line-length + content: | + type=SERVICE_START msg=audit(1575877870.934:286): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=sssd-kcm comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + type=SERVICE_STOP msg=audit(1575878320.981:287): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=sssd-kcm comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + type=USER_ACCT msg=audit(1575878471.739:288): pid=4430 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + type=USER_CMD msg=audit(1575878471.740:289): pid=4430 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 msg='cwd="/root" cmd=626F7267202D2D696E666F20637265617465202D2D636F6D7072657373696F6E206C7A34202D2D6578636C7564652D636163686573202D2D6578636C756465202A2F2A2E6C6F636B202D2D6578636C756465202A2F2E746F78202D2D6578636C756465202A2F2E737465737472202D2D6578636C756465202A2F727562792D76656E646F72202D2D6578636C756465202A2F7A75756C2F202D2D6578636C756465202A2F73736866732F202D2D6578636C756465202A2F2E6C6F63616C2F7368617265202F6D656469612F6261636B7570732F7268656C3A3A31306130393963382D316135612D313165612D613663622D386331363435366466626265202F686F6D652F636A65616E6E6572 exe="/usr/bin/sudo" terminal=? res=success'UID="root" AUID="unset" + type=USER_ACCT msg=audit(1575878554.296:294): pid=4445 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + type=USER_CMD msg=audit(1575878554.296:295): pid=4445 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 msg='cwd="/root" cmd=626F7267206C697374202F6D656469612F6261636B7570732F7268656C exe="/usr/bin/sudo" terminal=? res=success'UID="root" AUID="unset" + type=USER_ACCT msg=audit(1575878555.032:300): pid=4449 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + type=USER_CMD msg=audit(1575878555.032:301): pid=4449 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 msg='cwd="/root" cmd=626F7267207072756E65202D70202D2D6B6565702D77697468696E203277202D2D7374617473202F6D656469612F6261636B7570732F7268656C exe="/usr/bin/sudo" terminal=? res=success'UID="root" AUID="unset" + type=SERVICE_START msg=audit(1575878869.915:306): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=fprintd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + type=SERVICE_STOP msg=audit(1575878900.615:312): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=fprintd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" + + # yamllint enable rule:line-length + - name: Populate unclean auditlog + copy: + dest: /var/log/audit-unclean.log + owner: root + mode: 0600 + group: root + # yamllint disable rule:line-length + content: | + type=AVC msg=audit(1575534183.234:4933): avc: denied { write } for pid=11266 comm="iptables" path="pipe:[231496]" dev="pipefs" ino=231496 scontext=system_u:system_r:iptables_t:s0 tcontext=system_u:system_r:certmonger_t:s0 tclass=fifo_file permissive=1 + type=AVC msg=audit(1575534183.342:4934): avc: denied { write } for pid=11284 comm="iptables" path="pipe:[231496]" dev="pipefs" ino=231496 scontext=system_u:system_r:iptables_t:s0 tcontext=system_u:system_r:certmonger_t:s0 tclass=fifo_file permissive=1 + type=USER_AVC msg=audit(1575535009.861:5275): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_call interface=org.freedesktop.DBus member=Hello dest=org.freedesktop.DBus spid=38869 scontext=system_u:system_r:container_t:s0:c313,c573 tcontext=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535009.861:5276): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_call interface=org.freedesktop.systemd1.Manager member=GetDynamicUsers dest=org.freedesktop.systemd1 spid=38869 tpid=1 scontext=system_u:system_r:container_t:s0:c313,c573 tcontext=system_u:system_r:init_t:s0 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535009.862:5277): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_return dest=:1.1198 spid=1 tpid=38869 scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:container_t:s0:c313,c573 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535013.340:5290): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_call interface=org.freedesktop.DBus member=Hello dest=org.freedesktop.DBus spid=39132 scontext=system_u:system_r:container_t:s0:c192,c917 tcontext=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535013.341:5291): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_call interface=org.freedesktop.systemd1.Manager member=GetDynamicUsers dest=org.freedesktop.systemd1 spid=39132 tpid=1 scontext=system_u:system_r:container_t:s0:c192,c917 tcontext=system_u:system_r:init_t:s0 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535013.342:5292): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_return dest=:1.1209 spid=1 tpid=39132 scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:container_t:s0:c192,c917 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535028.912:5307): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_call interface=org.freedesktop.DBus member=Hello dest=org.freedesktop.DBus spid=39430 scontext=system_u:system_r:container_t:s0:c776,c848 tcontext=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" + type=USER_AVC msg=audit(1575535028.913:5308): pid=1397 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: denied { send_msg } for msgtype=method_call interface=org.freedesktop.systemd1.Manager member=GetDynamicUsers dest=org.freedesktop.systemd1 spid=39430 tpid=1 scontext=system_u:system_r:container_t:s0:c776,c848 tcontext=system_u:system_r:init_t:s0 tclass=dbus permissive=1 exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" diff --git a/roles/validate_selinux/molecule/default/verify.yml b/roles/validate_selinux/molecule/default/verify.yml new file mode 100644 index 0000000..dfd4c73 --- /dev/null +++ b/roles/validate_selinux/molecule/default/verify.yml @@ -0,0 +1,15 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. diff --git a/roles/validate_selinux/tasks/main.yml b/roles/validate_selinux/tasks/main.yml new file mode 100644 index 0000000..d0ef3a0 --- /dev/null +++ b/roles/validate_selinux/tasks/main.yml @@ -0,0 +1,123 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +# "validate-selinux" tasks + +- name: "Ensure {{ validate_selinux_audit_source }} does exist" + become: true + stat: + path: "{{ validate_selinux_audit_source }}" + register: auditlog_stat + +- name: "Fail if {{ validate_selinux_audit_source }} does not exit" + when: not auditlog_stat.stat.exists + fail: + msg: "ERROR: {{ validate_selinux_audit_source }} does not exist!" + +- name: Load skip list from provided file + when: + - validate_selinux_filter != 'None' + - validate_selinux_skip_list is not defined + include_vars: "{{ validate_selinux_filter }}" + +- name: Gather subset of facts + setup: + gather_subset: "!min,distribution_major_version" + when: + - validate_selinux_filter == 'None' + - validate_selinux_skip_list is not defined + - ansible_distribution_major_version is not defined + +- name: Load skip list variables (undercloud or overcloud) + when: + - validate_selinux_skip_list is not defined + include_vars: "{{ lookup('first_found', lookhere, errors='ignore') }}" + vars: + lookhere: + - "selinux_skip_{{ release }}_on_{{ ansible_distribution_major_version }}.yml" + - "selinux_skip_{{ release }}.yml" + +- name: Fetch denials from auditlog + become: true + ignore_errors: true + changed_when: false + shell: | + set -o pipefail + grep denied {{ validate_selinux_audit_source }} > /tmp/denials.log + +- name: Get stat for denials.log + stat: + path: /tmp/denials.log + register: denials_log + +- name: Everything is fine + when: denials_log.stat.size == 0 + debug: + msg: "No untracked SELinux AVC detected, congratulations!" + +- name: Next steps only if we have denials + when: denials_log.stat.size > 0 + block: + - name: Create skip list + when: validate_selinux_skip_list != {} + template: + src: skip-list.j2 + dest: "{{ validate_selinux_skip_list_dest }}" + mode: 0644 + + - name: Filter out denials + when: validate_selinux_skip_list != {} + ignore_errors: true + changed_when: false + shell: | + set -o pipefail + grep -v -f {{ validate_selinux_skip_list_dest }} /tmp/denials.log > {{ validate_selinux_filtered_denials_dest }} + + - name: No skip_list + when: validate_selinux_skip_list == {} + copy: + remote_src: true + src: /tmp/denials.log + dest: "{{ validate_selinux_filtered_denials_dest }}" + + - name: Get stat for filtered denials + stat: + path: "{{ validate_selinux_filtered_denials_dest }}" + register: denials_stat + + - name: debug + debug: + var: denials_stat + + - name: Fail if we found untracked denials + when: + - validate_selinux_strict|bool + - denials_stat.stat.size != 0 + fail: + msg: "Untracked SELinux AVCs found, please refer to {{ validate_selinux_filtered_denials_dest }}" + + - name: Output information in case we do not fail + when: + - not validate_selinux_strict|bool + - denials_stat.stat.size != 0 + debug: + msg: "Untracked SELinux AVCs found, please refer to {{ validate_selinux_filtered_denials_dest }}" + + - name: Output information if everything is fine + when: denials_stat.stat.size == 0 + debug: + msg: "No untracked SELinux AVC detected, congratulations!" diff --git a/roles/validate_selinux/templates/skip-list.j2 b/roles/validate_selinux/templates/skip-list.j2 new file mode 100644 index 0000000..4409f3e --- /dev/null +++ b/roles/validate_selinux/templates/skip-list.j2 @@ -0,0 +1,3 @@ +{% for entry in validate_selinux_skip_list %} +{{ entry.entry }} +{% endfor %} diff --git a/roles/validate_selinux/vars/main.yml b/roles/validate_selinux/vars/main.yml new file mode 100644 index 0000000..d826fe1 --- /dev/null +++ b/roles/validate_selinux/vars/main.yml @@ -0,0 +1,22 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +# While options found within the vars/ path can be overridden using extra +# vars, items within this path are considered part of the role and not +# intended to be modified. + +# All variables within this role should have a prefix of "validate_selinux" diff --git a/roles/xfs_check_ftype/molecule/default/Dockerfile.j2 b/roles/xfs_check_ftype/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..1b91a0e --- /dev/null +++ b/roles/xfs_check_ftype/molecule/default/Dockerfile.j2 @@ -0,0 +1,37 @@ +# Molecule managed +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/roles/xfs_check_ftype/molecule/default/molecule.yml b/roles/xfs_check_ftype/molecule/default/molecule.yml new file mode 100644 index 0000000..13d65f6 --- /dev/null +++ b/roles/xfs_check_ftype/molecule/default/molecule.yml @@ -0,0 +1,48 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + privileged: true + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + + - name: fedora28 + hostname: fedora28 + image: fedora:28 + privileged: true + pkg_extras: python*-setuptools + environment: + <<: *env + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/roles/xfs_check_ftype/molecule/default/playbook.yml b/roles/xfs_check_ftype/molecule/default/playbook.yml new file mode 100644 index 0000000..4c7f853 --- /dev/null +++ b/roles/xfs_check_ftype/molecule/default/playbook.yml @@ -0,0 +1,21 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Converge + hosts: all + roles: + - role: xfs_check_ftype diff --git a/roles/xfs_check_ftype/molecule/default/prepare.yml b/roles/xfs_check_ftype/molecule/default/prepare.yml new file mode 100644 index 0000000..822f32b --- /dev/null +++ b/roles/xfs_check_ftype/molecule/default/prepare.yml @@ -0,0 +1,60 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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: Prepare + hosts: all + + vars: + xfs_image_file: "/root/xfs_{{ ansible_distribution|lower }}.img" + + post_tasks: + - name: Create a blank image + command: "dd if=/dev/zero of={{ xfs_image_file }} bs=1M count=50" + tags: + - skip_ansible_lint + + - name: Install tools for managing XFS partitions + package: + name: "{{ item }}" + state: present + loop: + - parted + - xfsprogs + + - name: Map the partition file to the loop device + command: "losetup --find --show {{ xfs_image_file }}" + register: losetup + tags: + - skip_ansible_lint + + - name: Format the partition with XFS with ftype=1 + filesystem: + fstype: xfs + dev: "{{ losetup.stdout }}" + opts: -n ftype=1 + + - name: Create a directory for the partition to mount onto + file: + path: /xfs + state: directory + + - name: Mount the XFS partition + mount: + path: /xfs + src: "{{ losetup.stdout }}p1" + fstype: xfs + state: present diff --git a/roles/xfs_check_ftype/molecule/default/verify.yml b/roles/xfs_check_ftype/molecule/default/verify.yml new file mode 100644 index 0000000..dfd4c73 --- /dev/null +++ b/roles/xfs_check_ftype/molecule/default/verify.yml @@ -0,0 +1,15 @@ +--- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. diff --git a/roles/xfs_check_ftype/tasks/main.yml b/roles/xfs_check_ftype/tasks/main.yml new file mode 100644 index 0000000..4775676 --- /dev/null +++ b/roles/xfs_check_ftype/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- name: Check if there are XFS volumes with ftype=0 + become: true + shell: | + for dev in $(df -h | grep '/dev/' | grep -v 'tmp' | cut -d' ' -f1) + do + parseftype=$(xfs_info $dev | grep ftype=0); + if [[ ! -z "$parseftype" ]]; then + ftype="ftype=0"; + break; + fi + done + echo $ftype; + register: ftype + changed_when: false + +- name: Check ftype + fail: + msg: > + XFS volumes formatted using ftype=0 are incompatible + with the docker overlayfs driver. + Run xfs_info on {{ ansible_fqdn }} and fix those volumes + before proceeding with the upgrade. + when: + - ftype.stdout == 'ftype=0' diff --git a/roles/xfs_check_ftype/vars/main.yml b/roles/xfs_check_ftype/vars/main.yml new file mode 100644 index 0000000..79d8c7f --- /dev/null +++ b/roles/xfs_check_ftype/vars/main.yml @@ -0,0 +1,8 @@ +--- +metadata: + name: XFS ftype check + description: > + Check if there is at least 1 XFS volume + with ftype=0 in any deployed node. + groups: + - pre-upgrade diff --git a/validations_common/tests/library/test_validations_read_ini.py b/validations_common/tests/library/test_validations_read_ini.py new file mode 100644 index 0000000..b699567 --- /dev/null +++ b/validations_common/tests/library/test_validations_read_ini.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- + +# 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. + +""" +test_validations_read_ini +---------------------------------- + +Tests for `validations_read_ini` module. +""" + + +import os +import tempfile + +import library.validations_read_ini as validation +from validations_common.tests import base + + +invalid_content = ''' +[DEFAULT# + hello = +''' + +valid_content = ''' +[DEFAULT] +debug=True + +[dhcp] +dhcp_start=192.168.0.1 +dhcp_end=192.168.0.254 + +[secrets] +password=1234 +''' + + +class TestValidationsReadIni(base.TestCase): + + def test_check_file_invalid_path(self): + '''Test validations_read_ini when path is invalid''' + + msg = validation.check_file('non/existing/path', False) + self.assertEqual("Could not open the ini file: 'non/existing/path'", + msg) + + def test_check_file_ignore_missing(self): + '''Test validations_read_ini when ignoring missing files''' + + msg = validation.check_file('non/existing/path', True) + self.assertEqual("Could not open the ini file: 'non/existing/path'", + msg) + + def test_check_file_valid_path(self): + '''Test validations_read_ini when path is valid''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + msg = validation.check_file(tmp_name, False) + tmpfile.close() + + self.assertEqual('', msg) + + def test_get_result_invalid_format(self): + '''Test validations_read_ini when file format is valid''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(invalid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'section', 'key') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.INVALID_FORMAT, ret) + self.assertEqual("The file '{}' is not in a valid INI format.".format( + tmp_name), msg) + self.assertIsNone(value) + + def test_get_result_key_not_found(self): + '''Test validations_read_ini when key is not found''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(valid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'section', 'key') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.KEY_NOT_FOUND, ret) + self.assertEqual(("There is no key 'key' under the section 'section' " + "in file {}.").format(tmp_name), msg) + self.assertIsNone(value) + + def test_get_result_key_not_found_with_default(self): + '''Test validations_read_ini when key is not found but has a default''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(valid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'section', 'key', + 'foo') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.OK, ret) + self.assertEqual(("There is no key 'key' under section 'section' " + "in file {}. Using default value '{}'" + ).format(tmp_name, 'foo'), msg) + self.assertEqual(value, 'foo') + + def test_get_result_ok(self): + '''Test validations_read_ini when key is not found''' + + tmpfile = self.create_tmp_ini() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(valid_content.encode('utf-8')) + tmpfile.seek(0) + ret, msg, value = validation.get_result(tmp_name, 'secrets', + 'password') + tmpfile.close() + + self.assertEqual(validation.ReturnValue.OK, ret) + self.assertEqual(("The key 'password' under the section 'secrets'" + " in file {} has the value: '1234'").format( + tmp_name), msg) + self.assertEqual('1234', value) + + def create_tmp_ini(self): + '''Create temporary tmp.ini file, return its full name''' + + path = 'validations_common/tests' + tmpfile = tempfile.NamedTemporaryFile(suffix='.ini', prefix='tmp', + dir=path) + return tmpfile