Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Commit

Permalink
Migrate back haproxy validation to tripleo-validations
Browse files Browse the repository at this point in the history
This patch moves back the haproxy validation from validations-common to
tripleo-validations. It has been renamed in tripleo-haproxy and the role
is called now tripleo_haproxy.

This patch also fix the host targeting in order to only execute this
validation on nodes where haproxy is really installed and configured.

Note that the haproxy validations hosted in validations-common will be
removed in a follow up patch.

Closes-Bug: #1926024

Co-Authored-By: Michele Baldessari <[email protected]>

Signed-off-by: Gael Chamoulaud (Strider) <[email protected]>
Change-Id: I9869afd60342fdb0aca69a313c1c6e35e0947fb8
(cherry picked from commit 9dd662a)
(cherry picked from commit 253a16d)
  • Loading branch information
strider committed Jul 2, 2021
1 parent 727625f commit bd0d497
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 0 deletions.
14 changes: 14 additions & 0 deletions doc/source/modules/modules-tripleo_haproxy_conf.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
=============================
Module - tripleo_haproxy_conf
=============================


This module provides for the following ansible plugin:

* tripleo_haproxy_conf


.. ansibleautoplugin::
:module: library/tripleo_haproxy_conf.py
:documentation: true
:examples: true
46 changes: 46 additions & 0 deletions doc/source/roles/role-tripleo_haproxy.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
===============
tripleo_haproxy
===============

--------------
About The Role
--------------

An Ansible role to check if the ``HAProxy`` configuration has recommended
values.

Requirements
============

This role requires and Up and Running Overcloud.

Dependencies
============

None.

Example Playbook
================

.. code-block:: yaml
- hosts: undercloud
roles:
- { role: tripleo_haproxy }
License
=======

Apache

Author Information
==================

**Red Hat Tripleo DFG:PIDONE**

----------------
Full Description
----------------

.. ansibleautoplugin::
:role: roles/tripleo_haproxy
108 changes: 108 additions & 0 deletions library/tripleo_haproxy_conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# -*- 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: tripleo_haproxy_conf
short_description: Gather the HAProxy config
description:
- Gather the HAProxy config
- Owned by DFG:PIDONE
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
tripleo_haproxy_conf: path=/etc/haproxy/haproxy.cfg
'''


def generic_ini_style_conf_parser(file_path, section_regex, option_regex):
"""
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, as it can ignore some
valid options, but it is good enough for our use case.
:return: parsed haproxy configuration
:rtype: dict
"""
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):
"""
Provides section and option regex to the parser.
Essentially a wrapper for generic_ini_style_conf_parser.
Provides no extra functionality but simplifies the call, somewhat.
:return: parsed haproxy configuration
:rtype: dict
..note::
Both regular expressions bellow are used for parsing haproxy.cfg,
which has a rather vague syntax. The regexes are supposed to match all
possibilities described here, and some more:
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/load_balancer_administration/ch-haproxy-setup-vsa
"""
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()
26 changes: 26 additions & 0 deletions playbooks/tripleo-haproxy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
- hosts: haproxy
vars:
metadata:
name: TripleO HAProxy configuration
description: Verify the HAProxy configuration has recommended values.
groups:
- post-deployment
categories:
- os
- system
- ha
- loadbalancing
- proxy
products:
- tripleo
- 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'
roles:
- tripleo_haproxy
21 changes: 21 additions & 0 deletions roles/tripleo_haproxy/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
# Path to the haproxy.cfg file
haproxy_config_file: '/var/lib/config-data/puppet-generated/haproxy/etc/haproxy/haproxy.cfg'

# Global mininum per-process number of concurrent connections
global_maxconn_min: 20480

# Defaults mininum per-process number of concurrent connections
defaults_maxconn_min: 4096

# Time to wait in the queue for a connection slot to be free
defaults_timeout_queue: '2m'

# Inactivity time on the client side
defaults_timeout_client: '2m'

# Inactivity time on the server side
defaults_timeout_server: '2m'

# Additional check timeout
defaults_timeout_check: '10s'
71 changes: 71 additions & 0 deletions roles/tripleo_haproxy/molecule/default/converge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
# Copyright 2021 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 2m
timeout check 10s
- block:
- include_role:
name: tripleo_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!
3 changes: 3 additions & 0 deletions roles/tripleo_haproxy/molecule/default/molecule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
# inherits tripleo-validations/.config/molecule/config.yml
# To override default values, please take a look at the config.yml.
56 changes: 56 additions & 0 deletions roles/tripleo_haproxy/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
- name: Gather the HAProxy configuration
become: true
tripleo_haproxy_conf:
path: "{{ haproxy_config_file }}"

- name: Check the HAProxy configuration
fail:
msg: >-
{% if haproxy_conf.global.maxconn|int < global_maxconn_min %}
- [FAILED] 'global maxconn' value check
* Current value: {{ haproxy_conf.global.maxconn }}, Recommended value: > {{ global_maxconn_min }}
{% else %}
- [PASSED] 'global maxconn' value check
{% endif %}
{% if haproxy_conf.defaults.maxconn|int < defaults_maxconn_min %}
- [FAILED] 'defaults maxconn' value check
* Current value: {{ haproxy_conf.defaults.maxconn }}, Recommended Value: > {{ defaults_maxconn_min }}
{% else %}
- [PASSED] 'defaults maxconn' value check
{% endif %}
{% if haproxy_conf.defaults['timeout queue'] != defaults_timeout_queue %}
- [FAILED] 'timeout queue' option in 'defaults' check
* Current value: {{ haproxy_conf.defaults['timeout queue'] }}
* Recommended value: {{ defaults_timeout_queue }}
{% else %}
- [PASSED] 'timeout queue' option in 'defaults' check
{% endif %}
{% if haproxy_conf.defaults['timeout client'] != defaults_timeout_client %}
- [FAILED] 'timeout client' option in 'defaults' check
* Current value: {{ haproxy_conf.defaults['timeout client'] }}
* Recommended value: {{ defaults_timeout_client }}
{% else %}
- [PASSED] 'timeout client' option in 'defaults' check
{% endif %}
{% if haproxy_conf.defaults['timeout server'] != defaults_timeout_server %}
- [FAILED] 'timeout server' option in 'defaults' check
* Current value: {{ haproxy_conf.defaults['timeout server'] }}
* Recommended value: {{ defaults_timeout_server }}
{% else %}
- [PASSED] 'timeout server' option in 'defaults' check
{% endif %}
{% if haproxy_conf.defaults['timeout check'] != defaults_timeout_check %}
- [FAILED] 'timeout check' option in 'defaults' check
* Current value: {{ haproxy_conf.defaults['timeout check'] }}
* Recommended value: {{ defaults_timeout_check }}
{% else %}
- [PASSED] 'timeout check' option in 'defaults' check
{% endif %}
failed_when: >
(haproxy_conf.global.maxconn|int < global_maxconn_min) or
(haproxy_conf.defaults.maxconn|int < defaults_maxconn_min) or
(haproxy_conf.defaults['timeout queue'] != defaults_timeout_queue) or
(haproxy_conf.defaults['timeout client'] != defaults_timeout_client) or
(haproxy_conf.defaults['timeout server'] != defaults_timeout_server) or
(haproxy_conf.defaults['timeout check'] != defaults_timeout_check)
Loading

0 comments on commit bd0d497

Please sign in to comment.