diff mbox

[1/5] KVM test: Adding framework code to control bridge creation

Message ID 1306988589-17085-2-git-send-email-lmr@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lucas Meneghel Rodrigues June 2, 2011, 4:23 a.m. UTC
Provide in framework utility code to control the creation
of a bridge, in order to provide TAP functionality for
autotest users without relying on previous setup made by
the user.

This is a reimplementation of Amos's code, the differences
are:

 * Implemented as a setup class, taking advantage of object
internal state to use in different places of the code
 * Split up the operations to make it easier to understand
the steps and why we are doing them
 * Use of autotest API instead of commands

Changes from v2:
 * Changed firewall rules to match libvirt rules,
made the rules to be added with -I, which will place
them before exclusion rules
 * Safer cleanup, always try to cleanup when something
wrong happens during setup.

Signed-off-by: Cleber Rosa <crosa@redhat.com>
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
---
 client/virt/virt_test_setup.py |  172 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 172 insertions(+), 0 deletions(-)
diff mbox

Patch

diff --git a/client/virt/virt_test_setup.py b/client/virt/virt_test_setup.py
index f915c1b..8e69b01 100644
--- a/client/virt/virt_test_setup.py
+++ b/client/virt/virt_test_setup.py
@@ -105,3 +105,175 @@  class HugePageConfig(object):
             return
         utils.system("echo 0 > %s" % self.kernel_hp_file)
         logging.debug("Hugepage memory successfuly dealocated")
+
+
+class PrivateBridgeError(Exception):
+    def __init__(self, brname):
+        self.brname = brname
+
+    def __str__(self):
+        return "Bridge %s not available after setup" % self.brname
+
+
+class PrivateBridgeConfig(object):
+    __shared_state = {}
+    def __init__(self, params=None):
+        self.__dict__ = self.__shared_state
+        if params is not None:
+            self.brname = params.get("priv_brname", 'atbr0')
+            self.subnet = params.get("priv_subnet", '192.168.58')
+            self.ip_version = params.get("bridge_ip_version", "ipv4")
+            self.dhcp_server_pid = None
+            self.iptables_rules = [
+            "INPUT 1 -i %s -p udp -m udp --dport 53 -j ACCEPT" % self.brname,
+            "INPUT 2 -i %s -p tcp -m tcp --dport 53 -j ACCEPT" % self.brname,
+            "INPUT 3 -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.brname,
+            "INPUT 4 -i %s -p tcp -m tcp --dport 67 -j ACCEPT" % self.brname,
+            "INPUT 5 -i %s -p tcp -m tcp --dport 12323 -j ACCEPT" % self.brname,
+            "FORWARD 1 -m physdev --physdev-is-bridged -j ACCEPT",
+            "FORWARD 2 -d %s.0/24 -o %s -m state --state RELATED,ESTABLISHED "
+            "-j ACCEPT" % (self.subnet, self.brname),
+            "FORWARD 3 -s %s.0/24 -i %s -j ACCEPT" % (self.subnet, self.brname),
+            "FORWARD 4 -i %s -o %s -j ACCEPT" % (self.brname, self.brname),
+            ("FORWARD 5 -o %s -j REJECT --reject-with icmp-port-unreachable" %
+             self.brname),
+            ("FORWARD 6 -i %s -j REJECT --reject-with icmp-port-unreachable" %
+             self.brname)]
+
+
+    def _add_bridge(self):
+        utils.system("brctl addbr %s" % self.brname)
+        ip_fwd_path = "/proc/sys/net/%s/ip_forward" % self.ip_version
+        ip_fwd = open(ip_fwd_path, "w")
+        ip_fwd.write("1\n")
+        utils.system("brctl stp %s on" % self.brname)
+        utils.system("brctl setfd %s 0" % self.brname)
+
+
+    def _bring_bridge_up(self):
+        utils.system("ifconfig %s %s.1 up" % (self.brname, self.subnet))
+
+
+    def _iptables_add(self, cmd):
+        return utils.system("iptables -I %s" % cmd)
+
+
+    def _iptables_del(self, cmd):
+        return utils.system("iptables -D %s" % cmd)
+
+
+    def _enable_nat(self):
+        for rule in self.iptables_rules:
+            self._iptables_add(rule)
+
+
+    def _start_dhcp_server(self):
+        utils.system("service dnsmasq stop")
+        utils.system("dnsmasq --strict-order --bind-interfaces "
+                     "--listen-address %s.1 --dhcp-range %s.2,%s.254 "
+                     "--dhcp-lease-max=253 "
+                     "--dhcp-no-override "
+                     "--pid-file=/tmp/dnsmasq.pid "
+                     "--log-facility=/tmp/dnsmasq.log" %
+                     (self.subnet, self.subnet, self.subnet))
+        self.dhcp_server_pid = None
+        try:
+            self.dhcp_server_pid = int(open('/tmp/dnsmasq.pid', 'r').read())
+        except ValueError:
+            raise PrivateBridgeError(self.brname)
+        logging.debug("Started internal DHCP server with PID %s",
+                      self.dhcp_server_pid)
+
+
+    def _verify_bridge(self):
+        brctl_output = utils.system_output("brctl show")
+        if self.brname not in brctl_output:
+            raise PrivateBridgeError(self.brname)
+
+
+    def setup(self):
+        brctl_output = utils.system_output("brctl show")
+        for line in brctl_output:
+            if line.startswith(self.brname):
+                logging.critical("%s", len(line.split()))
+        if self.brname not in brctl_output:
+            logging.info("Configuring KVM test private bridge %s", self.brname)
+            try:
+                self._add_bridge()
+            except:
+                self._remove_bridge()
+                raise
+            try:
+                self._bring_bridge_up()
+            except:
+                self._bring_bridge_down()
+                self._remove_bridge()
+                raise
+            try:
+                self._enable_nat()
+            except:
+                self._disable_nat()
+                self._bring_bridge_down()
+                self._remove_bridge()
+                raise
+            try:
+                self._start_dhcp_server()
+            except:
+                self._stop_dhcp_server()
+                self._disable_nat()
+                self._bring_bridge_down()
+                self._remove_bridge()
+                raise
+            self._verify_bridge()
+
+
+    def _stop_dhcp_server(self):
+        if self.dhcp_server_pid is not None:
+            try:
+                os.kill(self.dhcp_server_pid, 15)
+            except OSError:
+                pass
+        else:
+            try:
+                dhcp_server_pid = int(open('/tmp/dnsmasq.pid', 'r').read())
+            except ValueError:
+                return
+            try:
+                os.kill(dhcp_server_pid, 15)
+            except OSError:
+                pass
+
+
+    def _bring_bridge_down(self):
+        utils.system("ifconfig %s down" % self.brname, ignore_status=True)
+
+
+    def _disable_nat(self):
+        for rule in self.iptables_rules:
+            split_list = rule.split(' ')
+            # We need to remove numbering here
+            split_list.pop(1)
+            rule = " ".join(split_list)
+            self._iptables_del(rule)
+
+
+    def _remove_bridge(self):
+        utils.system("brctl delbr %s" % self.brname, ignore_status=True)
+
+
+    def cleanup(self):
+        brctl_output = utils.system_output("brctl show")
+        cleanup = False
+        for line in brctl_output.split("\n"):
+            if line.startswith(self.brname):
+                # len == 4 means there is a TAP using the bridge
+                # so don't try to clean it up
+                if len(line.split()) < 4:
+                    cleanup = True
+                    break
+        if cleanup:
+            logging.debug("Cleaning up KVM test private bridge %s", self.brname)
+            self._stop_dhcp_server()
+            self._disable_nat()
+            self._bring_bridge_down()
+            self._remove_bridge()