From patchwork Thu Jun 2 04:23:05 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Meneghel Rodrigues X-Patchwork-Id: 842672 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p524VU7j006572 for ; Thu, 2 Jun 2011 04:31:31 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751867Ab1FBEXV (ORCPT ); Thu, 2 Jun 2011 00:23:21 -0400 Received: from mx1.redhat.com ([209.132.183.28]:55966 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751847Ab1FBEXU (ORCPT ); Thu, 2 Jun 2011 00:23:20 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p524NJdk000357 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 2 Jun 2011 00:23:19 -0400 Received: from justice.redhat.com (vpn-11-207.rdu.redhat.com [10.11.11.207]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p524NBpu014756; Thu, 2 Jun 2011 00:23:16 -0400 From: Lucas Meneghel Rodrigues To: autotest@test.kernel.org Cc: kvm@vger.kernel.org, Lucas Meneghel Rodrigues , Cleber Rosa , Amos Kong Subject: [PATCH 1/5] KVM test: Adding framework code to control bridge creation Date: Thu, 2 Jun 2011 01:23:05 -0300 Message-Id: <1306988589-17085-2-git-send-email-lmr@redhat.com> In-Reply-To: <1306988589-17085-1-git-send-email-lmr@redhat.com> References: <1306988589-17085-1-git-send-email-lmr@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Thu, 02 Jun 2011 04:31:31 +0000 (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 Signed-off-by: Lucas Meneghel Rodrigues Signed-off-by: Amos Kong --- client/virt/virt_test_setup.py | 172 ++++++++++++++++++++++++++++++++++++++++ 1 files changed, 172 insertions(+), 0 deletions(-) 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()