Revision ec7b6d63

b/qa/ganeti-qa.py
181 181
      # To support RAPI on an existing cluster we have to find out the secret
182 182
      rapi_secret = _LookupRapiSecret(rapi_user)
183 183

  
184
  qa_group.ConfigureGroups()
185

  
184 186
  # Test on empty cluster
185 187
  RunTestIf("node-list", qa_node.TestNodeList)
186 188
  RunTestIf("instance-list", qa_instance.TestInstanceList)
b/qa/qa-sample.json
264 264
    "burnin-rename": "xen-test-rename",
265 265
    "burnin-reboot": true,
266 266
    "reboot-types": ["soft", "hard", "full"],
267
    "use-iallocators": false
267
    "use-iallocators": false,
268
    "# Uncomment if you want to run the whole cluster on a different SSH port": null,
269
    "# ssh-port": 222
268 270
  },
269 271

  
270 272
  "# vim: set syntax=javascript :": null
b/qa/qa_group.py
24 24
"""
25 25

  
26 26
from ganeti import constants
27
from ganeti import netutils
27 28
from ganeti import query
28 29
from ganeti import utils
29 30

  
31
import qa_iptables
30 32
import qa_config
31 33
import qa_utils
32 34

  
......
41 43
  return groups.get("group-with-nodes", constants.INITIAL_NODE_GROUP_NAME)
42 44

  
43 45

  
46
def ConfigureGroups():
47
  """Configures groups and nodes for tests such as custom SSH ports.
48

  
49
  """
50

  
51
  defgroup = GetDefaultGroup()
52
  nodes = qa_config.get("nodes")
53
  options = qa_config.get("options", {})
54

  
55
  # Clear any old configuration
56
  qa_iptables.CleanRules(nodes)
57

  
58
  # Custom SSH ports:
59
  ssh_port = options.get("ssh-port")
60
  default_ssh_port = netutils.GetDaemonPort(constants.SSH)
61
  if (ssh_port is not None) and (ssh_port != default_ssh_port):
62
    ModifyGroupSshPort(qa_iptables.GLOBAL_RULES, defgroup, nodes, ssh_port)
63

  
64

  
65
def ModifyGroupSshPort(ipt_rules, group, nodes, ssh_port):
66
  """Modifies the node group settings and sets up iptable rules.
67

  
68
  For each pair of nodes add two rules that affect SSH connections from one
69
  to the other one.
70
  The first one redirects port 22 to some unused port so that connecting
71
  through 22 fails. The second redirects port `ssh_port` to port 22.
72
  Together this results in master seeing the SSH daemons on the nodes on
73
  `ssh_port` instead of 22.
74
  """
75
  default_ssh_port = netutils.GetDaemonPort(constants.SSH)
76
  all_nodes = qa_config.get("nodes")
77
  AssertCommand(["gnt-group", "modify",
78
                 "--node-parameters=ssh_port=" + str(ssh_port),
79
                 group])
80
  for node in nodes:
81
    ipt_rules.RedirectPort(node.primary, "localhost",
82
                           default_ssh_port, 65535)
83
    ipt_rules.RedirectPort(node.primary, "localhost",
84
                           ssh_port, default_ssh_port)
85
    for node2 in all_nodes:
86
      ipt_rules.RedirectPort(node2.primary, node.primary,
87
                             default_ssh_port, 65535)
88
      ipt_rules.RedirectPort(node2.primary, node.primary,
89
                             ssh_port, default_ssh_port)
90

  
91

  
44 92
def TestGroupAddRemoveRename():
45 93
  """gnt-group add/remove/rename"""
46 94
  existing_group_with_nodes = GetDefaultGroup()
b/qa/qa_iptables.py
1
#!/usr/bin/python -u
2
#
3

  
4
# Copyright (C) 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21

  
22
"""Manipulates nodes using `iptables` to simulate non-standard network
23
conditions.
24

  
25
"""
26

  
27
import uuid
28

  
29
import qa_config
30
import qa_utils
31

  
32
from qa_utils import AssertCommand
33

  
34
# String used as a comment for produced `iptables` results
35
IPTABLES_COMMENT_MARKER = "ganeti_qa_script"
36

  
37

  
38
class RulesContext(object):
39
  def __init__(self, nodes):
40
    self._nodes = set()
41

  
42
  def __enter__(self):
43
    self._marker = IPTABLES_COMMENT_MARKER + "_" + str(uuid.uuid4())
44
    return Rules(self)
45

  
46
  def __exit__(self, ext_type, exc_val, exc_tb):
47
    CleanRules(self._nodes, self._marker)
48

  
49
  def _AddNode(self, node):
50
    self._nodes.add(node)
51

  
52

  
53
class Rules(object):
54
  """Allows to introduce iptable rules and dispose them at the end of a block.
55

  
56
  Don't instantiate this class directly. Use `with RulesContext() as r` instead.
57
  """
58

  
59
  def __init__(self, ctx=None):
60
    self._ctx = ctx
61
    if self._ctx is not None:
62
      self._marker = self._ctx._marker
63
    else:
64
      self._marker = IPTABLES_COMMENT_MARKER
65

  
66
  def _AddNode(self, node):
67
    if self._ctx is not None:
68
      self._ctx._AddNode(node)
69

  
70
  def AppendRule(self, node, chain, rule, table="filter"):
71
    """Appends an `iptables` rule to a given node
72
    """
73
    AssertCommand(["iptables", "-t", table, "-A", chain] +
74
                  rule +
75
                  ["-m", "comment",
76
                   "--comment", self._marker],
77
                  node=node)
78
    self._AddNode(node)
79

  
80
  def RedirectPort(self, node, host, port, new_port):
81
    """Adds a rule to a master node that makes a destination host+port visible
82
    under a different port number.
83

  
84
    """
85
    self.AppendRule(node, "OUTPUT",
86
                    ["--protocol", "tcp",
87
                     "--destination", host, "--dport", str(port),
88
                     "--jump", "DNAT",
89
                     "--to-destination", ":" + str(new_port)],
90
                    table="nat")
91

  
92

  
93
GLOBAL_RULES = Rules()
94

  
95

  
96
def CleanRules(nodes, marker=IPTABLES_COMMENT_MARKER):
97
  """Removes all QA `iptables` rules matching a given marker from a given node.
98

  
99
  If no marker is given, the global default is used, which clean all custom
100
  markers.
101
  """
102
  if not hasattr(nodes, '__iter__'):
103
    nodes = [nodes]
104
  for node in nodes:
105
    AssertCommand(("iptables-save | grep -v '%s' | iptables-restore" %
106
                    (marker, )),
107
                  node=node)

Also available in: Unified diff