#!/usr/bin/python # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you 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 os import sys, path from utility import getHealthChecksData, formatPort def checkMaxconn(haproxyData, haCfgSections): if "maxconn" in haproxyData and "maxconn" in haCfgSections["global"]: if haproxyData["maxconn"] != haCfgSections["global"]["maxconn"][0].strip(): print("global maxconn mismatch occurred") return False return True def checkIdletimeout(haproxyData, haCfgSections): if "idletimeout" not in haproxyData: return True # Normalize idletimeout value to string for comparison idle_value = str(haproxyData["idletimeout"]).strip() # Safely get the defaults section and its timeout directives defaults_section = haCfgSections.get("defaults", {}) timeout_lines = defaults_section.get("timeout", []) # Extract client and server timeout values from the parsed "timeout" entries timeout_values = {} for tline in timeout_lines: tline = tline.strip() if not tline: continue parts = tline.split(None, 1) if len(parts) < 2: continue kind, value = parts[0].strip(), parts[1].strip() if kind in ("client", "server"): timeout_values[kind] = value # Special handling for idletimeout == 0: there should be no client/server timeouts configured if idle_value == "0": if "client" in timeout_values or "server" in timeout_values: print("defaults timeout client or timeout server should be absent when idletimeout is 0") return False return True # Non-zero idletimeout: both client and server timeouts must be present if "client" not in timeout_values or "server" not in timeout_values: print("defaults timeout client or timeout server missing") return False if idle_value != timeout_values["client"] or idle_value != timeout_values["server"]: print("defaults timeout client or timeout server mismatch occurred") return False return True def checkLoadBalance(haproxyData, haCfgSections): correct = True for lbSec in haproxyData: srcServer = lbSec["sourceIp"].replace('.', '_') + "-" + \ formatPort(lbSec["sourcePortStart"], lbSec["sourcePortEnd"]) secName = "listen " + srcServer if secName not in haCfgSections: print("Missing section for load balancing " + secName + "\n") correct = False else: cfgSection = haCfgSections[secName] if "server" in cfgSection: if lbSec["algorithm"] != cfgSection["balance"][0]: print("Incorrect balance method for " + secName + "Expected : " + lbSec["algorithm"] + " but found " + cfgSection["balance"][0] + "\n") correct = False bindStr = lbSec["sourceIp"] + ":" + formatPort(lbSec["sourcePortStart"], lbSec["sourcePortEnd"]) if cfgSection["bind"][0] != bindStr: print("Incorrect bind string found. Expected " + bindStr + " but found " + cfgSection["bind"][0] + ".") correct = False if (lbSec["sourcePortStart"] == "80" and lbSec["sourcePortEnd"] == "80" and lbSec["keepAliveEnabled"] == "false") \ or (lbSec["stickiness"].find("AppCookie") != -1 or lbSec["stickiness"].find("LbCookie") != -1): if not ("mode" in cfgSection and cfgSection["mode"][0] == "http"): print("Expected HTTP mode but not found") correct = False expectedServerIps = lbSec["vmIps"].split(" ") for expectedServerIp in expectedServerIps: pattern = expectedServerIp + ":" + \ formatPort(lbSec["destPortStart"], lbSec["destPortEnd"]) foundPattern = False for server in cfgSection["server"]: s = server.split() if s[0].strip().find(srcServer + "_") == 0 and s[1].strip() == pattern: foundPattern = True break if not foundPattern: correct = False print("Missing load balancing for " + pattern + ". ") return correct def main(): ''' Checks for max con and each load balancing rule - source ip, ports and destination ips and ports. Also checks for http mode. Does not check for stickiness policies. ''' haproxyData = getHealthChecksData("haproxyData") if haproxyData is None or len(haproxyData) == 0: print("No data provided to check, skipping") exit(0) with open("/etc/haproxy/haproxy.cfg", 'r') as haCfgFile: haCfgLines = haCfgFile.readlines() haCfgFile.close() if len(haCfgLines) == 0: print("Unable to read config file /etc/haproxy/haproxy.cfg") exit(1) haCfgSections = {} currSection = None currSectionDict = {} for line in haCfgLines: line = line.strip() if len(line) == 0: if currSection is not None and len(currSectionDict) > 0: haCfgSections[currSection] = currSectionDict currSection = None currSectionDict = {} continue if currSection is None: currSection = line else: lineSec = line.split(' ', 1) if lineSec[0] not in currSectionDict: currSectionDict[lineSec[0]] = [] currSectionDict[lineSec[0]].append(lineSec[1] if len(lineSec) > 1 else '') checkMaxConn = checkMaxconn(haproxyData[0], haCfgSections) checkIdleTimeout = checkIdletimeout(haproxyData[0], haCfgSections) checkLbRules = checkLoadBalance(haproxyData, haCfgSections) if checkMaxConn and checkIdleTimeout and checkLbRules: print("All checks pass") exit(0) else: exit(1) if __name__ == "__main__": if len(sys.argv) == 2 and sys.argv[1] == "advanced": main()