1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 """Node group related commands"""
31
32
33
34
35
36 from cStringIO import StringIO
37
38 from ganeti.cli import *
39 from ganeti import constants
40 from ganeti import opcodes
41 from ganeti import utils
42 from ganeti import compat
43
44
45
46 _LIST_DEF_FIELDS = ["name", "node_cnt", "pinst_cnt", "alloc_policy", "ndparams"]
47
48 _ENV_OVERRIDE = compat.UniqueFrozenset(["list"])
49
50
52 """Add a node group to the cluster.
53
54 @param opts: the command line options selected by the user
55 @type args: list
56 @param args: a list of length 1 with the name of the group to create
57 @rtype: int
58 @return: the desired exit code
59
60 """
61 ipolicy = CreateIPolicyFromOpts(
62 minmax_ispecs=opts.ipolicy_bounds_specs,
63 ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio,
64 ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio,
65 ipolicy_disk_templates=opts.ipolicy_disk_templates,
66 group_ipolicy=True)
67
68 (group_name,) = args
69 diskparams = dict(opts.diskparams)
70
71 if opts.disk_state:
72 disk_state = utils.FlatToDict(opts.disk_state)
73 else:
74 disk_state = {}
75 hv_state = dict(opts.hv_state)
76
77 op = opcodes.OpGroupAdd(group_name=group_name, ndparams=opts.ndparams,
78 alloc_policy=opts.alloc_policy,
79 diskparams=diskparams, ipolicy=ipolicy,
80 hv_state=hv_state,
81 disk_state=disk_state)
82 SubmitOrSend(op, opts)
83
84
86 """Assign nodes to a group.
87
88 @param opts: the command line options selected by the user
89 @type args: list
90 @param args: args[0]: group to assign nodes to; args[1:]: nodes to assign
91 @rtype: int
92 @return: the desired exit code
93
94 """
95 group_name = args[0]
96 node_names = args[1:]
97
98 op = opcodes.OpGroupAssignNodes(group_name=group_name, nodes=node_names,
99 force=opts.force)
100 SubmitOrSend(op, opts)
101
102
104 """Format dict data into command-line format.
105
106 @param data: The input dict to be formatted
107 @return: The formatted dict
108
109 """
110 if not data:
111 return "(empty)"
112
113 return utils.CommaJoin(["%s=%s" % (key, value)
114 for key, value in data.items()])
115
116
118 """List node groups and their properties.
119
120 @param opts: the command line options selected by the user
121 @type args: list
122 @param args: groups to list, or empty for all
123 @rtype: int
124 @return: the desired exit code
125
126 """
127 desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
128 fmtoverride = {
129 "node_list": (",".join, False),
130 "pinst_list": (",".join, False),
131 "ndparams": (_FmtDict, False),
132 }
133
134 cl = GetClient(query=True)
135
136 return GenericList(constants.QR_GROUP, desired_fields, args, None,
137 opts.separator, not opts.no_headers,
138 format_override=fmtoverride, verbose=opts.verbose,
139 force_filter=opts.force_filter, cl=cl)
140
141
143 """List node fields.
144
145 @param opts: the command line options selected by the user
146 @type args: list
147 @param args: fields to list, or empty for all
148 @rtype: int
149 @return: the desired exit code
150
151 """
152 cl = GetClient(query=True)
153
154 return GenericListFields(constants.QR_GROUP, args, opts.separator,
155 not opts.no_headers, cl=cl)
156
157
159 """Modifies a node group's parameters.
160
161 @param opts: the command line options selected by the user
162 @type args: list
163 @param args: should contain only one element, the node group name
164
165 @rtype: int
166 @return: the desired exit code
167
168 """
169 allmods = [opts.ndparams, opts.alloc_policy, opts.diskparams, opts.hv_state,
170 opts.disk_state, opts.ipolicy_bounds_specs,
171 opts.ipolicy_vcpu_ratio, opts.ipolicy_spindle_ratio,
172 opts.diskparams, opts.ipolicy_disk_templates]
173 if allmods.count(None) == len(allmods):
174 ToStderr("Please give at least one of the parameters.")
175 return 1
176
177 if opts.disk_state:
178 disk_state = utils.FlatToDict(opts.disk_state)
179 else:
180 disk_state = {}
181
182 hv_state = dict(opts.hv_state)
183
184 diskparams = dict(opts.diskparams)
185
186
187 ipolicy = CreateIPolicyFromOpts(
188 minmax_ispecs=opts.ipolicy_bounds_specs,
189 ipolicy_disk_templates=opts.ipolicy_disk_templates,
190 ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio,
191 ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio,
192 group_ipolicy=True,
193 allowed_values=[constants.VALUE_DEFAULT])
194
195 op = opcodes.OpGroupSetParams(group_name=args[0],
196 ndparams=opts.ndparams,
197 alloc_policy=opts.alloc_policy,
198 hv_state=hv_state,
199 disk_state=disk_state,
200 diskparams=diskparams,
201 ipolicy=ipolicy)
202
203 result = SubmitOrSend(op, opts)
204
205 if result:
206 ToStdout("Modified node group %s", args[0])
207 for param, data in result:
208 ToStdout(" - %-5s -> %s", param, data)
209
210 return 0
211
212
214 """Remove a node group from the cluster.
215
216 @param opts: the command line options selected by the user
217 @type args: list
218 @param args: a list of length 1 with the name of the group to remove
219 @rtype: int
220 @return: the desired exit code
221
222 """
223 (group_name,) = args
224 op = opcodes.OpGroupRemove(group_name=group_name)
225 SubmitOrSend(op, opts)
226
227
229 """Rename a node group.
230
231 @param opts: the command line options selected by the user
232 @type args: list
233 @param args: a list of length 2, [old_name, new_name]
234 @rtype: int
235 @return: the desired exit code
236
237 """
238 group_name, new_name = args
239 op = opcodes.OpGroupRename(group_name=group_name, new_name=new_name)
240 SubmitOrSend(op, opts)
241
242
244 """Evacuate a node group.
245
246 """
247 (group_name, ) = args
248
249 cl = GetClient()
250
251 op = opcodes.OpGroupEvacuate(group_name=group_name,
252 iallocator=opts.iallocator,
253 target_groups=opts.to,
254 early_release=opts.early_release,
255 sequential=opts.sequential,
256 force_failover=opts.force_failover)
257 result = SubmitOrSend(op, opts, cl=cl)
258
259
260 jex = JobExecutor(cl=cl, opts=opts)
261
262 for (status, job_id) in result[constants.JOB_IDS_KEY]:
263 jex.AddJobId(None, status, job_id)
264
265 results = jex.GetResults()
266 bad_cnt = len([row for row in results if not row[0]])
267 if bad_cnt == 0:
268 ToStdout("All instances evacuated successfully.")
269 rcode = constants.EXIT_SUCCESS
270 else:
271 ToStdout("There were %s errors during the evacuation.", bad_cnt)
272 rcode = constants.EXIT_FAILURE
273
274 return rcode
275
276
286
287
289 """Shows info about node group.
290
291 """
292 cl = GetClient(query=True)
293 selected_fields = ["name",
294 "ndparams", "custom_ndparams",
295 "diskparams", "custom_diskparams",
296 "ipolicy", "custom_ipolicy"]
297 result = cl.QueryGroups(names=args, fields=selected_fields,
298 use_locking=False)
299
300 PrintGenericInfo([
301 _FormatGroupInfo(group) for group in result
302 ])
303
304
313
314
316 """Shows the command that can be used to re-create a node group.
317
318 Currently it works only for ipolicy specs.
319
320 """
321 cl = GetClient(query=True)
322 selected_fields = ["name"]
323 if opts.include_defaults:
324 selected_fields += ["ipolicy"]
325 else:
326 selected_fields += ["custom_ipolicy"]
327 result = cl.QueryGroups(names=args, fields=selected_fields,
328 use_locking=False)
329
330 for group in result:
331 ToStdout(_GetCreateCommand(group))
332
333
334 commands = {
335 "add": (
336 AddGroup, ARGS_ONE_GROUP,
337 [DRY_RUN_OPT, ALLOC_POLICY_OPT, NODE_PARAMS_OPT, DISK_PARAMS_OPT,
338 HV_STATE_OPT, DISK_STATE_OPT, PRIORITY_OPT]
339 + SUBMIT_OPTS + INSTANCE_POLICY_OPTS,
340 "<group_name>", "Add a new node group to the cluster"),
341 "assign-nodes": (
342 AssignNodes, ARGS_ONE_GROUP + ARGS_MANY_NODES,
343 [DRY_RUN_OPT, FORCE_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
344 "<group_name> <node>...", "Assign nodes to a group"),
345 "list": (
346 ListGroups, ARGS_MANY_GROUPS,
347 [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT, FORCE_FILTER_OPT],
348 "[<group_name>...]",
349 "Lists the node groups in the cluster. The available fields can be shown"
350 " using the \"list-fields\" command (see the man page for details)."
351 " The default list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS)),
352 "list-fields": (
353 ListGroupFields, [ArgUnknown()], [NOHDR_OPT, SEP_OPT], "[fields...]",
354 "Lists all available fields for node groups"),
355 "modify": (
356 SetGroupParams, ARGS_ONE_GROUP,
357 [DRY_RUN_OPT] + SUBMIT_OPTS +
358 [ALLOC_POLICY_OPT, NODE_PARAMS_OPT, HV_STATE_OPT, DISK_STATE_OPT,
359 DISK_PARAMS_OPT, PRIORITY_OPT]
360 + INSTANCE_POLICY_OPTS,
361 "<group_name>", "Alters the parameters of a node group"),
362 "remove": (
363 RemoveGroup, ARGS_ONE_GROUP, [DRY_RUN_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
364 "[--dry-run] <group-name>",
365 "Remove an (empty) node group from the cluster"),
366 "rename": (
367 RenameGroup, [ArgGroup(min=2, max=2)],
368 [DRY_RUN_OPT] + SUBMIT_OPTS + [PRIORITY_OPT],
369 "[--dry-run] <group-name> <new-name>", "Rename a node group"),
370 "evacuate": (
371 EvacuateGroup, [ArgGroup(min=1, max=1)],
372 [TO_GROUP_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT, SEQUENTIAL_OPT,
373 FORCE_FAILOVER_OPT]
374 + SUBMIT_OPTS,
375 "[-I <iallocator>] [--to <group>]",
376 "Evacuate all instances within a group"),
377 "list-tags": (
378 ListTags, ARGS_ONE_GROUP, [],
379 "<group_name>", "List the tags of the given group"),
380 "add-tags": (
381 AddTags, [ArgGroup(min=1, max=1), ArgUnknown()],
382 [TAG_SRC_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
383 "<group_name> tag...", "Add tags to the given group"),
384 "remove-tags": (
385 RemoveTags, [ArgGroup(min=1, max=1), ArgUnknown()],
386 [TAG_SRC_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
387 "<group_name> tag...", "Remove tags from the given group"),
388 "info": (
389 GroupInfo, ARGS_MANY_GROUPS, [], "[<group_name>...]",
390 "Show group information"),
391 "show-ispecs-cmd": (
392 ShowCreateCommand, ARGS_MANY_GROUPS, [INCLUDEDEFAULTS_OPT],
393 "[--include-defaults] [<group_name>...]",
394 "Show the command line to re-create a group"),
395 }
396
397
399 return GenericMain(commands,
400 override={"tag_type": constants.TAG_NODEGROUP},
401 env_override=_ENV_OVERRIDE)
402