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