Statistics
| Branch: | Tag: | Revision:

root / qa / qa_group.py @ ec7b6d63

History | View | Annotate | Download (11.7 kB)

1
#
2
#
3

    
4
# Copyright (C) 2010, 2011, 2012 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
"""QA tests for node groups.
23

24
"""
25

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

    
31
import qa_iptables
32
import qa_config
33
import qa_utils
34

    
35
from qa_utils import AssertCommand, AssertEqual, GetCommandOutput
36

    
37

    
38
def GetDefaultGroup():
39
  """Returns the default node group.
40

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

    
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

    
92
def TestGroupAddRemoveRename():
93
  """gnt-group add/remove/rename"""
94
  existing_group_with_nodes = GetDefaultGroup()
95

    
96
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
97

    
98
  AssertCommand(["gnt-group", "add", group1])
99
  AssertCommand(["gnt-group", "add", group2])
100
  AssertCommand(["gnt-group", "add", group2], fail=True)
101
  AssertCommand(["gnt-group", "add", existing_group_with_nodes], fail=True)
102

    
103
  AssertCommand(["gnt-group", "rename", group1, group2], fail=True)
104
  AssertCommand(["gnt-group", "rename", group1, group3])
105

    
106
  try:
107
    AssertCommand(["gnt-group", "rename", existing_group_with_nodes, group1])
108

    
109
    AssertCommand(["gnt-group", "remove", group2])
110
    AssertCommand(["gnt-group", "remove", group3])
111
    AssertCommand(["gnt-group", "remove", group1], fail=True)
112
  finally:
113
    # Try to ensure idempotency re groups that already existed.
114
    AssertCommand(["gnt-group", "rename", group1, existing_group_with_nodes])
115

    
116

    
117
def TestGroupAddWithOptions():
118
  """gnt-group add with options"""
119
  (group1, ) = qa_utils.GetNonexistentGroups(1)
120

    
121
  AssertCommand(["gnt-group", "add", "--alloc-policy", "notvalid", group1],
122
                fail=True)
123

    
124
  AssertCommand(["gnt-group", "add", "--alloc-policy", "last_resort",
125
                 "--node-parameters", "oob_program=/bin/true", group1])
126

    
127
  AssertCommand(["gnt-group", "remove", group1])
128

    
129

    
130
def _GetGroupIPolicy(groupname):
131
  """Return the run-time values of the cluster-level instance policy.
132

133
  @type groupname: string
134
  @param groupname: node group name
135
  @rtype: tuple
136
  @return: (policy, specs), where:
137
      - policy is a dictionary of the policy values, instance specs excluded
138
      - specs is a dictionary containing only the specs, using the internal
139
        format (see L{constants.IPOLICY_DEFAULTS} for an example), but without
140
        the standard values
141

142
  """
143
  info = qa_utils.GetObjectInfo(["gnt-group", "info", groupname])
144
  assert len(info) == 1
145
  policy = info[0]["Instance policy"]
146

    
147
  (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
148

    
149
  # Sanity checks
150
  assert "minmax" in ret_specs
151
  assert len(ret_specs["minmax"]) > 0
152
  assert len(ret_policy) > 0
153
  return (ret_policy, ret_specs)
154

    
155

    
156
def _TestGroupSetISpecs(groupname, new_specs=None, diff_specs=None,
157
                        fail=False, old_values=None):
158
  """Change instance specs on a group.
159

160
  At most one of new_specs or diff_specs can be specified.
161

162
  @type groupname: string
163
  @param groupname: group name
164
  @type new_specs: dict
165
  @param new_specs: new complete specs, in the same format returned by
166
      L{_GetGroupIPolicy}
167
  @type diff_specs: dict
168
  @param diff_specs: partial specs, it can be an incomplete specifications, but
169
      if min/max specs are specified, their number must match the number of the
170
      existing specs
171
  @type fail: bool
172
  @param fail: if the change is expected to fail
173
  @type old_values: tuple
174
  @param old_values: (old_policy, old_specs), as returned by
175
      L{_GetGroupIPolicy}
176
  @return: same as L{_GetGroupIPolicy}
177

178
  """
179
  build_cmd = lambda opts: ["gnt-group", "modify"] + opts + [groupname]
180
  get_policy = lambda: _GetGroupIPolicy(groupname)
181
  return qa_utils.TestSetISpecs(
182
    new_specs=new_specs, diff_specs=diff_specs,
183
    get_policy_fn=get_policy, build_cmd_fn=build_cmd,
184
    fail=fail, old_values=old_values)
185

    
186

    
187
def _TestGroupModifyISpecs(groupname):
188
  # This test is built on the assumption that the default ipolicy holds for
189
  # the node group under test
190
  old_values = _GetGroupIPolicy(groupname)
191
  samevals = dict((p, 4) for p in constants.ISPECS_PARAMETERS)
192
  base_specs = {
193
    constants.ISPECS_MINMAX: [{
194
      constants.ISPECS_MIN: samevals,
195
      constants.ISPECS_MAX: samevals,
196
      }],
197
    }
198
  mod_values = _TestGroupSetISpecs(groupname, new_specs=base_specs,
199
                                   old_values=old_values)
200
  for par in constants.ISPECS_PARAMETERS:
201
    # First make sure that the test works with good values
202
    good_specs = {
203
      constants.ISPECS_MINMAX: [{
204
        constants.ISPECS_MIN: {par: 8},
205
        constants.ISPECS_MAX: {par: 8},
206
        }],
207
      }
208
    mod_values = _TestGroupSetISpecs(groupname, diff_specs=good_specs,
209
                                     old_values=mod_values)
210
    bad_specs = {
211
      constants.ISPECS_MINMAX: [{
212
        constants.ISPECS_MIN: {par: 8},
213
        constants.ISPECS_MAX: {par: 4},
214
        }],
215
      }
216
    _TestGroupSetISpecs(groupname, diff_specs=bad_specs, fail=True,
217
                        old_values=mod_values)
218
  AssertCommand(["gnt-group", "modify", "--ipolicy-bounds-specs", "default",
219
                 groupname])
220
  AssertEqual(_GetGroupIPolicy(groupname), old_values)
221

    
222
  # Get the ipolicy command (from the cluster config)
223
  mnode = qa_config.GetMasterNode()
224
  addcmd = GetCommandOutput(mnode.primary, utils.ShellQuoteArgs([
225
    "gnt-group", "show-ispecs-cmd", "--include-defaults", groupname,
226
    ]))
227
  modcmd = ["gnt-group", "modify"]
228
  opts = addcmd.split()
229
  assert opts[0:2] == ["gnt-group", "add"]
230
  for k in range(2, len(opts) - 1):
231
    if opts[k].startswith("--ipolicy-"):
232
      assert k + 2 <= len(opts)
233
      modcmd.extend(opts[k:k + 2])
234
  modcmd.append(groupname)
235
  # Apply the ipolicy to the group and verify the result
236
  AssertCommand(modcmd)
237
  new_addcmd = GetCommandOutput(mnode.primary, utils.ShellQuoteArgs([
238
    "gnt-group", "show-ispecs-cmd", groupname,
239
    ]))
240
  AssertEqual(addcmd, new_addcmd)
241

    
242

    
243
def _TestGroupModifyIPolicy(groupname):
244
  _TestGroupModifyISpecs(groupname)
245

    
246
  # We assume that the default ipolicy holds
247
  (old_policy, old_specs) = _GetGroupIPolicy(groupname)
248
  for (par, setval, iname, expval) in [
249
    ("vcpu-ratio", 1.5, None, 1.5),
250
    ("spindle-ratio", 1.5, None, 1.5),
251
    ("disk-templates", constants.DT_PLAIN,
252
     "allowed disk templates", constants.DT_PLAIN)
253
    ]:
254
    if not iname:
255
      iname = par
256
    build_cmdline = lambda val: ["gnt-group", "modify", "--ipolicy-" + par,
257
                                 str(val), groupname]
258

    
259
    AssertCommand(build_cmdline(setval))
260
    (new_policy, new_specs) = _GetGroupIPolicy(groupname)
261
    AssertEqual(new_specs, old_specs)
262
    for (p, val) in new_policy.items():
263
      if p == iname:
264
        AssertEqual(val, expval)
265
      else:
266
        AssertEqual(val, old_policy[p])
267

    
268
    AssertCommand(build_cmdline("default"))
269
    (new_policy, new_specs) = _GetGroupIPolicy(groupname)
270
    AssertEqual(new_specs, old_specs)
271
    AssertEqual(new_policy, old_policy)
272

    
273

    
274
def TestGroupModify():
275
  """gnt-group modify"""
276
  # This tests assumes LVM to be enabled, thus it should skip if
277
  # this is not the case
278
  if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
279
    return
280
  (group1, ) = qa_utils.GetNonexistentGroups(1)
281

    
282
  AssertCommand(["gnt-group", "add", group1])
283

    
284
  try:
285
    _TestGroupModifyIPolicy(group1)
286
    AssertCommand(["gnt-group", "modify", "--alloc-policy", "unallocable",
287
                   "--node-parameters", "oob_program=/bin/false", group1])
288
    AssertCommand(["gnt-group", "modify",
289
                   "--alloc-policy", "notvalid", group1], fail=True)
290
    AssertCommand(["gnt-group", "modify",
291
                   "--node-parameters", "spindle_count=10", group1])
292
    if qa_config.TestEnabled("htools"):
293
      AssertCommand(["hbal", "-L", "-G", group1])
294
    AssertCommand(["gnt-group", "modify",
295
                   "--node-parameters", "spindle_count=default", group1])
296
  finally:
297
    AssertCommand(["gnt-group", "remove", group1])
298

    
299

    
300
def TestGroupList():
301
  """gnt-group list"""
302
  qa_utils.GenericQueryTest("gnt-group", query.GROUP_FIELDS.keys())
303

    
304

    
305
def TestGroupListFields():
306
  """gnt-group list-fields"""
307
  qa_utils.GenericQueryFieldsTest("gnt-group", query.GROUP_FIELDS.keys())
308

    
309

    
310
def TestAssignNodesIncludingSplit(orig_group, node1, node2):
311
  """gnt-group assign-nodes --force
312

313
  Expects node1 and node2 to be primary and secondary for a common instance.
314

315
  """
316
  assert node1 != node2
317

    
318
  (other_group, ) = qa_utils.GetNonexistentGroups(1)
319

    
320
  master_node = qa_config.GetMasterNode().primary
321

    
322
  def AssertInGroup(group, nodes):
323
    real_output = GetCommandOutput(master_node,
324
                                   "gnt-node list --no-headers -o group " +
325
                                   utils.ShellQuoteArgs(nodes))
326
    AssertEqual(real_output.splitlines(), [group] * len(nodes))
327

    
328
  AssertInGroup(orig_group, [node1, node2])
329
  AssertCommand(["gnt-group", "add", other_group])
330

    
331
  try:
332
    AssertCommand(["gnt-group", "assign-nodes", other_group, node1, node2])
333
    AssertInGroup(other_group, [node1, node2])
334

    
335
    # This should fail because moving node1 to orig_group would leave their
336
    # common instance split between orig_group and other_group.
337
    AssertCommand(["gnt-group", "assign-nodes", orig_group, node1], fail=True)
338
    AssertInGroup(other_group, [node1, node2])
339

    
340
    AssertCommand(["gnt-group", "assign-nodes", "--force", orig_group, node1])
341
    AssertInGroup(orig_group, [node1])
342
    AssertInGroup(other_group, [node2])
343

    
344
    AssertCommand(["gnt-group", "assign-nodes", orig_group, node2])
345
    AssertInGroup(orig_group, [node1, node2])
346
  finally:
347
    AssertCommand(["gnt-group", "remove", other_group])