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

Reply via email to