From c7c6a092ff24b9a988299545d791e8e020d84861 Mon Sep 17 00:00:00 2001 From: thompsa Date: Thu, 4 Mar 2010 15:58:00 +1300 Subject: Merge the conflicting frontend bind addresses into one, allowing ACL rules to choose the backend. Non-conflicting entries should not be affected. --- config/haproxy-dev/haproxy.inc | 391 ++++++++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 161 deletions(-) diff --git a/config/haproxy-dev/haproxy.inc b/config/haproxy-dev/haproxy.inc index a799cd48..275ea8ab 100644 --- a/config/haproxy-dev/haproxy.inc +++ b/config/haproxy-dev/haproxy.inc @@ -58,44 +58,6 @@ $a_acltypes[] = array('name' => 'path_contains', 'descr' => 'Path contains', $a_acltypes[] = array('name' => 'source_ip', 'descr' => 'Source IP', 'mode' => '', 'syntax' => 'src'); -function haproxy_find_acl($name) { - global $a_acltypes; - - /* XXX why is this broken from xmlsync? */ - if (!$a_acltypes) { - $a_acltypes = array(); - $a_acltypes[] = array('name' => 'host_starts_with', 'descr' => 'Host starts with', - 'mode' => 'http', 'syntax' => 'hdr_beg(host) -i'); - $a_acltypes[] = array('name' => 'host_ends_with', 'descr' => 'Host ends with', - 'mode' =>'http', 'syntax' => 'hdr_end(host) -i'); - $a_acltypes[] = array('name' => 'host_matches', 'descr' => 'Host matches', - 'mode' =>'http', 'syntax' => 'hdr(host) -i'); - $a_acltypes[] = array('name' => 'host_regex', 'descr' => 'Host regex', - 'mode' =>'http', 'syntax' => 'hdr_reg(host) -i'); - $a_acltypes[] = array('name' => 'host_contains', 'descr' => 'Host contains', - 'mode' => 'http', 'syntax' => 'hdr_dir(host) -i'); - $a_acltypes[] = array('name' => 'path_starts_with', 'descr' => 'Path starts with', - 'mode' => 'http', 'syntax' => 'path_beg -i'); - $a_acltypes[] = array('name' => 'path_ends_with', 'descr' => 'Path ends with', - 'mode' => 'http', 'syntax' => 'path_end -i'); - $a_acltypes[] = array('name' => 'path_matches', 'descr' => 'Path matches', - 'mode' => 'http', 'syntax' => 'path -i'); - $a_acltypes[] = array('name' => 'path_regex', 'descr' => 'Path regex', - 'mode' => 'http', 'syntax' => 'path_reg -i'); - $a_acltypes[] = array('name' => 'path_contains', 'descr' => 'Path contains', - 'mode' => 'http', 'syntax' => 'path_dir -i'); - $a_acltypes[] = array('name' => 'source_ip', 'descr' => 'Source IP', - 'mode' => '', 'syntax' => 'src'); - } - - if($a_acltypes) { - foreach ($a_acltypes as $acl) { - if ($acl['name'] == $name) - return $acl; - } - } -} - function haproxy_custom_php_deinstall_command() { exec("rm /usr/local/sbin/haproxy"); exec("rm /usr/local/pkg/haproxy.inc"); @@ -158,6 +120,131 @@ EOD; exec("/usr/local/etc/rc.d/haproxy.sh start"); } +function haproxy_find_acl($name) { + global $a_acltypes; + + /* XXX why is this broken from xmlsync? */ + if (!$a_acltypes) { + $a_acltypes = array(); + $a_acltypes[] = array('name' => 'host_starts_with', 'descr' => 'Host starts with', + 'mode' => 'http', 'syntax' => 'hdr_beg(host) -i'); + $a_acltypes[] = array('name' => 'host_ends_with', 'descr' => 'Host ends with', + 'mode' =>'http', 'syntax' => 'hdr_end(host) -i'); + $a_acltypes[] = array('name' => 'host_matches', 'descr' => 'Host matches', + 'mode' =>'http', 'syntax' => 'hdr(host) -i'); + $a_acltypes[] = array('name' => 'host_regex', 'descr' => 'Host regex', + 'mode' =>'http', 'syntax' => 'hdr_reg(host) -i'); + $a_acltypes[] = array('name' => 'host_contains', 'descr' => 'Host contains', + 'mode' => 'http', 'syntax' => 'hdr_dir(host) -i'); + $a_acltypes[] = array('name' => 'path_starts_with', 'descr' => 'Path starts with', + 'mode' => 'http', 'syntax' => 'path_beg -i'); + $a_acltypes[] = array('name' => 'path_ends_with', 'descr' => 'Path ends with', + 'mode' => 'http', 'syntax' => 'path_end -i'); + $a_acltypes[] = array('name' => 'path_matches', 'descr' => 'Path matches', + 'mode' => 'http', 'syntax' => 'path -i'); + $a_acltypes[] = array('name' => 'path_regex', 'descr' => 'Path regex', + 'mode' => 'http', 'syntax' => 'path_reg -i'); + $a_acltypes[] = array('name' => 'path_contains', 'descr' => 'Path contains', + 'mode' => 'http', 'syntax' => 'path_dir -i'); + $a_acltypes[] = array('name' => 'source_ip', 'descr' => 'Source IP', + 'mode' => '', 'syntax' => 'src'); + } + + if($a_acltypes) { + foreach ($a_acltypes as $acl) { + if ($acl['name'] == $name) + return $acl; + } + } +} + +function write_backend($fd, $name, $pool, $frontend) { + if($pool['status'] != 'active' || !is_array($pool['ha_servers']['item'])) + continue; + + fwrite ($fd, "backend " . $name . "\n"); + if($pool['cookie_name'] && strtolower($frontend['type']) == "http") + fwrite ($fd, "\tcookie\t\t\t" . $pool['cookie_name'] . " insert indirect\n"); + + // https is an alias for tcp for clarity purpouses + if(strtolower($frontend['type']) == "https") { + $backend_type = "tcp"; + $httpchk = "ssl-hello-chk"; + } else { + $backend_type = $frontend['type']; + $httpchk = "httpchk"; + } + + fwrite ($fd, "\tmode\t\t\t" . $backend_type . "\n"); + + if($frontend['balance']) + fwrite ($fd, "\tbalance\t\t\t" . $frontend['balance'] . "\n"); + + if($frontend['connection_timeout']) + fwrite ($fd, "\tcontimeout\t\t" . $frontend['connection_timeout'] . "\n"); + + if($frontend['server_timeout']) + fwrite ($fd, "\tsrvtimeout\t\t" . $frontend['server_timeout'] . "\n"); + + if($frontend['retries']) + fwrite ($fd, "\tretries\t\t\t" . $frontend['retries'] . "\n"); + + if($frontend['stats_enabled']=='yes') { + fwrite ($fd, "\tstats\t\t\tenable\n"); + if($frontend['stats_uri']) + fwrite ($fd, "\tstats\t\t\turi ".$frontend['stats_uri']."\n"); + if($frontend['stats_realm']) + fwrite ($fd, "\tstats\t\t\trealm " . $frontend['stats_realm'] . "\n"); + else + fwrite ($fd, "\tstats\t\t\trealm .\n"); + fwrite ($fd, "\tstats\t\t\tauth " . $frontend['stats_username'].":". $frontend['stats_password']."\n"); + } + + $uri = $pool['monitor_uri']; + if ($pool['monitor_uri']) + $uri = $pool['monitor_uri']; + else + $uri = "/"; + fwrite ($fd, "\toption\t\t\t{$httpchk} HEAD " . $uri . " HTTP/1.0\n"); + + if($pool['cookie'] && strtolower($frontend['type']) == "http") + $cookie = " cookie {$pool['cookie']} "; + else + $cookie = ""; + if($pool['advanced']) { + $advanced = base64_decode($pool['advanced']); + $advanced_txt = " " . $advanced; + } else { + $advanced_txt = ""; + } + if($pool['checkinter']) + $checkinter = "check inter {$pool['checkinter']}"; + else + $checkinter = "check inter 1000"; + + $a_servers = &$pool['ha_servers']['item']; + foreach($a_servers as $be) { + if(!$be['port']) { + // the listener can specify a default port + $be['port'] = $frontend['svrport']; + } + if(!$be['port']) { + // last resort, use the frontend port + $ports = split(",", "{$frontend['port']},"); + $be['port'] = $ports[0]; + } + if (!$be['name']) + $be['name'] = $be['address']; + if($be['backup']) { + $isbackup = "backup"; + } else { + $isbackup = ""; + } + fwrite ($fd, "\tserver\t\t\t" . $be['name'] . " " . $be['address'].":" . $be['port'] . " $cookie " . " $checkinter $isbackup weight " . $be['weight'] . "{$advanced_txt}\n"); + } + fwrite ($fd, "\n"); +} + function haproxy_configure() { global $config, $g; @@ -187,17 +274,52 @@ function haproxy_configure() { fwrite ($fd, "\n"); } - // Construct and write out configuration file + // Try and get a unique array for address:port as frontends can duplicate + $a_bind = array(); if(is_array($a_backends)) { foreach ($a_backends as $backend) { - if($backend['status'] != 'active') continue; - // Define our backend name - $backendinfo = "listen {$backend['name']}\n"; + if(!$backend['pool']) + continue; + + $bname = $backend['extaddr'] . ":" . $backend['port']; + if (!is_array($a_bind[$bname])) { + $a_bind[$bname] = array(); + $a_bind[$bname]['config'] = array(); + // Settings which are constant for a merged frontend + $a_bind[$bname]['name'] = $backend['name']; + $a_bind[$bname]['defaultpool'] = $backend['pool'] . "_" . strtolower($backend['type']); + $a_bind[$bname]['extaddr'] = $backend['extaddr']; + $a_bind[$bname]['port'] = $backend['port']; + } + $bind = &$a_bind[$bname]; + + // Overwrite ? + $bind['type'] = $backend['type']; + $bind['forwardfor'] = $backend['forwardfor']; + $bind['httpclose'] = $backend['httpclose']; + $bind['max_connections'] = $backend['max_connections']; + $bind['client_timeout'] = $backend['client_timeout']; + $bind['advanced'] = $backend['advanced']; + + // pointer to each backend + $bind['config'][] = $backend; + } + } + + $a_pendingpl = array(); + + // Construct and write out configuration file + if(is_array($a_bind)) { + foreach ($a_bind as $bind) { + if (count($bind['config']) > 1) + $frontendinfo = "frontend {$bind['name']}-merged\n"; + else + $frontendinfo = "frontend {$bind['name']}\n"; // Prepare ports for processing by splitting - $portss = "{$backend['port']},"; + $portss = "{$bind['port']},"; $ports = split(",", $portss); // Initialize variable @@ -206,30 +328,30 @@ function haproxy_configure() { // Process and add bind directives for ports foreach($ports as $port) { if($port) { - if($backend['extaddr'] == "any") + if($bind['extaddr'] == "any") $listenip .= "\tbind\t\t\t0.0.0.0:{$port}\n"; - elseif($backend['extaddr']) - $listenip .= "\tbind\t\t\t{$backend['extaddr']}:{$port}\n"; + elseif($bind['extaddr']) + $listenip .= "\tbind\t\t\t{$bind['extaddr']}:{$port}\n"; else $listenip .= "\tbind\t\t\t" . get_current_wan_address('wan') . ":{$port}\n"; } } - fwrite ($fd, "{$backendinfo}"); + fwrite ($fd, "{$frontendinfo}"); fwrite ($fd, "{$listenip}"); // Advanced pass thru - if($backend['advanced']) { - $advanced = base64_decode($backend['advanced']); + if($bind['advanced']) { + $advanced = base64_decode($bind['advanced']); fwrite($fd, "\t" . $advanced . "\n"); } // https is an alias for tcp for clarity purpouses - if(strtolower($backend['type']) == "https") { + if(strtolower($bind['type']) == "https") { $backend_type = "tcp"; $httpchk = "ssl-hello-chk"; } else { - $backend_type = $backend['type']; + $backend_type = $bind['type']; $httpchk = "httpchk"; } @@ -237,134 +359,81 @@ function haproxy_configure() { fwrite ($fd, "\tlog\t\t\tglobal\n"); fwrite ($fd, "\toption\t\t\tdontlognull\n"); - if($backend['httpclose']) + if($bind['httpclose']) fwrite ($fd, "\toption\t\t\thttpclose\n"); - if($backend['forwardfor']) + if($bind['forwardfor']) fwrite ($fd, "\toption\t\t\tforwardfor\n"); - if($backend['max_connections']) - fwrite ($fd, "\tmaxconn\t\t\t" . $backend['max_connections'] . "\n"); - - if($backend['client_timeout']) - fwrite ($fd, "\tclitimeout\t\t" . $backend['client_timeout'] . "\n"); - - if($backend['balance']) - fwrite ($fd, "\tbalance\t\t\t" . $backend['balance'] . "\n"); - - if($backend['connection_timeout']) - fwrite ($fd, "\tcontimeout\t\t" . $backend['connection_timeout'] . "\n"); - - if($backend['server_timeout']) - fwrite ($fd, "\tsrvtimeout\t\t" . $backend['server_timeout'] . "\n"); + if($bind['max_connections']) + fwrite ($fd, "\tmaxconn\t\t\t" . $bind['max_connections'] . "\n"); - if($backend['retries']) - fwrite ($fd, "\tretries\t\t\t" . $backend['retries'] . "\n"); + if($bind['client_timeout']) + fwrite ($fd, "\tclitimeout\t\t" . $bind['client_timeout'] . "\n"); - if($backend['stats_enabled']=='yes') { - fwrite ($fd, "\tstats\t\t\tenable\n"); - if($backend['stats_uri']) - fwrite ($fd, "\tstats\t\t\turi ".$backend['stats_uri']."\n"); - if($backend['stats_realm']) - fwrite ($fd, "\tstats\t\t\trealm " . $backend['stats_realm'] . "\n"); - else - fwrite ($fd, "\tstats\t\t\trealm .\n"); - fwrite ($fd, "\tstats\t\t\tauth " . $backend['stats_username'].":". $backend['stats_password']."\n"); - } - - $a_acl=&$backend['ha_acls']['item']; - if(!is_array($a_acl)) - $a_acl=array(); + fwrite ($fd, "\tdefault_backend\t\t" . $bind['defaultpool'] . "\n"); + + // Combine the rest of the listener configs + foreach ($bind['config'] as $bconfig) { + $a_acl=&$bconfig['ha_acls']['item']; + if(!is_array($a_acl)) + $a_acl=array(); - foreach ($a_acl as $entry) { - $acl = haproxy_find_acl($entry['expression']); - if (!$acl) - continue; + $poolname = $bconfig['pool'] . "_" . strtolower($bconfig['type']); + if (!isset($a_pendingpl[$poolname])) { + $a_pendingpl[$poolname] = array(); + $a_pendingpl[$poolname]['name'] = $poolname; + $a_pendingpl[$poolname]['frontend'] = $bconfig; + } - // Filter out acls for different modes - if ($acl['mode'] != '' && $acl['mode'] != strtolower($backend['type'])) - continue; + foreach ($a_acl as $entry) { + $acl = haproxy_find_acl($entry['expression']); + if (!$acl) + continue; - if ($acl['syntax'] != '') - $expr = $acl['syntax'] . " " . $entry['value']; - else - $expr = $entry['expression'] . " " . $entry['value']; + // Filter out acls for different modes + if ($acl['mode'] != '' && $acl['mode'] != strtolower($bind['type'])) + continue; - fwrite ($fd, "\tacl\t\t\t".$entry['name']."\t".$expr."\n"); - } + if ($acl['syntax'] != '') + $expr = $acl['syntax'] . " " . $entry['value']; + else + $expr = $entry['expression'] . " " . $entry['value']; - if (is_array($a_pools)) { - foreach ($a_pools as $pool) { - if ($backend['pool'] == $pool['name'] && is_array($pool['ha_servers']['item'])) { - if($pool['status'] != 'active') - continue; - if($pool['cookie_name'] && strtolower($backend['type']) == "http") - fwrite ($fd, "\tcookie\t\t\t" . $pool['cookie_name'] . " insert indirect\n"); - - $uri = $pool['monitor_uri']; - if ($pool['monitor_uri']) - $uri = $pool['monitor_uri']; - else - $uri = "/"; - fwrite ($fd, "\toption\t\t\t{$httpchk} HEAD " . $uri . " HTTP/1.0\n"); - - if($pool['cookie'] && strtolower($backend['type']) == "http") - $cookie = " cookie {$pool['cookie']} "; - else - $cookie = ""; - if($pool['advanced']) { - $advanced = base64_decode($pool['advanced']); - $advanced_txt = " " . $advanced; - } else { - $advanced_txt = ""; - } - if($pool['checkinter']) - $checkinter = "check inter {$pool['checkinter']}"; - else - $checkinter = "check inter 1000"; - - $a_servers = &$pool['ha_servers']['item']; - foreach($a_servers as $be) { - if(!$be['port']) { - // the listener can specify a default port - $be['port'] = $backend['svrport']; - } - if(!$be['port']) { - // last resort, use the frontend port - $ports = split(",", "{$backend['port']},"); - $be['port'] = $ports[0]; - } - if (!$be['name']) - $be['name'] = $be['address']; - if($be['backup']) { - $isbackup = "backup"; - } else { - $isbackup = ""; - } - fwrite ($fd, "\tserver\t\t\t" . $be['name'] . " " . $be['address'].":" . $be['port'] . " $cookie " . " $checkinter $isbackup weight " . $be['weight'] . "{$advanced_txt}\n"); - } - - } + fwrite ($fd, "\tacl\t\t\t".$entry['name']."\t".$expr."\n"); + fwrite ($fd, "\tuse_backend\t\t$poolname if " . $entry['name'] . "\n"); } + } fwrite ($fd, "\n"); } - // Sync HAProxy configuration (if enabled) - if(isset($config['installedpackages']['haproxy']['enablesync'])) { - if($config['installedpackages']['haproxy']['synchost1']) { - haproxy_do_xmlrpc_sync($config['installedpackages']['haproxy']['synchost1'], - $config['installedpackages']['haproxy']['syncpassword']); - } - if($config['installedpackages']['haproxy']['synchost2']) { - haproxy_do_xmlrpc_sync($config['installedpackages']['haproxy']['synchost2'], - $config['installedpackages']['haproxy']['syncpassword']); - } - if($config['installedpackages']['haproxy']['synchost3']) { - haproxy_do_xmlrpc_sync($config['installedpackages']['haproxy']['synchost3'], - $config['installedpackages']['haproxy']['syncpassword']); + } + if (is_array($a_pendingpl) && is_array($a_pools)) { + foreach ($a_pendingpl as $pending) { + foreach ($a_pools as $pool) { + if ($pending['frontend']['pool'] == $pool['name']) { + write_backend($fd, $pending['name'], $pool, $pending['frontend']); + } } } } + fwrite ($fd, "\n"); + + // Sync HAProxy configuration (if enabled) + if(isset($config['installedpackages']['haproxy']['enablesync'])) { + if($config['installedpackages']['haproxy']['synchost1']) { + haproxy_do_xmlrpc_sync($config['installedpackages']['haproxy']['synchost1'], + $config['installedpackages']['haproxy']['syncpassword']); + } + if($config['installedpackages']['haproxy']['synchost2']) { + haproxy_do_xmlrpc_sync($config['installedpackages']['haproxy']['synchost2'], + $config['installedpackages']['haproxy']['syncpassword']); + } + if($config['installedpackages']['haproxy']['synchost3']) { + haproxy_do_xmlrpc_sync($config['installedpackages']['haproxy']['synchost3'], + $config['installedpackages']['haproxy']['syncpassword']); + } + } // create config file fclose($fd); -- cgit v1.2.3