I'll update my vim configuration, thx.
2013/11/12 Hrvoje Ribicic <[email protected]> > I did not notice this before, but your description has been wrapped at 80 > chars / line when it should have been wrapped at 72 / line. > > I will fix that, but otherwise LGTM, thanks! > > > On Tue, Nov 12, 2013 at 3:41 PM, Petr Pudlak <[email protected]> wrote: > >> This patch adds the `ssh-port` option. If set to a non-standard port, the >> QA >> script sets up the default node group with this port, and before running >> test >> it adds `iptable` rules to all nodes so that the nodes see each other's >> SSH >> servers as running on this port. Their SSH configuration is _not_ changed >> and >> other machines see the nodes on 22 as before. >> >> The `iptable` rules are reset on each QA run, trying to preserve any >> existing >> rules (not created by the script) that might be present. >> >> Signed-off-by: Petr Pudlak <[email protected]> >> --- >> qa/ganeti-qa.py | 2 + >> qa/qa-sample.json | 4 +- >> qa/qa_group.py | 48 ++++++++++++++++++++++++ >> qa/qa_iptables.py | 107 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 4 files changed, 160 insertions(+), 1 deletion(-) >> create mode 100644 qa/qa_iptables.py >> >> diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py >> index bf26cc5..b62fe9d 100755 >> --- a/qa/ganeti-qa.py >> +++ b/qa/ganeti-qa.py >> @@ -181,6 +181,8 @@ def SetupCluster(rapi_user): >> # To support RAPI on an existing cluster we have to find out the >> secret >> rapi_secret = _LookupRapiSecret(rapi_user) >> >> + qa_group.ConfigureGroups() >> + >> # Test on empty cluster >> RunTestIf("node-list", qa_node.TestNodeList) >> RunTestIf("instance-list", qa_instance.TestInstanceList) >> diff --git a/qa/qa-sample.json b/qa/qa-sample.json >> index fd28f38..f16d9ab 100644 >> --- a/qa/qa-sample.json >> +++ b/qa/qa-sample.json >> @@ -263,7 +263,9 @@ >> "burnin-rename": "xen-test-rename", >> "burnin-reboot": true, >> "reboot-types": ["soft", "hard", "full"], >> - "use-iallocators": false >> + "use-iallocators": false, >> + "# Uncomment if you want to run the whole cluster on a different SSH >> port": null, >> + "# ssh-port": 222 >> }, >> >> "# vim: set syntax=javascript :": null >> diff --git a/qa/qa_group.py b/qa/qa_group.py >> index f11e512..da88602 100644 >> --- a/qa/qa_group.py >> +++ b/qa/qa_group.py >> @@ -24,9 +24,11 @@ >> """ >> >> from ganeti import constants >> +from ganeti import netutils >> from ganeti import query >> from ganeti import utils >> >> +import qa_iptables >> import qa_config >> import qa_utils >> >> @@ -41,6 +43,52 @@ def GetDefaultGroup(): >> return groups.get("group-with-nodes", >> constants.INITIAL_NODE_GROUP_NAME) >> >> >> +def ConfigureGroups(): >> + """Configures groups and nodes for tests such as custom SSH ports. >> + >> + """ >> + >> + defgroup = GetDefaultGroup() >> + nodes = qa_config.get("nodes") >> + options = qa_config.get("options", {}) >> + >> + # Clear any old configuration >> + qa_iptables.CleanRules(nodes) >> + >> + # Custom SSH ports: >> + ssh_port = options.get("ssh-port") >> + default_ssh_port = netutils.GetDaemonPort(constants.SSH) >> + if (ssh_port is not None) and (ssh_port != default_ssh_port): >> + ModifyGroupSshPort(qa_iptables.GLOBAL_RULES, defgroup, nodes, >> ssh_port) >> + >> + >> +def ModifyGroupSshPort(ipt_rules, group, nodes, ssh_port): >> + """Modifies the node group settings and sets up iptable rules. >> + >> + For each pair of nodes add two rules that affect SSH connections from >> one >> + to the other one. >> + The first one redirects port 22 to some unused port so that connecting >> + through 22 fails. The second redirects port `ssh_port` to port 22. >> + Together this results in master seeing the SSH daemons on the nodes on >> + `ssh_port` instead of 22. >> + """ >> + default_ssh_port = netutils.GetDaemonPort(constants.SSH) >> + all_nodes = qa_config.get("nodes") >> + AssertCommand(["gnt-group", "modify", >> + "--node-parameters=ssh_port=" + str(ssh_port), >> + group]) >> + for node in nodes: >> + ipt_rules.RedirectPort(node.primary, "localhost", >> + default_ssh_port, 65535) >> + ipt_rules.RedirectPort(node.primary, "localhost", >> + ssh_port, default_ssh_port) >> + for node2 in all_nodes: >> + ipt_rules.RedirectPort(node2.primary, node.primary, >> + default_ssh_port, 65535) >> + ipt_rules.RedirectPort(node2.primary, node.primary, >> + ssh_port, default_ssh_port) >> + >> + >> def TestGroupAddRemoveRename(): >> """gnt-group add/remove/rename""" >> existing_group_with_nodes = GetDefaultGroup() >> diff --git a/qa/qa_iptables.py b/qa/qa_iptables.py >> new file mode 100644 >> index 0000000..a561039 >> --- /dev/null >> +++ b/qa/qa_iptables.py >> @@ -0,0 +1,107 @@ >> +#!/usr/bin/python -u >> +# >> + >> +# Copyright (C) 2013 Google Inc. >> +# >> +# This program is free software; you can redistribute it and/or modify >> +# it under the terms of the GNU General Public License as published by >> +# the Free Software Foundation; either version 2 of the License, or >> +# (at your option) any later version. >> +# >> +# This program is distributed in the hope that it will be useful, but >> +# WITHOUT ANY WARRANTY; without even the implied warranty of >> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> +# General Public License for more details. >> +# >> +# You should have received a copy of the GNU General Public License >> +# along with this program; if not, write to the Free Software >> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA >> +# 02110-1301, USA. >> + >> + >> +"""Manipulates nodes using `iptables` to simulate non-standard network >> +conditions. >> + >> +""" >> + >> +import uuid >> + >> +import qa_config >> +import qa_utils >> + >> +from qa_utils import AssertCommand >> + >> +# String used as a comment for produced `iptables` results >> +IPTABLES_COMMENT_MARKER = "ganeti_qa_script" >> + >> + >> +class RulesContext(object): >> + def __init__(self, nodes): >> + self._nodes = set() >> + >> + def __enter__(self): >> + self._marker = IPTABLES_COMMENT_MARKER + "_" + str(uuid.uuid4()) >> + return Rules(self) >> + >> + def __exit__(self, ext_type, exc_val, exc_tb): >> + CleanRules(self._nodes, self._marker) >> + >> + def _AddNode(self, node): >> + self._nodes.add(node) >> + >> + >> +class Rules(object): >> + """Allows to introduce iptable rules and dispose them at the end of a >> block. >> + >> + Don't instantiate this class directly. Use `with RulesContext() as r` >> instead. >> + """ >> + >> + def __init__(self, ctx=None): >> + self._ctx = ctx >> + if self._ctx is not None: >> + self._marker = self._ctx._marker >> + else: >> + self._marker = IPTABLES_COMMENT_MARKER >> + >> + def _AddNode(self, node): >> + if self._ctx is not None: >> + self._ctx._AddNode(node) >> + >> + def AppendRule(self, node, chain, rule, table="filter"): >> + """Appends an `iptables` rule to a given node >> + """ >> + AssertCommand(["iptables", "-t", table, "-A", chain] + >> + rule + >> + ["-m", "comment", >> + "--comment", self._marker], >> + node=node) >> + self._AddNode(node) >> + >> + def RedirectPort(self, node, host, port, new_port): >> + """Adds a rule to a master node that makes a destination host+port >> visible >> + under a different port number. >> + >> + """ >> + self.AppendRule(node, "OUTPUT", >> + ["--protocol", "tcp", >> + "--destination", host, "--dport", str(port), >> + "--jump", "DNAT", >> + "--to-destination", ":" + str(new_port)], >> + table="nat") >> + >> + >> +GLOBAL_RULES = Rules() >> + >> + >> +def CleanRules(nodes, marker=IPTABLES_COMMENT_MARKER): >> + """Removes all QA `iptables` rules matching a given marker from a >> given node. >> + >> + If no marker is given, the global default is used, which clean all >> custom >> + markers. >> + """ >> + if not hasattr(nodes, '__iter__'): >> + nodes = [nodes] >> + for node in nodes: >> + AssertCommand(("iptables-save | grep -v '%s' | iptables-restore" % >> + (marker, )), >> + node=node) >> -- >> 1.8.4.1 >> >> > > > Hrvoje Ribicic > Ganeti Engineering > Google Germany GmbH > Dienerstr. 12, 80331, München > > Registergericht und -nummer: Hamburg, HRB 86891 > Sitz der Gesellschaft: Hamburg > Geschäftsführer: Graham Law, Christine Elizabeth Flores > Steuernummer: 48/725/00206 > Umsatzsteueridentifikationsnummer: DE813741370 >
