Merge pull request #931 from ekholabs/fix/loadbalancer

CLOUDSTACK-8947 - Load Balancer not working with Isolated NetworksThis PR fixes the Load Balance feature by adding iptables rules for the public IP and port of the LB.

In order to cover the changes, I improved and executed the smoke/test_loadbalance.py. In addition, I also executed many other tests to make sure the main network/VM functionalities are working as expected.

Test report will follow.

* pr/931:
  CLOUDSTACK-8947 - Do not rely on the machine hostname to verify the test
  CLOUDSTACK-8947 - Fail fast!
  CLOUDSTACK-8947 - Adding some logging to better understand whay is happening with the Processes
  CLOUDSTACK-8947 - Adding some logging to better understand what's happening with the rules
  CLOUDSTACK-8947 - Configure the firewall when the load balancer is setup
  CLOUDSTACK-8947 - Avoid multiple entries in the FW_EGRESS_RULES table
  CLOUDSTACK-8947 - Open the input chain to IP when loadbalancer is configured
  CLOUDSTACK-8947 - FW_EGRESS should be added only to filter table

Signed-off-by: Remi Bergsma <github@remi.nl>
This commit is contained in:
Remi Bergsma 2015-10-14 18:30:32 +02:00
commit 2ce5a0c964
5 changed files with 110 additions and 75 deletions

View File

@ -141,14 +141,14 @@ class CsAcl(CsDataBag):
" -m %s " % rule['protocol'] +
" --icmp-type %s -j %s" % (icmp_type, self.rule['action'])])
else:
fwr = " -A FW_EGRESS_RULES" + \
" -s %s " % cidr
fwr = " -A FW_EGRESS_RULES"
if rule['protocol'] != "all":
fwr += "-p %s " % rule['protocol'] + \
fwr += " -s %s " % cidr + \
" -p %s " % cidr, rule['protocol'] + \
" -m %s " % rule['protocol'] + \
" --dport %s" % rnge
self.fw.append(["filter", "front", "%s -j %s" % (fwr, rule['action'])])
self.fw.append(["filter", "", "%s -j %s" % (fwr, rule['action'])])
logging.debug("EGRESS rule configured for protocol ==> %s, action ==> %s", rule['protocol'], rule['action'])

View File

@ -19,6 +19,7 @@ import os.path
import re
import shutil
from cs.CsDatabag import CsDataBag
from CsProcess import CsProcess
from CsFile import CsFile
import CsHelper
@ -27,7 +28,7 @@ HAPROXY_CONF_P = "/etc/haproxy/haproxy.cfg"
class CsLoadBalancer(CsDataBag):
""" Manage dhcp entries """
""" Manage Load Balancer entries """
def process(self):
if "config" not in self.dbag.keys():
@ -43,4 +44,33 @@ class CsLoadBalancer(CsDataBag):
if not file2.compare(file1):
file1.commit()
shutil.copy2(HAPROXY_CONF_T, HAPROXY_CONF_P)
CsHelper.service("haproxy", "restart")
proc = CsProcess(['/var/run/haproxy.pid'])
if not proc.find():
logging.debug("CsLoadBalancer:: will restart HAproxy!")
CsHelper.service("haproxy", "restart")
else:
logging.debug("CsLoadBalancer:: will reload HAproxy!")
CsHelper.service("haproxy", "reload")
add_rules = self.dbag['config'][0]['add_rules']
remove_rules = self.dbag['config'][0]['remove_rules']
self._configure_firewall(add_rules, remove_rules)
def _configure_firewall(self, add_rules, remove_rules):
firewall = self.config.get_fw()
logging.debug("CsLoadBalancer:: configuring firewall. Add rules ==> %s" % add_rules)
logging.debug("CsLoadBalancer:: configuring firewall. Remove rules ==> %s" % remove_rules)
for rules in add_rules:
path = rules.split(':')
ip = path[0]
port = path[1]
firewall.append(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)])
for rules in remove_rules:
path = rules.split(':')
ip = path[0]
port = path[1]
firewall.append(["filter", "", "-D INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)])

View File

@ -150,6 +150,8 @@ class CsNetfilters(object):
new_rule.set_table(fw[0])
if isinstance(fw[1], int):
new_rule.set_count(fw[1])
logging.debug("Checking if the rule already exists: rule=%s table=%s chain=%s", new_rule.get_rule(), new_rule.get_table(), new_rule.get_chain())
if self.has_rule(new_rule):
logging.debug("Exists: rule=%s table=%s", fw[2], new_rule.get_table())
else:

View File

@ -46,6 +46,8 @@ class CsProcess(object):
matches = len([m for m in proc if m in self.search])
if matches == items:
self.pid.append(re.split("\s+", i)[1])
logging.debug("CsProcess:: Searching for process ==> %s and found PIDs ==> %s", self.search, self.pid)
return self.pid
def find(self):

View File

@ -120,7 +120,7 @@ class TestLoadBalance(cloudstackTestCase):
cleanup_resources(cls.apiclient, cls._cleanup)
return
def try_ssh(self, ip_addr, hostnames):
def try_ssh(self, ip_addr, unameCmd):
try:
self.debug(
"SSH into VM (IPaddress: %s) & NAT Rule (Public IP: %s)" %
@ -133,10 +133,11 @@ class TestLoadBalance(cloudstackTestCase):
ip_addr,
self.services['lbrule']["publicport"],
self.vm_1.username,
self.vm_1.password
self.vm_1.password,
retries=5
)
hostnames.append(ssh_1.execute("hostname")[0])
self.debug(hostnames)
unameCmd.append(ssh_1.execute("uname")[0])
self.debug(unameCmd)
except Exception as e:
self.fail("%s: SSH failed for VM with IP Address: %s" %
(e, ip_addr))
@ -150,7 +151,7 @@ class TestLoadBalance(cloudstackTestCase):
# Validate the Following:
#1. listLoadBalancerRules should return the added rule
#2. attempt to ssh twice on the load balanced IP
#3. verify using the hostname of the VM
#3. verify using the UNAME of the VM
# that round robin is indeed happening as expected
src_nat_ip_addrs = PublicIPAddress.list(
self.apiclient,
@ -254,30 +255,30 @@ class TestLoadBalance(cloudstackTestCase):
)
hostnames = []
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
unameResults = []
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
self.debug("Hostnames: %s" % str(hostnames))
self.debug("UNAME: %s" % str(unameResults))
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
self.assertIn(
self.vm_2.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server2"
)
#SSH should pass till there is a last VM associated with LB rule
lb_rule.remove(self.apiclient, [self.vm_2])
# making hostnames list empty
hostnames[:] = []
# making unameResultss list empty
unameResults[:] = []
try:
self.debug("SSHing into IP address: %s after removing VM (ID: %s)" %
@ -286,10 +287,10 @@ class TestLoadBalance(cloudstackTestCase):
self.vm_2.id
))
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
except Exception as e:
@ -300,7 +301,7 @@ class TestLoadBalance(cloudstackTestCase):
with self.assertRaises(Exception):
self.debug("Removed all VMs, trying to SSH")
self.try_ssh(src_nat_ip_addr.ipaddress, hostnames)
self.try_ssh(src_nat_ip_addr.ipaddress, unameResults)
return
@attr(tags = ["advanced", "advancedns", "smoke"], required_hardware="true")
@ -310,7 +311,7 @@ class TestLoadBalance(cloudstackTestCase):
# Validate the Following:
#1. listLoadBalancerRules should return the added rule
#2. attempt to ssh twice on the load balanced IP
#3. verify using the hostname of the VM that
#3. verify using the UNAME of the VM that
# round robin is indeed happening as expected
#Create Load Balancer rule and assign VMs to rule
@ -371,22 +372,22 @@ class TestLoadBalance(cloudstackTestCase):
"Check List Load Balancer instances Rules returns valid VM ID"
)
try:
hostnames = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
unameResults = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.debug("Hostnames: %s" % str(hostnames))
self.debug("UNAME: %s" % str(unameResults))
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
self.assertIn(
self.vm_2.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server2"
)
@ -398,15 +399,15 @@ class TestLoadBalance(cloudstackTestCase):
self.vm_2.id
))
# Making host list empty
hostnames[:] = []
unameResults[:] = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
self.debug("Hostnames after removing VM2: %s" % str(hostnames))
self.debug("UNAME after removing VM2: %s" % str(unameResults))
except Exception as e:
self.fail("%s: SSH failed for VM with IP Address: %s" %
(e, self.non_src_nat_ip.ipaddress.ipaddress))
@ -418,7 +419,7 @@ class TestLoadBalance(cloudstackTestCase):
self.non_src_nat_ip.ipaddress.ipaddress,
self.vm_1.id
))
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
return
@attr(tags = ["advanced", "advancedns", "smoke"], required_hardware="true")
@ -466,29 +467,29 @@ class TestLoadBalance(cloudstackTestCase):
)
lb_rule.assign(self.apiclient, [self.vm_1, self.vm_2])
hostnames = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
unameResults = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.debug("Hostnames: %s" % str(hostnames))
self.debug("UNAME: %s" % str(unameResults))
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
self.assertIn(
self.vm_2.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server2"
)
#Removing VM and assigning another VM to LB rule
lb_rule.remove(self.apiclient, [self.vm_2])
# making hostnames list empty
hostnames[:] = []
# making unameResults list empty
unameResults[:] = []
try:
self.debug("SSHing again into IP address: %s with VM (ID: %s) added to LB rule" %
@ -496,11 +497,11 @@ class TestLoadBalance(cloudstackTestCase):
self.non_src_nat_ip.ipaddress.ipaddress,
self.vm_1.id,
))
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
except Exception as e:
@ -509,22 +510,22 @@ class TestLoadBalance(cloudstackTestCase):
lb_rule.assign(self.apiclient, [self.vm_3])
# # Making hostnames list empty
hostnames[:] = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, hostnames)
self.debug("Hostnames: %s" % str(hostnames))
# # Making unameResults list empty
unameResults[:] = []
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.try_ssh(self.non_src_nat_ip.ipaddress.ipaddress, unameResults)
self.debug("UNAME: %s" % str(unameResults))
self.assertIn(
self.vm_1.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server1"
)
self.assertIn(
self.vm_3.name,
hostnames,
"Linux",
unameResults,
"Check if ssh succeeded for server3"
)
return