Statistics
| Branch: | Tag: | Revision:

root / qa / qa_group.py @ bab4f56a

History | View | Annotate | Download (10 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 query
28
from ganeti import utils
29

    
30
import qa_config
31
import qa_utils
32

    
33
from qa_utils import AssertCommand, AssertEqual, GetCommandOutput
34

    
35

    
36
def GetDefaultGroup():
37
  """Returns the default node group.
38

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

    
43

    
44
def TestGroupAddRemoveRename():
45
  """gnt-group add/remove/rename"""
46
  existing_group_with_nodes = GetDefaultGroup()
47

    
48
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
49

    
50
  AssertCommand(["gnt-group", "add", group1])
51
  AssertCommand(["gnt-group", "add", group2])
52
  AssertCommand(["gnt-group", "add", group2], fail=True)
53
  AssertCommand(["gnt-group", "add", existing_group_with_nodes], fail=True)
54

    
55
  AssertCommand(["gnt-group", "rename", group1, group2], fail=True)
56
  AssertCommand(["gnt-group", "rename", group1, group3])
57

    
58
  try:
59
    AssertCommand(["gnt-group", "rename", existing_group_with_nodes, group1])
60

    
61
    AssertCommand(["gnt-group", "remove", group2])
62
    AssertCommand(["gnt-group", "remove", group3])
63
    AssertCommand(["gnt-group", "remove", group1], fail=True)
64
  finally:
65
    # Try to ensure idempotency re groups that already existed.
66
    AssertCommand(["gnt-group", "rename", group1, existing_group_with_nodes])
67

    
68

    
69
def TestGroupAddWithOptions():
70
  """gnt-group add with options"""
71
  (group1, ) = qa_utils.GetNonexistentGroups(1)
72

    
73
  AssertCommand(["gnt-group", "add", "--alloc-policy", "notvalid", group1],
74
                fail=True)
75

    
76
  AssertCommand(["gnt-group", "add", "--alloc-policy", "last_resort",
77
                 "--node-parameters", "oob_program=/bin/true", group1])
78

    
79
  AssertCommand(["gnt-group", "remove", group1])
80

    
81

    
82
def _GetGroupIPolicy(groupname):
83
  """Return the run-time values of the cluster-level instance policy.
84

85
  @type groupname: string
86
  @param groupname: node group name
87
  @rtype: tuple
88
  @return: (policy, specs), where:
89
      - policy is a dictionary of the policy values, instance specs excluded
90
      - specs is a dictionary containing only the specs, using the internal
91
        format (see L{constants.IPOLICY_DEFAULTS} for an example), but without
92
        the standard values
93

94
  """
95
  info = qa_utils.GetObjectInfo(["gnt-group", "info", groupname])
96
  assert len(info) == 1
97
  policy = info[0]["Instance policy"]
98

    
99
  (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
100

    
101
  # Sanity checks
102
  assert "minmax" in ret_specs
103
  assert len(ret_specs["minmax"]) > 0
104
  assert len(ret_policy) > 0
105
  return (ret_policy, ret_specs)
106

    
107

    
108
def _TestGroupSetISpecs(groupname, new_specs=None, diff_specs=None,
109
                        fail=False, old_values=None):
110
  """Change instance specs on a group.
111

112
  At most one of new_specs or diff_specs can be specified.
113

114
  @type groupname: string
115
  @param groupname: group name
116
  @type new_specs: dict
117
  @param new_specs: new complete specs, in the same format returned by
118
      L{_GetGroupIPolicy}
119
  @type diff_specs: dict
120
  @param diff_specs: partial specs, it can be an incomplete specifications, but
121
      if min/max specs are specified, their number must match the number of the
122
      existing specs
123
  @type fail: bool
124
  @param fail: if the change is expected to fail
125
  @type old_values: tuple
126
  @param old_values: (old_policy, old_specs), as returned by
127
      L{_GetGroupIPolicy}
128
  @return: same as L{_GetGroupIPolicy}
129

130
  """
131
  build_cmd = lambda opts: ["gnt-group", "modify"] + opts + [groupname]
132
  get_policy = lambda: _GetGroupIPolicy(groupname)
133
  return qa_utils.TestSetISpecs(
134
    new_specs=new_specs, diff_specs=diff_specs,
135
    get_policy_fn=get_policy, build_cmd_fn=build_cmd,
136
    fail=fail, old_values=old_values)
137

    
138

    
139
def _TestGroupModifyISpecs(groupname):
140
  # This test is built on the assumption that the default ipolicy holds for
141
  # the node group under test
142
  old_values = _GetGroupIPolicy(groupname)
143
  samevals = dict((p, 4) for p in constants.ISPECS_PARAMETERS)
144
  base_specs = {
145
    constants.ISPECS_MINMAX: [{
146
      constants.ISPECS_MIN: samevals,
147
      constants.ISPECS_MAX: samevals,
148
      }],
149
    }
150
  mod_values = _TestGroupSetISpecs(groupname, new_specs=base_specs,
151
                                   old_values=old_values)
152
  for par in constants.ISPECS_PARAMETERS:
153
    # First make sure that the test works with good values
154
    good_specs = {
155
      constants.ISPECS_MINMAX: [{
156
        constants.ISPECS_MIN: {par: 8},
157
        constants.ISPECS_MAX: {par: 8},
158
        }],
159
      }
160
    mod_values = _TestGroupSetISpecs(groupname, diff_specs=good_specs,
161
                                     old_values=mod_values)
162
    bad_specs = {
163
      constants.ISPECS_MINMAX: [{
164
        constants.ISPECS_MIN: {par: 8},
165
        constants.ISPECS_MAX: {par: 4},
166
        }],
167
      }
168
    _TestGroupSetISpecs(groupname, diff_specs=bad_specs, fail=True,
169
                        old_values=mod_values)
170
  AssertCommand(["gnt-group", "modify", "--ipolicy-bounds-specs", "default",
171
                 groupname])
172
  AssertEqual(_GetGroupIPolicy(groupname), old_values)
173

    
174
  # Get the ipolicy command (from the cluster config)
175
  mnode = qa_config.GetMasterNode()
176
  addcmd = GetCommandOutput(mnode.primary, utils.ShellQuoteArgs([
177
    "gnt-group", "show-ispecs-cmd", "--include-defaults", groupname,
178
    ]))
179
  modcmd = ["gnt-group", "modify"]
180
  opts = addcmd.split()
181
  assert opts[0:2] == ["gnt-group", "add"]
182
  for k in range(2, len(opts) - 1):
183
    if opts[k].startswith("--ipolicy-"):
184
      assert k + 2 <= len(opts)
185
      modcmd.extend(opts[k:k + 2])
186
  modcmd.append(groupname)
187
  # Apply the ipolicy to the group and verify the result
188
  AssertCommand(modcmd)
189
  new_addcmd = GetCommandOutput(mnode.primary, utils.ShellQuoteArgs([
190
    "gnt-group", "show-ispecs-cmd", groupname,
191
    ]))
192
  AssertEqual(addcmd, new_addcmd)
193

    
194

    
195
def _TestGroupModifyIPolicy(groupname):
196
  _TestGroupModifyISpecs(groupname)
197

    
198
  # We assume that the default ipolicy holds
199
  (old_policy, old_specs) = _GetGroupIPolicy(groupname)
200
  for (par, setval, iname, expval) in [
201
    ("vcpu-ratio", 1.5, None, 1.5),
202
    ("spindle-ratio", 1.5, None, 1.5),
203
    ("disk-templates", constants.DT_PLAIN,
204
     "allowed disk templates", constants.DT_PLAIN)
205
    ]:
206
    if not iname:
207
      iname = par
208
    build_cmdline = lambda val: ["gnt-group", "modify", "--ipolicy-" + par,
209
                                 str(val), groupname]
210

    
211
    AssertCommand(build_cmdline(setval))
212
    (new_policy, new_specs) = _GetGroupIPolicy(groupname)
213
    AssertEqual(new_specs, old_specs)
214
    for (p, val) in new_policy.items():
215
      if p == iname:
216
        AssertEqual(val, expval)
217
      else:
218
        AssertEqual(val, old_policy[p])
219

    
220
    AssertCommand(build_cmdline("default"))
221
    (new_policy, new_specs) = _GetGroupIPolicy(groupname)
222
    AssertEqual(new_specs, old_specs)
223
    AssertEqual(new_policy, old_policy)
224

    
225

    
226
def TestGroupModify():
227
  """gnt-group modify"""
228
  # This tests assumes LVM to be enabled, thus it should skip if
229
  # this is not the case
230
  if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
231
    return
232
  (group1, ) = qa_utils.GetNonexistentGroups(1)
233

    
234
  AssertCommand(["gnt-group", "add", group1])
235

    
236
  try:
237
    _TestGroupModifyIPolicy(group1)
238
    AssertCommand(["gnt-group", "modify", "--alloc-policy", "unallocable",
239
                   "--node-parameters", "oob_program=/bin/false", group1])
240
    AssertCommand(["gnt-group", "modify",
241
                   "--alloc-policy", "notvalid", group1], fail=True)
242
    AssertCommand(["gnt-group", "modify",
243
                   "--node-parameters", "spindle_count=10", group1])
244
    if qa_config.TestEnabled("htools"):
245
      AssertCommand(["hbal", "-L", "-G", group1])
246
    AssertCommand(["gnt-group", "modify",
247
                   "--node-parameters", "spindle_count=default", group1])
248
  finally:
249
    AssertCommand(["gnt-group", "remove", group1])
250

    
251

    
252
def TestGroupList():
253
  """gnt-group list"""
254
  qa_utils.GenericQueryTest("gnt-group", query.GROUP_FIELDS.keys())
255

    
256

    
257
def TestGroupListFields():
258
  """gnt-group list-fields"""
259
  qa_utils.GenericQueryFieldsTest("gnt-group", query.GROUP_FIELDS.keys())
260

    
261

    
262
def TestAssignNodesIncludingSplit(orig_group, node1, node2):
263
  """gnt-group assign-nodes --force
264

265
  Expects node1 and node2 to be primary and secondary for a common instance.
266

267
  """
268
  assert node1 != node2
269

    
270
  (other_group, ) = qa_utils.GetNonexistentGroups(1)
271

    
272
  master_node = qa_config.GetMasterNode().primary
273

    
274
  def AssertInGroup(group, nodes):
275
    real_output = GetCommandOutput(master_node,
276
                                   "gnt-node list --no-headers -o group " +
277
                                   utils.ShellQuoteArgs(nodes))
278
    AssertEqual(real_output.splitlines(), [group] * len(nodes))
279

    
280
  AssertInGroup(orig_group, [node1, node2])
281
  AssertCommand(["gnt-group", "add", other_group])
282

    
283
  try:
284
    AssertCommand(["gnt-group", "assign-nodes", other_group, node1, node2])
285
    AssertInGroup(other_group, [node1, node2])
286

    
287
    # This should fail because moving node1 to orig_group would leave their
288
    # common instance split between orig_group and other_group.
289
    AssertCommand(["gnt-group", "assign-nodes", orig_group, node1], fail=True)
290
    AssertInGroup(other_group, [node1, node2])
291

    
292
    AssertCommand(["gnt-group", "assign-nodes", "--force", orig_group, node1])
293
    AssertInGroup(orig_group, [node1])
294
    AssertInGroup(other_group, [node2])
295

    
296
    AssertCommand(["gnt-group", "assign-nodes", orig_group, node2])
297
    AssertInGroup(orig_group, [node1, node2])
298
  finally:
299
    AssertCommand(["gnt-group", "remove", other_group])